diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000000..6daee86e906 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,102 @@ +# +# CI for running tests +# + +on: [push, pull_request] +name: Automated Tests + +jobs: + run-tests-asan-ubsan: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install dependencies + run: | + sudo apt-get -y update + sudo apt-get -ym install libbz2-dev zlib1g-dev libpng-dev autoconf clang-14 ccache moreutils + - name: Cache + uses: actions/cache@v3 + with: + path: ~/.ccache + key: ccache:${{ github.job }}:${{ github.ref }}:${{ github.sha }} + restore-keys: | + ccache:${{ github.job }}:${{ github.ref }} + ccache:${{ github.job }} + - name: Configure Simutrans Build + run: | + autoconf + CC="ccache clang-14" CXX="ccache clang++-14" ./configure + echo "FLAGS += -fsanitize=address,undefined -fno-sanitize-recover=all -fno-sanitize=shift,function" >> config.default + echo "LDFLAGS += -fsanitize=address,undefined" >> config.default + - name: Build Simutrans + run: | + CC="ccache clang-14" CXX="ccache clang++-14" make -j$(nproc) + - name: Install pak128.britain-ex-nightly + run: | + pushd simutrans + printf '2\ni\ny\n' | ../get_pak.sh + popd + - name: Link tests as scenario + run: | + mkdir -p ./simutrans/pak128.britain-ex-nightly/scenario + ln -sT $GITHUB_WORKSPACE/tests ./simutrans/pak128.britain-ex-nightly/scenario/automated-tests + - name: Create simuconf + run: | + mkdir -p ~/simutrans/ + echo "frames_per_second = 100" >> ~/simutrans/simuconf.tab + echo "fast_forward_frames_per_second = 100" >> ~/simutrans/simuconf.tab + - name: Run tests + run: | + export ASAN_OPTIONS="print_stacktrace=1 abort_on_error=1 detect_leaks=0" + export UBSAN_OPTIONS="print_stacktrace=1 abort_on_error=1" + cp scripts/run-automated-tests.sh . + chmod +x run-automated-tests.sh + timeout 10m ./run-automated-tests.sh # 10 minutes ought to be enough for anybody. + + run-tests-tsan: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install dependencies + run: | + sudo apt-get -y update + sudo apt-get -ym install libbz2-dev zlib1g-dev libpng-dev autoconf clang-14 ccache moreutils + - name: Cache + uses: actions/cache@v3 + with: + path: ~/.ccache + key: ccache:${{ github.job }}:${{ github.ref }}:${{ github.sha }} + restore-keys: | + ccache:${{ github.job }}:${{ github.ref }} + ccache:${{ github.job }} + - name: Configure Simutrans Build + run: | + autoconf + CC="ccache clang-14" CXX="ccache clang++-14" ./configure + echo "FLAGS += -fsanitize=thread" >> config.default + echo "LDFLAGS += -fsanitize=thread" >> config.default + - name: Build Simutrans + run: | + CC="ccache clang-14" CXX="ccache clang++-14" make -j$(nproc) + - name: Install pak128.britain-ex-nightly + run: | + pushd simutrans + printf '2\ni\ny\n' | ../get_pak.sh + popd + - name: Link tests as scenario + run: | + mkdir -p ./simutrans/pak128.britain-ex-nightly/scenario + ln -sT $GITHUB_WORKSPACE/tests ./simutrans/pak128.britain-ex-nightly/scenario/automated-tests + - name: Create simuconf + run: | + mkdir -p ~/simutrans/ + echo "frames_per_second = 100" >> ~/simutrans/simuconf.tab + echo "fast_forward_frames_per_second = 100" >> ~/simutrans/simuconf.tab + - name: Run tests + run: | + export TSAN_OPTIONS="print_stacktrace=1 second_deadlock_stack=1 history_size=7 verbose=2" + cp scripts/run-automated-tests.sh . + chmod +x run-automated-tests.sh + timeout 10m ./run-automated-tests.sh # 10 minutes ought to be enough for anybody. diff --git a/.gitignore b/.gitignore index 489fa36ef67..a3896e5ce00 100644 --- a/.gitignore +++ b/.gitignore @@ -179,7 +179,6 @@ text/zh # Folders: -script/ pak*/ text/citylists/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 501902acaa7..681e6c0f19b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,8 @@ project(simutrans-extended LANGUAGES C CXX) include(SimutransCommitInfo) -# Force C++11 everywhere -set(CMAKE_CXX_STANDARD 11) +# Force C++14 everywhere +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_EXTENSIONS OFF) @@ -306,6 +306,23 @@ if (APPLE) endif (APPLE) + +# +# Tests +# +add_custom_target(test + $ + -use_workdir + -objects pak128.britain-ex-nightly + -scenario automated-tests + -debug 2 + -lang en + -fps 100 + DEPENDS simutrans + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/simutrans +) + + # # Installation # diff --git a/Makefile b/Makefile index 235c3e5c112..0cc844bd414 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ else endif ifeq ($(OSTYPE),mac) - CFLAGS += -std=c++11 -stdlib=libc++ + CFLAGS += -stdlib=libc++ LDFLAGS += -stdlib=libc++ endif @@ -84,13 +84,9 @@ else SOURCES += sys/clipboard_internal.cc endif -ifeq ($(OSTYPE),openbsd) - CXXFLAGS += -std=c++11 -endif - LIBS += -lbz2 -lz -lpng -CXXFLAGS += -std=gnu++11 +CXXFLAGS += -std=c++14 ifneq ($(OSTYPE),mingw) LIBS += -lbz2 -lz @@ -533,6 +529,7 @@ SOURCES += script/api_class.cc SOURCES += script/api_function.cc SOURCES += script/api_param.cc SOURCES += script/api/api_city.cc +SOURCES += script/api/api_command.cc SOURCES += script/api/api_const.cc SOURCES += script/api/api_control.cc SOURCES += script/api/api_convoy.cc @@ -544,6 +541,7 @@ SOURCES += script/api/api_line.cc SOURCES += script/api/api_map_objects.cc SOURCES += script/api/api_obj_desc.cc SOURCES += script/api/api_obj_desc_base.cc +SOURCES += script/api/api_pathfinding.cc SOURCES += script/api/api_player.cc SOURCES += script/api/api_scenario.cc SOURCES += script/api/api_schedule.cc @@ -556,6 +554,7 @@ SOURCES += script/api/get_next.cc SOURCES += script/dynamic_string.cc SOURCES += script/export_objs.cc SOURCES += script/script.cc +SOURCES += script/script_loader.cc SOURCES += squirrel/sq_extensions.cc SOURCES += squirrel/squirrel/sqapi.cc SOURCES += squirrel/squirrel/sqclass.cc @@ -792,3 +791,6 @@ makeobj: nettool: $(Q)$(MAKE) -e -C nettools FLAGS="$(FLAGS)" +test: simutrans + $(BUILDDIR)/$(PROG) -set_workdir $(shell pwd)/simutrans -objects pak -scenario automated-tests -debug 2 -lang en -fps 100 + diff --git a/Simutrans-Extended.vcxproj b/Simutrans-Extended.vcxproj index 1fe07cf8e39..18a8d3b0140 100644 --- a/Simutrans-Extended.vcxproj +++ b/Simutrans-Extended.vcxproj @@ -2106,6 +2106,7 @@ + @@ -2115,8 +2116,9 @@ - + + @@ -2132,6 +2134,7 @@ + diff --git a/bauer/brueckenbauer.cc b/bauer/brueckenbauer.cc index 2e6de639b1d..14db12e66cf 100644 --- a/bauer/brueckenbauer.cc +++ b/bauer/brueckenbauer.cc @@ -164,14 +164,28 @@ void bridge_builder_t::fill_menu(tool_selector_t *tool_selector, const waytype_t } // now sorted ... - FOR(vector_tpl, const i, matching) { + for (bridge_desc_t const* const i : matching) { tool_selector->add_tool_selector(i->get_builder()); } } +const vector_tpl& bridge_builder_t::get_available_bridges(const waytype_t wtyp) +{ + static vector_tpl dummy; + dummy.clear(); + const uint16 time = welt->get_timeline_year_month(); + for(auto const& i : desc_table) { + bridge_desc_t const* const b = i.value; + if ( b->get_waytype()==wtyp && b->is_available(time) ) { + dummy.append(b); + } + } + return dummy; +} + -inline bool ribi_check( ribi_t::ribi ribi, ribi_t::ribi check_ribi ) +static inline bool ribi_check( ribi_t::ribi ribi, ribi_t::ribi check_ribi ) { // either check for single (if nothing given) otherwise ensure exact match return check_ribi ? ribi == check_ribi : ribi_t::is_single( ribi ); diff --git a/bauer/brueckenbauer.h b/bauer/brueckenbauer.h index f638ee576db..24cffb53532 100644 --- a/bauer/brueckenbauer.h +++ b/bauer/brueckenbauer.h @@ -155,6 +155,11 @@ class bridge_builder_t { * @param wtyp way type */ static void fill_menu(tool_selector_t *tool_selector, const waytype_t wtyp, sint16 sound_ok); + + /** + * Returns a list with available bridge types. + */ + static const vector_tpl& get_available_bridges(const waytype_t wtyp); }; #endif diff --git a/bauer/fabrikbauer.cc b/bauer/fabrikbauer.cc index 2e62194c926..a3bb12fd562 100644 --- a/bauer/fabrikbauer.cc +++ b/bauer/fabrikbauer.cc @@ -81,7 +81,7 @@ void init_fab_map( karte_t *welt ) for( int i=0; iget_size().y; i++ ) { fab_map[i] = 0; } - FOR(vector_tpl, const f, welt->get_fab_list()) { + for(fabrik_t* const f : welt->get_fab_list()) { add_factory_to_fab_map(welt, f); } if( welt->get_settings().get_max_factory_spacing_percent() ) { @@ -94,7 +94,7 @@ void init_fab_map( karte_t *welt ) /** - * @param x,y world position + * @param x,y world position, needs to be valid coordinates * @returns true, if factory coordinate */ inline bool is_factory_at(sint16 x, sint16 y) @@ -431,7 +431,7 @@ class ocean_test_driver_t : public test_driver_t bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->is_water(); }; ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi_unmasked(water_wt); }; waytype_t get_waytype() const OVERRIDE { return water_wt; }; // To trigger jps "jump" behavior; otherwise use invalid_wt - int get_cost(const grund_t *, const sint32, koord) OVERRIDE { return 1; }; + int get_cost(const grund_t *, const sint32, ribi_t::ribi) OVERRIDE { return 1; }; bool is_target(const grund_t *, const grund_t *) OVERRIDE { return false; }; // unused }; @@ -966,8 +966,7 @@ int factory_builder_t::build_chain_link(const fabrik_t* origin_fab, const factor weighted_vector_tplproducer; find_producer(producer, ware, welt->get_timeline_year_month()); bool local_supplier_unavailable = true; - FOR(weighted_vector_tpl, producer_type, producer) - { + for(const factory_desc_t* producer_type : producer) { if (producer_type->get_building()->is_allowed_region(origin_region)) { local_supplier_unavailable = false; @@ -1177,8 +1176,7 @@ int factory_builder_t::build_chain_link(const fabrik_t* origin_fab, const factor /* now the cross-connect part: * connect also the factories we stole from before ... */ - FOR(slist_tpl, const fab, new_factories) - { + for(fabrik_t* const fab : new_factories) { for(slist_tpl::iterator fabs_to_correct = factories_to_correct.begin(), end = factories_to_correct.end(); fabs_to_correct != end;) { fabs_to_correct->demand -= 1; diff --git a/bauer/goods_manager.cc b/bauer/goods_manager.cc index 9059ec731d9..8157c7bf4b9 100644 --- a/bauer/goods_manager.cc +++ b/bauer/goods_manager.cc @@ -98,7 +98,7 @@ bool goods_manager_t::successfully_loaded() // now assign unique category indices for unique categories max_catg_index = 0; // first assign special freight (which always needs an own category) - FOR(vector_tpl, const i, goods) { + for(goods_desc_t* const i : goods) { if (i->get_catg() == 0) { i->catg_index = max_catg_index++; } @@ -106,7 +106,7 @@ bool goods_manager_t::successfully_loaded() // mapping of waren_t::catg to catg_index, map[catg] = catg_index uint8 map[255] = {0}; - FOR(vector_tpl, const i, goods) { + for(goods_desc_t* const i : goods) { uint8 const catg = i->get_catg(); if( catg > 0 ) { if( map[catg] == 0 ) { // We didn't found this category yet -> just create new index. diff --git a/bauer/hausbauer.cc b/bauer/hausbauer.cc index d1b9cf724d1..5d26a7e069e 100644 --- a/bauer/hausbauer.cc +++ b/bauer/hausbauer.cc @@ -323,7 +323,7 @@ void hausbauer_t::fill_menu(tool_selector_t* tool_selector, building_desc_t::bty const uint16 time = welt->get_timeline_year_month(); DBG_DEBUG("hausbauer_t::fill_menu()","maximum %i",station_building.get_count()); - FOR( vector_tpl, const desc, station_building ) { + for(building_desc_t const* const desc : station_building ) { // DBG_DEBUG("hausbauer_t::fill_menu()", "try to add %s (%p)", desc->get_name(), desc); if( desc->get_type() == btype && desc->get_builder() && ((btype == building_desc_t::headquarters || btype == building_desc_t::signalbox) || desc->get_extra()==(uint16)wt) ) { if(desc->is_available(time) && @@ -341,7 +341,7 @@ void hausbauer_t::fill_menu(tool_selector_t* tool_selector, building_desc_t::bty void hausbauer_t::new_world() { unbuilt_monuments.clear(); - FOR(vector_tpl, const i, monuments) { + for(building_desc_t const* const i : monuments) { unbuilt_monuments.append(i); } } @@ -392,6 +392,7 @@ void hausbauer_t::remove( player_t *player, const gebaeude_t *gb, bool map_gener } } } + // tell players of the deletion for(uint8 i=0; iget_player(i); @@ -399,6 +400,7 @@ void hausbauer_t::remove( player_t *player, const gebaeude_t *gb, bool map_gener player->notify_factory(player_t::notify_delete, fab); } } + // remove all transformers // TODO: review this mechanism - it may be unnecessary and cause crashes. for(k.y = -1; k.y < size.y+1; k.y ++) { @@ -971,7 +973,7 @@ const building_desc_t* hausbauer_t::get_random_station(const building_desc_t::bt return NULL; } - FOR(vector_tpl, const desc, station_building) { + for(building_desc_t const* const desc : station_building) { if( desc->get_type()== btype && desc->get_extra()==(uint32)wt && (enables==0 || (desc->get_enabled()&enables)!=0) ) { // skip underground stations if( !desc->can_be_built_aboveground() ) { @@ -1002,7 +1004,7 @@ const building_desc_t* hausbauer_t::get_special(uint32 bev, building_desc_t::bty default: return NULL; } - FOR(vector_tpl, const desc, *list) { + for(building_desc_t const* const desc : *list) { // extra data contains number of inhabitants for building if( desc->get_extra()==bev ) { if((cl == MAX_CLIMATES || desc->is_allowed_climate(cl)) && desc->is_allowed_region(region)) @@ -1080,7 +1082,7 @@ const building_desc_t* hausbauer_t::get_city_building_from_list(const vector_tpl const uint16 level_replaced = sum_level; const uint16 area_of_building = size.x*size.y; - FOR(vector_tpl, const desc, building_list) { + for(building_desc_t const* const desc : building_list) { // only allow buildings of the designated size. if(!is_allowed_size(desc, size)) { continue; @@ -1148,8 +1150,7 @@ const building_desc_t* hausbauer_t::get_city_building_from_list(const vector_tpl // DBG_MESSAGE("hausbauer_t::get_city_building_from_list()","target level %i", level ); const building_desc_t *desc_at_least=NULL; - FOR(vector_tpl, const desc, building_list) - { + for(building_desc_t const* const desc : building_list) { const uint16 random = simrand(100, "static const building_desc_t* get_city_building_from_list"); if( desc->is_allowed_climate(cl) && desc->is_allowed_region(region) && is_allowed_size(desc, size) && desc->get_distribution_weight()>0 && @@ -1246,7 +1247,7 @@ const building_desc_t* hausbauer_t::get_headquarters(int level, uint16 time) if( level<0 ) { return NULL; } - FOR(vector_tpl, const desc, hausbauer_t::headquarters) { + for(building_desc_t const* const desc : hausbauer_t::headquarters) { if( desc->get_extra()==(uint32)level && desc->is_available(time) ) { return desc; } @@ -1261,7 +1262,7 @@ const building_desc_t *hausbauer_t::get_random_desc(vector_tpl auswahl(16); - FOR(vector_tpl, const desc, list) { + for(building_desc_t const* const desc : list) { if((cl==MAX_CLIMATES || desc->is_allowed_climate(cl)) && desc->get_distribution_weight()>0 && (time==0 || (desc->get_intro_year_month()<=time && (ignore_retire || desc->get_retire_year_month()>time) ) ) ) { // DBG_MESSAGE("hausbauer_t::get_random_desc()","appended %s at %i", desc->get_name(), thislevel ); auswahl.append(desc, desc->get_distribution_weight()); diff --git a/bauer/tree_builder.cc b/bauer/tree_builder.cc index 32b0649baae..8ba940dc068 100644 --- a/bauer/tree_builder.cc +++ b/bauer/tree_builder.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ @@ -232,7 +232,7 @@ bool tree_builder_t::successfully_loaded() DBG_MESSAGE("tree_builder_t::successfully_loaded", "No trees found - feature disabled"); } - for (const auto &i : desc_table) { + for(auto const& i : desc_table) { tree_list.insert_ordered(i.value, compare_tree_desc); if( tree_list.get_count()==255 ) { dbg->error( "tree_builder_t::successfully_loaded", "Maximum tree count exceeded! (%u > 255)", desc_table.get_count() ); @@ -306,12 +306,12 @@ void tree_builder_t::distribute_trees(int dichte, sint16 xtop, sint16 ytop, sint settings_t const& s = welt->get_settings(); sint32 const x = welt->get_size().x; sint32 const y = welt->get_size().y; - unsigned const t_forest_size = (unsigned)pow(((double)x * (double)y), 0.25) * s.get_forest_base_size() / 11 + (x + y) / (2 * s.get_forest_map_size_divisor()); - uint8 const c_forest_count = (unsigned)pow(((double)x * (double)y), 0.5) / s.get_forest_count_divisor(); + unsigned const t_forest_size = (uint32)pow(((double)x * (double)y), 0.25) * s.get_forest_base_size() / 11 + (x + y) / (2 * s.get_forest_map_size_divisor()); + uint32 const c_forest_count = (uint32)pow(((double)x * (double)y), 0.5) / s.get_forest_count_divisor(); DBG_MESSAGE("tree_builder_t::distribute_trees", "Creating %i forests", c_forest_count); - for (uint8 c1 = 0 ; c1 < c_forest_count ; c1++) { + for (uint32 c1 = 0; c1 < c_forest_count ; c1++) { // to have same execution order for simrand koord const start = koord::koord_random(x, y); koord const size = koord(t_forest_size,t_forest_size) + koord::koord_random(t_forest_size, t_forest_size); diff --git a/bauer/tree_builder.h b/bauer/tree_builder.h index 4ea7330d7b6..9f35f0d9aa5 100644 --- a/bauer/tree_builder.h +++ b/bauer/tree_builder.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/bauer/tunnelbauer.cc b/bauer/tunnelbauer.cc index d9665046d5a..58499af9aec 100644 --- a/bauer/tunnelbauer.cc +++ b/bauer/tunnelbauer.cc @@ -81,7 +81,7 @@ const tunnel_desc_t *tunnel_builder_t::get_tunnel_desc(const waytype_t wtyp, con { const tunnel_desc_t *find_desc = NULL; - for(auto const & i: tunnel_by_name) { + for(auto const& i : tunnel_by_name) { tunnel_desc_t* const desc = i.value; if( desc->get_waytype()==wtyp ) { if( desc->is_available(time) ) { @@ -124,14 +124,14 @@ void tunnel_builder_t::fill_menu(tool_selector_t* tool_selector, const waytype_t const uint16 time=welt->get_timeline_year_month(); vector_tpl matching(tunnel_by_name.get_count()); - for(auto const & i : tunnel_by_name) { + for(auto const& i : tunnel_by_name) { tunnel_desc_t* const desc = i.value; if( desc->get_waytype()==wtyp && desc->is_available(time) ) { matching.insert_ordered(desc, compare_tunnels); } } // now sorted ... - FOR(vector_tpl, const i, matching) { + for(tunnel_desc_t const* const i : matching) { tool_selector->add_tool_selector(i->get_builder()); } } diff --git a/bauer/wegbauer.cc b/bauer/wegbauer.cc index 10572bd8658..fc894e7c251 100644 --- a/bauer/wegbauer.cc +++ b/bauer/wegbauer.cc @@ -160,7 +160,7 @@ const vector_tpl& way_builder_t::get_way_list(const waytype const uint16 time = welt->get_timeline_year_month(); for(auto const& i : desc_table) { way_desc_t const* const test = i.value; - if (test->get_wtyp() == wtyp && test->get_styp() == styp && test->is_available(time) && test->get_builder()) { + if (test->get_wtyp() == wtyp && test->get_styp() == styp && test->is_available(time) && test->get_builder() && !test->is_mothballed()) { dummy.append(test); } } @@ -440,18 +440,19 @@ void way_builder_t::fill_menu(tool_selector_t *tool_selector, const waytype_t wt const uint16 time = welt->get_timeline_year_month(); // list of matching types (sorted by speed) - vector_tpl matching; + vector_tpl matching; - for(auto const& i : desc_table) { - way_desc_t* const desc = i.value; + for(auto const &iter : desc_table) { + way_desc_t const* const desc = iter.value; if ( desc->get_styp()==styp && desc->get_wtyp()==wtyp && desc->get_builder() && desc->is_available(time) ) { matching.append(desc); } } + std::sort(matching.begin(), matching.end(), compare_ways); // now add sorted ways ... - FOR(vector_tpl, const i, matching) { + for(way_desc_t const* const i : matching) { tool_selector->add_tool_selector(i->get_builder()); } } @@ -482,6 +483,7 @@ bool way_builder_t::check_crossing(const koord zv, const grund_t *bd, waytype_t return true; } } + // special case: tram track on road if ( (wtyp==road_wt && w->get_waytype()==track_wt && w->get_desc()->get_styp()==type_tram) || (wtyp0==tram_wt && w->get_waytype()==road_wt) ) { @@ -492,6 +494,7 @@ bool way_builder_t::check_crossing(const koord zv, const grund_t *bd, waytype_t { return false; } + // check for existing crossing crossing_t *cr = bd->find(); if (cr) { @@ -500,6 +503,7 @@ bool way_builder_t::check_crossing(const koord zv, const grund_t *bd, waytype_t // only cross with the right direction return (ns_way==iwtyp ? ribi_t::is_straight_ns(ribi_type(zv)) : ribi_t::is_straight_ew(ribi_type(zv))); } + // no crossings in tunnels if((bautyp & tunnel_flag)!=0 || bd->ist_tunnel()) { return false; @@ -540,10 +544,11 @@ bool way_builder_t::check_crossing(const koord zv, const grund_t *bd, waytype_t // it is our way we want to cross: can we built a crossing here? // both ways must be straight and no ends return ribi_t::is_straight(w_ribi) - && !ribi_t::is_single(w_ribi) - && ribi_t::is_straight(ribi_type(zv)) + && !ribi_t::is_single(w_ribi) + && ribi_t::is_straight(ribi_type(zv)) && (w_ribi&ribi_type(zv))==0; } + // cannot build crossing here return false; } @@ -556,6 +561,7 @@ bool way_builder_t::check_powerline(const koord zv, const grund_t *bd) const if(zv==koord(0,0)) { return true; } + leitung_t* lt = bd->find(); if(lt!=NULL) { ribi_t::ribi lt_ribi = lt->get_ribi(); @@ -568,10 +574,12 @@ bool way_builder_t::check_powerline(const koord zv, const grund_t *bd) const && (lt_ribi&ribi_type(zv))==0 && !bd->ist_tunnel(); } + // check for transformer if (bd->find() != NULL || bd->find() != NULL) { return false; } + // ok, there is not high power transmission stuff going on here return true; } @@ -616,9 +624,8 @@ bool way_builder_t::check_owner( const player_t *player1, const player_t *player } -/* do not go through depots, station buildings etc. ... - * direction results from layout - */ +/// do not go through depots, station buildings etc. +/// direction results from layout bool way_builder_t::check_building( const grund_t *to, const koord dir ) const { if(dir==koord(0,0)) { @@ -634,8 +641,8 @@ bool way_builder_t::check_building( const grund_t *to, const koord dir ) const } // first find all kind of buildings - gebaeude_t *gb = to->find(); - if(gb==NULL) { + gebaeude_t *building = to->find(); + if(building==NULL) { // but depots might be overlooked ... depot_t* depot = to->get_depot(); // no road to tram depot and vice-versa @@ -644,13 +651,14 @@ bool way_builder_t::check_building( const grund_t *to, const koord dir ) const return false; } } - gb = depot; + building = depot; } + // check, if we may enter - if(gb) { + if(building) { // now check for directions - uint8 layouts = gb->get_tile()->get_desc()->get_all_layouts(); - uint8 layout = gb->get_tile()->get_layout(); + uint8 layouts = building->get_tile()->get_desc()->get_all_layouts(); + uint8 layout = building->get_tile()->get_layout(); ribi_t::ribi r = ribi_type(dir); if( layouts&1 ) { return false; @@ -660,23 +668,30 @@ bool way_builder_t::check_building( const grund_t *to, const koord dir ) const } return ribi_t::is_straight( r | ribi_t::doubles(ribi_t::layout_to_ribi[layout&1]) ); } + return true; } -/** This is the core routine for the way search +/** + * This is the core routine for the way search * it will check - * A) allowed step - * B) if allowed, calculate the cost for the step from from to to + * A) allowed step + * B) if allowed, calculate the cost for the step from from to to */ bool way_builder_t::is_allowed_step( const grund_t *from, const grund_t *to, sint32 *costs ) { - const koord from_pos=from->get_pos().get_2d(); - const koord to_pos=to->get_pos().get_2d(); - const koord zv=to_pos-from_pos; + const koord from_pos = from->get_pos().get_2d(); + const koord to_pos = to->get_pos().get_2d(); + const koord zv = to_pos-from_pos; + // fake empty elevated tiles - static monorailboden_t to_dummy(koord3d::invalid, slope_t::flat); static monorailboden_t from_dummy(koord3d::invalid, slope_t::flat); + static monorailboden_t to_dummy(koord3d::invalid, slope_t::flat); + + if (desc == NULL) { + return false; + } const sint8 altitude = max(from->get_pos().z, to->get_pos().z) - welt->get_groundwater(); const sint8 max_altitude = desc->get_max_altitude(); @@ -1281,6 +1296,7 @@ bool way_builder_t::check_terraforming( const grund_t *from, const grund_t *to, const slope_t::type to_slope = to->get_weg_hang(); const sint8 from_hgt = from->get_hoehe(); const sint8 to_hgt = to->get_hoehe(); + // we may change slope of a tile if it is sloped already if( (from_slope == slope_t::flat || from->get_hoehe() == welt->get_water_hgt( from->get_pos().get_2d() )) && (to_slope == slope_t::flat || to->get_hoehe() == welt->get_water_hgt( to->get_pos().get_2d() )) ) { @@ -1376,14 +1392,16 @@ bool way_builder_t::check_terraforming( const grund_t *from, const grund_t *to, } return true; } + return false; } + void way_builder_t::do_terraforming() { uint32 last_terraformed = terraform_index.get_count(); - FOR(vector_tpl, const i, terraform_index) { // index in route + for(uint32 const i : terraform_index) { // index in route grund_t *from = welt->lookup(route[i]); uint8 from_slope = from->get_grund_hang(); @@ -1481,6 +1499,7 @@ void way_builder_t::check_for_bridge(const grund_t* parent_from, const grund_t* */ const weg_t *way0 = from->get_weg_nr(0); const weg_t *way1 = from->get_weg_nr(1); + if( way0 ) { switch( bautyp&bautyp_mask ) { case schiene_tram: @@ -1596,14 +1615,14 @@ way_builder_t::way_builder_t(player_t* player) : next_gr(32) */ void way_builder_t::set_keep_existing_ways(bool yesno) { - keep_existing_ways = yesno; + keep_existing_ways = yesno; keep_existing_faster_ways = false; } void way_builder_t::set_keep_existing_faster_ways(bool yesno) { - keep_existing_ways = false; + keep_existing_ways = false; keep_existing_faster_ways = yesno; } @@ -1614,9 +1633,11 @@ void way_builder_t::init_builder(bautyp_t wt, const way_desc_t *b, const tunnel_ desc = b; bridge_desc = br; tunnel_desc = tunnel; + if(wt&tunnel_flag && tunnel==NULL) { dbg->fatal("way_builder_t::init_builder()","needs a tunnel description for an underground route!"); } + if((wt&bautyp_mask)==luft) { wt &= bautyp_mask | bot_flag; } @@ -1633,6 +1654,7 @@ void way_builder_t::init_builder(bautyp_t wt, const way_desc_t *b, const tunnel_ } #endif } + DBG_MESSAGE("way_builder_t::init_builder()", "setting way type to %d, desc=%s, bridge_desc=%s, tunnel_desc=%s", bautyp, desc ? desc->get_name() : "NULL", @@ -1665,7 +1687,8 @@ void get_mini_maxi( const vector_tpl &ziel, koord3d &mini, koord3d &max } -/* this routine uses A* to calculate the best route +/** + * this routine uses A* to calculate the best route * beware: change the cost and you will mess up the system! * (but you can try, look at simuconf.tab) */ @@ -1677,7 +1700,7 @@ sint32 way_builder_t::intern_calc_route(const vector_tpl &start, const // check for existing koordinates bool has_target_ground = false; - FOR(vector_tpl, const& i, ziel) { + for(koord3d const& i : ziel) { has_target_ground |= welt->lookup(i) != 0; } if( !has_target_ground ) { @@ -1713,7 +1736,7 @@ sint32 way_builder_t::intern_calc_route(const vector_tpl &start, const uint32 step = 0; const grund_t* gr=NULL; - FOR(vector_tpl, const& i, start) { + for(koord3d const& i : start) { gr = welt->lookup(i); // is valid ground? @@ -1777,7 +1800,7 @@ DBG_DEBUG("insert to close","(%i,%i,%i) f=%i",gr->get_pos().x,gr->get_pos().y,g next_gr.clear(); // only one direction allowed ... - const ribi_t::ribi straight_dir = tmp->parent!=NULL ? ribi_type(gr->get_pos()-tmp->parent->gr->get_pos()) : (ribi_t::ribi)ribi_t::all; + const ribi_t::ribi straight_dir = tmp->parent!=NULL ? ribi_type(gr->get_pos() - tmp->parent->gr->get_pos()) : (ribi_t::ribi)ribi_t::all; // test directions // .. use only those that are allowed by current slope @@ -1835,7 +1858,7 @@ DBG_DEBUG("insert to close","(%i,%i,%i) f=%i",gr->get_pos().x,gr->get_pos().y,g } // now check all valid ones ... - FOR(vector_tpl, const& r, next_gr) { + for(next_gr_t const& r : next_gr) { to = r.gr; if( to==NULL) { @@ -2172,6 +2195,7 @@ bool way_builder_t::intern_calc_route_runways(koord3d start3d, const koord3d zie } from = to; } + // now we can build here route.clear(); terraform_index.clear(); @@ -2179,6 +2203,7 @@ bool way_builder_t::intern_calc_route_runways(koord3d start3d, const koord3d zie for( int i=0; i<=dist; i++ ) { route.append(welt->lookup_kartenboden(start + zv * i)->get_pos()); } + return true; } @@ -2282,6 +2307,7 @@ void way_builder_t::build_tunnel_and_bridges() if(bridge_desc==NULL && tunnel_desc==NULL) { return; } + // check for bridges and tunnels (no tunnel/bridge at last/first tile) for(uint32 i=1; i, const i, terraform_index) { // index in route + for(uint32 const i : terraform_index) { // index in route grund_t *from = welt->lookup(route[i]); uint8 from_slope = from->get_grund_hang(); @@ -2544,6 +2570,7 @@ sint64 way_builder_t::calc_costs() { } } } + DBG_MESSAGE("way_builder_t::calc_costs()","construction estimate: %f",costs/100.0); return costs; } @@ -2593,6 +2620,7 @@ bool way_builder_t::build_tunnel_tile() new_maintenance = tunnel_builder_t::get_total_maintenance(tunnel_ground->get_pos(),tunnel_desc); player_t::add_maintenance(player_builder,new_maintenance-old_maintenance,tunnel_desc->get_waytype()); } + player_t::book_construction_costs(player_builder, cost, route[0].get_2d(), tunnel_desc->get_waytype()); return true; } @@ -2601,7 +2629,7 @@ bool way_builder_t::build_tunnel_tile() void way_builder_t::build_elevated() { - FOR(koord3d_vector_t, & i, route) { + for(koord3d & i : route) { planquadrat_t* const plan = welt->access(i.get_2d()); grund_t* const gr0 = plan->get_boden_in_hoehe(i.z); @@ -2958,7 +2986,6 @@ void way_builder_t::build_track() } - void way_builder_t::build_powerline() { if( get_count() < 1 ) { @@ -3011,14 +3038,13 @@ void way_builder_t::build_powerline() } - // this can drive any river, even a river that has max_speed=0 class fluss_test_driver_t : public test_driver_t { bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi_unmasked(water_wt)!=0; } ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_weg_ribi_unmasked(water_wt); } waytype_t get_waytype() const OVERRIDE { return invalid_wt; } - int get_cost(const grund_t *, const sint32, koord) OVERRIDE { return 1; } + int get_cost(const grund_t *, const sint32, ribi_t::ribi) OVERRIDE { return 1; } bool is_target(const grund_t *cur,const grund_t *) OVERRIDE { return cur->is_water() && cur->get_grund_hang()==slope_t::flat; } }; @@ -3104,7 +3130,7 @@ void way_builder_t::build_river() route_t to_the_sea; fluss_test_driver_t river_tester; if(to_the_sea.find_route(welt, welt->lookup_kartenboden(route[start_n].get_2d())->get_pos(), &river_tester, 0, ribi_t::all, 0, 1, 0, 0x7FFFFFFF, false)) { - FOR(koord3d_vector_t, const& i, to_the_sea.get_route()) { + for(koord3d const& i : to_the_sea.get_route()) { if (weg_t* const w = welt->lookup(i)->get_weg(water_wt)) { int type; for( type=env_t::river_types-1; type>0; type-- ) { @@ -3263,4 +3289,3 @@ void way_builder_t::update_ribi_mask_oneway(strasse_t* str, uint32 i) { } } } - diff --git a/bauer/wegbauer.h b/bauer/wegbauer.h index 08492b8a872..18b97deda91 100644 --- a/bauer/wegbauer.h +++ b/bauer/wegbauer.h @@ -30,6 +30,7 @@ class strasse_t; class way_builder_t { static karte_ptr_t welt; + public: static const way_desc_t *leitung_desc; @@ -97,7 +98,8 @@ class way_builder_t private: /// flags used in intern_calc_route, saved in the otherwise unused route_t::ANode->count - enum build_type_t { + enum build_type_t + { build_straight = 1 << 0, ///< next step has to be straight terraform = 1 << 1, ///< terraform this tile build_tunnel_bridge = 1 << 2 ///< bridge/tunnel ends here @@ -116,32 +118,23 @@ class way_builder_t player_t *player_builder; - /** - * Type of building operation - */ + /// Type of building operation bautyp_t bautyp; - /** - * Type of way to build - */ - const way_desc_t * desc; + /// Which way to build + const way_desc_t *desc; - /** - * Type of bridges to build (zero=>no bridges) - */ - const bridge_desc_t * bridge_desc; + /// Type of bridge to build (null => no bridges) + const bridge_desc_t *bridge_desc; - /** - * Type of tunnels to build (zero=>no bridges) - */ - const tunnel_desc_t * tunnel_desc; + /// Type of tunnel to build (null => no bridges) + const tunnel_desc_t *tunnel_desc; /** * Only for road * @author THLeaderH */ overtaking_mode_t overtaking_mode; - /** * If a way is built on top of another way, should the type * of the former way be kept or replaced (true == keep) @@ -173,6 +166,7 @@ class way_builder_t uint32 maximum; // hoechste Suchtiefe koord3d_vector_t route; + // index in route with terraformed tiles vector_tpl terraform_index; diff --git a/boden/grund.cc b/boden/grund.cc index 05b0c8fa50b..fcbc1cf6378 100644 --- a/boden/grund.cc +++ b/boden/grund.cc @@ -579,7 +579,7 @@ void grund_t::finish_rotate90() } ground_texts.clear(); // then transfer all rotated texts - FOR(text_map, const& iter, ground_texts_rotating) { + for(auto iter : ground_texts_rotating) { ground_texts.put(iter.key, iter.value); } ground_texts_rotating.clear(); @@ -591,13 +591,13 @@ void grund_t::enlarge_map( sint16, sint16 /*new_size_y*/ ) typedef inthashtable_tpl text_map; text_map ground_texts_enlarged; // we have recalculate the keys - FOR(text_map, iter, ground_texts) { + for(auto iter : ground_texts) { koord3d k = get_ground_koord3d_key( iter.key ); ground_texts_enlarged.put( get_ground_text_key(k), iter.value ); } ground_texts.clear(); // then transfer all texts back - FOR(text_map, const& iter, ground_texts_enlarged) { + for(auto iter : ground_texts_enlarged) { ground_texts.put(iter.key, iter.value); } ground_texts_enlarged.clear(); @@ -2386,7 +2386,7 @@ bool grund_t::remove_excessive_roads() bool ret = remove_excessive_roads(road_tiles); if (ret) { - FOR(grund_t::road_network_plan_t, i, road_tiles) { + for(auto i : road_tiles) { koord k = i.key; grund_t *gr = welt->lookup_kartenboden(k); if (!i.value) { @@ -2421,7 +2421,7 @@ int grund_t::count_neighbouring_roads(road_network_plan_t &road_tiles) bool grund_t::fixup_road_network_plan(road_network_plan_t &road_tiles) { - FOR(road_network_plan_t, i, road_tiles) { + for(auto i : road_tiles) { if (i.value == true) { grund_t *gr = welt->lookup_kartenboden(i.key); @@ -2564,7 +2564,7 @@ class public_driver_t: public test_driver_t { virtual ribi_t::ribi get_ribi(const grund_t* gr) const { return other->get_ribi(gr); } virtual waytype_t get_waytype() const { return other->get_waytype(); } - virtual int get_cost(const grund_t *gr, const sint32 c, koord p) { return other->get_cost(gr,c,p); } + virtual int get_cost(const grund_t *gr, const sint32 c, ribi_t::ribi from) { return other->get_cost(gr,c,from); } virtual bool is_target(const grund_t *gr,const grund_t *gr2) { return other-> is_target(gr,gr2); } }; @@ -2840,8 +2840,7 @@ bool grund_t::removing_way_would_disrupt_public_right_of_way(waytype_t wt) } } - FOR(minivec_tpl, const& diversionary_route, diversionary_routes) - { + for(route_t const& diversionary_route : diversionary_routes) { for(uint32 n = 1; n < diversionary_route.get_count()-1; n++) { // All diversionary routes must themselves be set as public rights of way. diff --git a/boden/wege/weg.cc b/boden/wege/weg.cc index 3edd31c44d5..d49ec4822f8 100644 --- a/boden/wege/weg.cc +++ b/boden/wege/weg.cc @@ -1265,6 +1265,7 @@ void weg_t::finish_rd() check_diagonal(); if(is_diagonal()) { + // maint /= sqrt(2) maint *= 10; maint /= 14; } diff --git a/boden/wege/weg.h b/boden/wege/weg.h index b19f96c3c36..8e6477aeb13 100644 --- a/boden/wege/weg.h +++ b/boden/wege/weg.h @@ -494,10 +494,9 @@ class weg_t : public obj_no_info_t void book(int amount, way_statistics type) { statistics[WAY_STAT_THIS_MONTH][type] += amount; } /** - * return statistics value - * always returns last month's value - */ - int get_statistics(int type) const { return statistics[WAY_STAT_LAST_MONTH][type]; } + * return statistics value + */ + int get_statistics(way_stat_months month, way_statistics type) const { return statistics[(int)month][(int)type]; } bool is_disused() const { return statistics[WAY_STAT_LAST_MONTH][WAY_STAT_CONVOIS] == 0 && statistics[WAY_STAT_THIS_MONTH][WAY_STAT_CONVOIS] == 0; } diff --git a/clipboard_s2.cc b/clipboard_s2.cc index 37bea7708e3..5bebff7fa49 100644 --- a/clipboard_s2.cc +++ b/clipboard_s2.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/cmake/SimutransSourceList.cmake b/cmake/SimutransSourceList.cmake index ee273cd0eb9..c073847c735 100644 --- a/cmake/SimutransSourceList.cmake +++ b/cmake/SimutransSourceList.cmake @@ -280,6 +280,7 @@ target_sources(simutrans-extended PRIVATE player/finance.cc player/simplay.cc script/api/api_city.cc + script/api/api_command.cc script/api/api_const.cc script/api/api_control.cc script/api/api_convoy.cc @@ -291,6 +292,7 @@ target_sources(simutrans-extended PRIVATE script/api/api_map_objects.cc script/api/api_obj_desc_base.cc script/api/api_obj_desc.cc + script/api/api_pathfinding.cc script/api/api_player.cc script/api/api_scenario.cc script/api/api_schedule.cc @@ -306,6 +308,7 @@ target_sources(simutrans-extended PRIVATE script/dynamic_string.cc script/export_objs.cc script/script.cc + script/script_loader.cc simcity.cc simconvoi.cc simdebug.cc diff --git a/config.template b/config.template index d8c653ac713..dc36ac2b280 100644 --- a/config.template +++ b/config.template @@ -35,7 +35,7 @@ #PROFILE = 2 # Enable profiling with optimisation flags, can be used with `OPTIMISE = 1' #LTO = 1 # enable link time optimizations #TUNE_NATIVE = 1 # enable tuning for this machine (makes binary not portable) -#GCC_POPCOUNT = 1 # use gcc builtin popcount to speed up on modern machines (makes binary not portable) +#GCC_POPCOUNT = 1 # use gcc builtin popcount to speed up on modern machines (makes binary not portable) #AV_FOUNDATION = 1 # Use AVFoundation instead of QTKit. If you are using macOS 10.12 or later, this must be enabled. @@ -110,5 +110,4 @@ USE_ZSTD = 1 # For compiling on Linux with SDL2, use -I/usr/include/SDL2 # For compiling on Linux with Freetype 2, use -I/usr/include/freetype2 # For compiling on Linux with miniupnpc, use -I/usr/includeude/miniupnpc -FLAGS = -DUSE_C -fno-delete-null-pointer-checks -fno-strict-aliasing -std=c++11 - +FLAGS = -DUSE_C -fno-delete-null-pointer-checks -fno-strict-aliasing -std=c++14 diff --git a/dataobj/crossing_logic.cc b/dataobj/crossing_logic.cc index 222124f7896..bce5e625485 100644 --- a/dataobj/crossing_logic.cc +++ b/dataobj/crossing_logic.cc @@ -62,7 +62,7 @@ void crossing_logic_t::recalc_state() if( !crossings.empty() ) { on_way1.clear(); on_way2.clear(); - FOR(minivec_tpl, const i, crossings) { + for(crossing_t* const i : crossings) { // add vehicles already there if (grund_t* const gr = welt->lookup(i->get_pos())) { for( uint8 i=3; iget_top(); i++ ) { @@ -175,7 +175,7 @@ void crossing_logic_t::set_state( crossing_state_t new_state ) if(new_state!=state) { state = new_state; - FOR(minivec_tpl, const i, crossings) { + for(crossing_t* const i : crossings) { i->state_changed(); } } @@ -255,7 +255,7 @@ const crossing_desc_t *crossing_logic_t::get_crossing(const waytype_t ns, const if( way0 <= 8 && way1 <= 8 && way0 != way1 ) { const uint8 index = way0 * 9 + way1 - ((way0+2)*(way0+1))/2; - FOR( minivec_tpl, const i, can_cross_array[index] ) { + for(crossing_desc_t const* const i : can_cross_array[index] ) { if( !i->is_available(timeline_year_month) ) { continue; } @@ -355,7 +355,7 @@ void crossing_logic_t::add( crossing_t *start_cr, crossing_state_t state ) } // set new crossing logic to all - FOR(slist_tpl, const cr, crossings) { + for(crossing_t* const cr : crossings) { cr->set_logic( found_logic ); found_logic->append_crossing( cr ); } diff --git a/dataobj/freelist.cc b/dataobj/freelist.cc index a28726a515e..7cda62534b5 100644 --- a/dataobj/freelist.cc +++ b/dataobj/freelist.cc @@ -244,7 +244,7 @@ void freelist_t::free_all_nodes() } printf("freelist_t::free_all_nodes(): zeroing\n"); for( int i=0; iget_attractions().get_count(); city_count = welt->get_cities().get_count(); citizen_count = 0; - FOR(weighted_vector_tpl, const i, welt->get_cities()) { + for(stadt_t* const i : welt->get_cities()) { citizen_count += i->get_city_population(); } diff --git a/dataobj/koord3d.cc b/dataobj/koord3d.cc index dd01eb6932b..5d2db879ba2 100644 --- a/dataobj/koord3d.cc +++ b/dataobj/koord3d.cc @@ -115,7 +115,7 @@ ribi_t::ribi koord3d_vector_t::get_short_ribi( uint32 index ) const void koord3d_vector_t::rotate90( sint16 y_size ) { - FOR(koord3d_vector_t, & i, *this) { + for(koord3d & i : *this) { i.rotate90(y_size); } } diff --git a/dataobj/koord3d.h b/dataobj/koord3d.h index 2ebc0318018..7474a576524 100644 --- a/dataobj/koord3d.h +++ b/dataobj/koord3d.h @@ -16,19 +16,16 @@ /** * 3D Coordinates */ -class koord3d //: public koord +class koord3d { public: sint16 x; sint16 y; sint8 z; -// koord3d() : koord(0, 0), z(0) {} koord3d() : x(0), y(0), z(0) {} -// koord3d(sint16 xp, sint16 yp, sint8 zp) : koord(xp, yp), z(zp) {} koord3d(sint16 xp, sint16 yp, sint8 zp) : x(xp), y(yp), z(zp) {} -// koord3d(koord xyp, sint8 zp) : koord(xyp), z(zp) {} koord3d(koord xyp, sint8 zp) : x(xyp.x), y(xyp.y), z(zp) {} const char *get_str() const; @@ -40,10 +37,9 @@ class koord3d //: public koord static const koord3d invalid; -// const koord& get_2d() const { return *this; } koord get_2d() const { return koord(x, y); } - inline const koord3d& operator += (const koord3d& a) + const koord3d& operator += (const koord3d& a) { x += a.x; y += a.y; @@ -51,7 +47,7 @@ class koord3d //: public koord return *this; } - inline const koord3d& operator -= (koord3d& a) + const koord3d& operator -= (koord3d& a) { x -= a.x; y -= a.y; @@ -59,14 +55,14 @@ class koord3d //: public koord return *this; } - inline const koord3d& operator += (const koord& a) + const koord3d& operator += (const koord& a) { x += a.x; y += a.y; return *this; } - inline const koord3d& operator -= (const koord& a) + const koord3d& operator -= (const koord& a) { x -= a.x; y -= a.y; diff --git a/dataobj/loadsave.cc b/dataobj/loadsave.cc index f880d7761e0..0a05d04a76c 100644 --- a/dataobj/loadsave.cc +++ b/dataobj/loadsave.cc @@ -16,7 +16,6 @@ #include "../simversion.h" #include "../simmem.h" #include "../simdebug.h" - #include "../utils/plainstring.h" #include "../utils/simstring.h" @@ -28,7 +27,6 @@ #endif #include "../io/rdwr/compare_file_rd_stream.h" - #define INVALID_RDWR_ID (-1) //#undef MULTI_THREAD @@ -148,6 +146,8 @@ void loading_finalize() pthread_cond_broadcast(&readdata_cond); pthread_mutex_unlock(&readdata_mutex); } + + /* * Multi-threaded saving: * @@ -155,7 +155,6 @@ void loading_finalize() * - end-of-saving is signaled to thread with get_buf_pos(buf)==0, * which is protected by loadsave_mutex */ - void *save_thread( void *ptr ) { loadsave_param_t *lsp = reinterpret_cast(ptr); @@ -703,9 +702,9 @@ size_t loadsave_t::fill_buffer( int buf_num ) const size_t sz = stream->read(buff[ buf_num ].buf, LS_BUF_SIZE); - #ifdef MULTI_THREAD +#ifdef MULTI_THREAD pthread_mutex_lock(&loadsave_mutex); - #endif +#endif const rdwr_stream_t::status_t status = stream->get_status(); const bool stream_ok = (status == rdwr_stream_t::STATUS_EOF || status == rdwr_stream_t::STATUS_OK); @@ -714,14 +713,13 @@ size_t loadsave_t::fill_buffer( int buf_num ) buff[buf_num].pos = 0; buff[buf_num].len = stream_ok ? sz : 0; // buf_len is unsigned, set to zero in case of error - #ifdef MULTI_THREAD +#ifdef MULTI_THREAD pthread_mutex_unlock(&loadsave_mutex); - #endif +#endif return sz; } - /*************** High level routines to read/write data types ************* * (check also for Intel/Motorola) etc */ @@ -979,6 +977,7 @@ void loadsave_t::rdwr_xml_number(sint64 &s, const char *typ) if(minus) { s = -s; } + if( lsgetc()!='/' ) { dbg->fatal( "loadsave_t::rdwr_xml_number()", "missing '/' (not closing tag)" ); } diff --git a/dataobj/objlist.h b/dataobj/objlist.h index 058bb765f8f..4e908b8aec6 100644 --- a/dataobj/objlist.h +++ b/dataobj/objlist.h @@ -11,7 +11,17 @@ #include "../obj/simobj.h" -class objlist_t { +/** + * All things including ways are stored in this structure. + * The entries are packed, i.e. the first free entry is at the top. + * To save memory, a single element (like in the case for houses or a single tree) + * is stored directly (obj.one) and capacity==1. Otherwise obj.some points to an + * array. + * The objects are sorted according to their drawing order. + * ways are always first. + */ +class objlist_t +{ private: union { obj_t **some; // valid if capacity > 1 diff --git a/dataobj/powernet.cc b/dataobj/powernet.cc index d36a19edb27..e713438df8a 100644 --- a/dataobj/powernet.cc +++ b/dataobj/powernet.cc @@ -30,7 +30,7 @@ void powernet_t::new_world() void powernet_t::step_all(uint32 delta_t) { - FOR(slist_tpl, const p, powernet_list) { + for(powernet_t* const p : powernet_list) { p->step(delta_t); } } diff --git a/dataobj/rect.h b/dataobj/rect.h index 4fd3ee09fee..2060d7b090b 100644 --- a/dataobj/rect.h +++ b/dataobj/rect.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/dataobj/ribi.cc b/dataobj/ribi.cc index 47f0bb2dcd9..f9305da7fe7 100644 --- a/dataobj/ribi.cc +++ b/dataobj/ribi.cc @@ -10,6 +10,7 @@ #include "koord.h" #include "koord3d.h" +// since we have now a dummy function instead an array const ribi_t::_nesw ribi_t::nesw; // same like the layouts of buildings @@ -249,15 +250,15 @@ const int slope_t::flags[81] = { const slope_t::type slope_from_ribi[16] = { - 0, - slope_t::north, - slope_t::east, 0, slope_t::south, + slope_t::west, + 0, + slope_t::north, 0, 0, 0, - slope_t::west, + slope_t::east, 0, 0, 0, @@ -346,42 +347,16 @@ bool ribi_t::is_perpendicular(ribi x, ribi y) } -sint16 slope_t::get_sloping_upwards(const slope_t::type slope, const sint16 relative_pos_x, const sint16 relative_pos_y) +sint16 get_sloping_upwards(const slope_t::type slope, const ribi_t::ribi from) { - if (relative_pos_y < 0) { - if (slope == north) { - return 1; - } - else if (slope == 2 * north) { - return 2; - } - return 0; - } - if (relative_pos_y > 0) { - if (slope == south) { - return 1; - } - else if (slope == 2 * south) { - return 2; - } - return 0; - } - if (relative_pos_x < 0) { - if (slope == west) { - return 1; - } - else if (slope == 2 * west) { - return 2; - } - return 0; + // slope upwards relative to direction 'from' + const slope_t::type from_slope = slope_type(from); + + if (from_slope == slope) { + return 1; } - if (relative_pos_x > 0) { - if (slope == east) { - return 1; - } - else if (slope == 2 * east) { - return 2; - } + else if (2*from_slope == slope) { + return 2; } return 0; } diff --git a/dataobj/ribi.h b/dataobj/ribi.h index 2d6bd238e46..d695653d299 100644 --- a/dataobj/ribi.h +++ b/dataobj/ribi.h @@ -97,12 +97,6 @@ class slope_t { static bool is_way_ns(type x) { return (flags[x] & way_ns) != 0; } /// Returns if way in e/w direction can be build on this slope. static bool is_way_ew(type x) { return (flags[x] & way_ew) != 0; } - - /** - * Check if the slope is upwards, relative to the previous tile. - * @returns 1 for single upwards and 2 for double upwards - */ - static sint16 get_sloping_upwards(const type slope, const sint16 relative_pos_x, const sint16 relative_pos_y); }; @@ -288,6 +282,12 @@ slope_t::type slope_type(koord dir); */ slope_t::type slope_type(ribi_t::ribi); +/** + * Check if the slope is upwards, relative to the direction @p from. + * @returns 1 for single upwards and 2 for double upwards + */ +sint16 get_sloping_upwards(const slope_t::type slope, const ribi_t::ribi from); + /** * Calculate direction bit from coordinate differences. */ diff --git a/dataobj/route.cc b/dataobj/route.cc index 8b6af4939ec..d939b64b742 100644 --- a/dataobj/route.cc +++ b/dataobj/route.cc @@ -864,7 +864,7 @@ bool route_t::find_route(karte_t *welt, const koord3d start, test_driver_t *tdri k->gr = to; k->count = tmp->count+1; k->f = 0; - k->g = tmp->g + tdriver->get_cost(to, max_khm, gr->get_pos().get_2d()); + k->g = tmp->g + tdriver->get_cost(to, max_khm, ribi_t::nesw[r]); k->ribi_from = ribi_t::nesw[r]; uint8 current_dir = ribi_t::nesw[r]; @@ -1239,7 +1239,7 @@ route_t::route_result_t route_t::intern_calc_route(karte_t *welt, const koord3d } // new values for cost g (without way it is either in the air or in water => no costs) - const int way_cost = flags == simple_cost ? 1 : tdriver->get_cost(to, max_speed, tmp->gr->get_pos().get_2d()) + (is_overweight == slowly_only ? 400 : 0); + const int way_cost = flags == simple_cost ? 1 : tdriver->get_cost(to, max_speed, next_ribi[r]) + (is_overweight == slowly_only ? 400 : 0); uint32 new_g = tmp->g + (w ? way_cost : flags == simple_cost ? 1 : 10); // check for curves (usually, one would need the lastlast and the last; diff --git a/dataobj/scenario.cc b/dataobj/scenario.cc index 46aff896517..2c713d36cf0 100644 --- a/dataobj/scenario.cc +++ b/dataobj/scenario.cc @@ -29,8 +29,10 @@ // scripting #include "../script/script.h" -#include "../script/export_objs.h" +#include "../script/script_loader.h" #include "../script/api/api.h" +#include "../script/api_param.h" +#include "../script/api_class.h" #include "../tpl/plainstringhashtable_tpl.h" @@ -55,7 +57,6 @@ scenario_t::scenario_t(karte_t *w) : what_scenario = 0; script = NULL; - rotation = 0; won = false; lost = false; rdwr_error = false; @@ -112,16 +113,17 @@ const char* scenario_t::init( const char *scenario_base, const char *scenario_na buf.clear(); buf.printf("%s.sve", scenario_name.c_str()); welt->get_settings().set_filename( strdup(buf) ); + // re-initialize coordinate and rotation handling + script_api::coordinate_transform_t::initialize(); } - load_compatibility_script(); + script_loader_t::load_compatibility_script(script); // load translations translator::load_files_from_folder( scenario_path.c_str(), "scenario" ); cached_text_files.clear(); what_scenario = SCRIPTED; - rotation = 0; // callback script->register_callback(&scenario_t::set_completion, "scenario_t_set_completed"); @@ -135,6 +137,9 @@ const char* scenario_t::init( const char *scenario_base, const char *scenario_na welt->get_settings().set_starting_year( time / 12); welt->get_settings().set_starting_month( time % 12); + // set my player number to PLAYER_UNOWNED + script->set_my_player(PLAYER_UNOWNED); + // now call startup function if ((err = script->call_function(script_vm_t::QUEUE, "start"))) { dbg->warning("scenario_t::init", "error [%s] calling start", err); @@ -146,24 +151,9 @@ const char* scenario_t::init( const char *scenario_base, const char *scenario_na bool scenario_t::load_script(const char* filename) { - script = new script_vm_t(scenario_path.c_str()); - // load global stuff - // constants must be known compile time - export_global_constants(script->get_vm()); - // load scenario base definitions - char basefile[1024 + 24 + 1]; - sprintf( basefile, "%sscript/scenario_base.nut", env_t::data_dir ); - const char* err = script->call_script(basefile); - if (err) { // should not happen ... - dbg->error("scenario_t::load_script", "error [%s] calling %s", err, basefile); - return false; - } - - // register api functions - register_export_function(script->get_vm()); - err = script->get_error(); - if (err) { - dbg->error("scenario_t::load_script", "error [%s] calling register_export_function", err); + // start vm + script = script_loader_t::start_vm("scenario_base.nut", "script-scenario.log", scenario_path.c_str(), true); + if (script == NULL) { return false; } @@ -175,8 +165,7 @@ bool scenario_t::load_script(const char* filename) } // load scenario definition - err = script->call_script(filename); - if (err) { + if (const char* err = script->call_script(filename)) { dbg->error("scenario_t::load_script", "error [%s] calling %s", err, filename); return false; } @@ -184,67 +173,9 @@ bool scenario_t::load_script(const char* filename) } -void scenario_t::load_compatibility_script() -{ - // check api version - plainstring api_version; - if (const char* err = script->call_function(script_vm_t::FORCE, "get_api_version", api_version)) { - dbg->warning("scenario_t::init", "error [%s] calling get_api_version", err); - api_version = "120.1"; - } - if (api_version != "*") { - // load scenario compatibility script - cbuffer_t buf; - buf.printf("%sscript/scenario_compat.nut", env_t::data_dir ); - if (const char* err = script->call_script((const char*)buf) ) { - dbg->warning("scenario_t::init", "error [%s] calling scenario_compat.nut", err); - } - else { - plainstring dummy; - // call compatibility function - if ((err = script->call_function(script_vm_t::FORCE, "compat", dummy, api_version) )) { - dbg->warning("scenario_t::init", "error [%s] calling compat", err); - } - } - } -} - - -void scenario_t::koord_w2sq(koord &k) const -{ - switch( rotation ) { - // 0: do nothing - case 1: k = koord(k.y, welt->get_size().y-1 - k.x); break; - case 2: k = koord(welt->get_size().x-1 - k.x, welt->get_size().y-1 - k.y); break; - case 3: k = koord(welt->get_size().x-1 - k.y, k.x); break; - default: break; - } -} - - -void scenario_t::koord_sq2w(koord &k) +void scenario_t::koord_sq2w(koord &k) const { - // just rotate back - rotation = 4 - rotation; - koord_w2sq(k); - // restore original rotation - rotation = 4 - rotation; -} - - -void scenario_t::ribi_w2sq(ribi_t::ribi &r) const -{ - if (rotation) { - r = ( ( (r << 4) | r) >> rotation) & 15; - } -} - - -void scenario_t::ribi_sq2w(ribi_t::ribi &r) const -{ - if (rotation) { - r = ( ( (r << 4) | r) << rotation) >> 4 & 15; - } + script_api::coordinate_transform_t::koord_sq2w(k); } @@ -574,8 +505,8 @@ const char* scenario_t::is_schedule_allowed(const player_t* player, const schedu if (schedule == NULL) { return ""; } - if (schedule->empty() || env_t::server) { - // empty schedule, networkgame: all allowed + if (env_t::server) { + // networkgame: allowed return NULL; } // call script @@ -589,6 +520,43 @@ const char* scenario_t::is_schedule_allowed(const player_t* player, const schedu } +const char* scenario_t::is_convoy_allowed(const player_t* player, convoihandle_t cnv, depot_t* depot) +{ + // sanity checks + if (!cnv.is_bound() || depot == NULL) { + return ""; + } + if (env_t::server) { + // networkgame: allowed + return NULL; + } + // call script + if (what_scenario == SCRIPTED) { + static plainstring msg; + const char *err = script->call_function(script_vm_t::FORCE, "is_convoy_allowed", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), cnv, (obj_t*)depot); + + return err == NULL ? msg.c_str() : NULL; + } + return NULL; + +} + +const char* scenario_t::jump_to_link_executed(koord3d pos) +{ + if (env_t::server) { + // networkgame: allowed + return NULL; + } + // call script + if (what_scenario == SCRIPTED) { + static plainstring msg; + const char *err = script->call_function(script_vm_t::FORCE, "jump_to_link_executed", msg, pos); + + return err == NULL ? msg.c_str() : NULL; + } + return NULL; +} + const char* scenario_t::get_error_text() { if (script) { @@ -657,6 +625,18 @@ void scenario_t::step() if (need_toolbar_update) { tool_t::update_toolbars(); need_toolbar_update = false; + + // reset active tool if now forbidden + // check scenario conditions for all players + for(uint8 player_nr = 0; player_nr < PLAYER_UNOWNED; player_nr++) { + if (player_t *player = welt->get_player(player_nr)) { + tool_t *tool = welt->get_tool(player_nr); + + if (!is_tool_allowed(player, tool->get_id(), tool->get_waytype())) { + welt->local_set_tool(tool_t::general_tool[TOOL_QUERY], player); + } + } + } } } @@ -738,7 +718,7 @@ plainstring scenario_t::load_language_file(const char* filename) FILE* file = dr_fopen(wanted_file.c_str(), "rb"); if (file == NULL) { // try English - file = fopen((path + "en" + PATH_SEPARATOR + filename).c_str(), "rb"); + file = dr_fopen((path + "en" + PATH_SEPARATOR + filename).c_str(), "rb"); } if (file == NULL) { // try scenario directory @@ -766,7 +746,7 @@ plainstring scenario_t::load_language_file(const char* filename) return text; } -bool scenario_t::open_info_win() const +bool scenario_t::open_info_win(const char* tab) const { // pop up for the win scenario_info_t *si = (scenario_info_t*)win_get_magic(magic_scenario_info); @@ -801,7 +781,7 @@ void scenario_t::rdwr(loadsave_t *file) return; } - file->rdwr_byte(rotation); + script_api::coordinate_transform_t::rdwr(file); file->rdwr_short(won); file->rdwr_short(lost); file->rdwr_str(scenario_name); @@ -826,21 +806,21 @@ void scenario_t::rdwr(loadsave_t *file) rdwr_error = true; // try addon directory first if (env_t::default_settings.get_with_private_paks()) { - scenario_path = ( std::string("addons/") + env_t::objfilename + "scenario/" + scenario_name.c_str() + "/").c_str(); + scenario_path = ( std::string("addons/") + env_t::objfilename + "scenario" + PATH_SEPARATOR + scenario_name.c_str() + "/").c_str(); script_filename.printf("%sscenario.nut", scenario_path.c_str()); rdwr_error = !load_script(script_filename); } // failed, try scenario from pakset directory if (rdwr_error) { - scenario_path = (env_t::data_dir + env_t::objfilename + "scenario/" + scenario_name.c_str() + "/").c_str(); + scenario_path = (env_t::objfilename + "scenario/" + scenario_name.c_str() + PATH_SEPARATOR).c_str(); script_filename.clear(); script_filename.printf("%sscenario.nut", scenario_path.c_str()); rdwr_error = !load_script(script_filename); } if (!rdwr_error) { - load_compatibility_script(); + script_loader_t::load_compatibility_script(script); // restore persistent data const char* err = script->eval_string(str); if (err) { @@ -859,7 +839,7 @@ void scenario_t::rdwr(loadsave_t *file) } else { plainstring str; - script->call_function(script_vm_t::FORCE, "save", str); + script->call_function(script_vm_t::FORCEX, "save", str); dbg->warning("scenario_t::rdwr", "write persistent scenario data: %s", str.c_str()); file->rdwr_str(str); } @@ -885,7 +865,7 @@ void scenario_t::rdwr(loadsave_t *file) } if (what_scenario == SCRIPTED && file->is_loading() && !rdwr_error) { - const char* err = script->call_function(script_vm_t::FORCE, "resume_game"); + const char* err = script->call_function(script_vm_t::FORCEX, "resume_game"); if (err) { dbg->warning("scenario_t::rdwr", "error [%s] calling resume_game", err); rdwr_error = true; @@ -900,9 +880,6 @@ void scenario_t::rdwr(loadsave_t *file) void scenario_t::rotate90(const sint16 y_size) { - rotation ++; - rotation %= 4; - for(uint32 i=0; irotate90(y_size); } diff --git a/dataobj/scenario.h b/dataobj/scenario.h index d495823789f..3412794ca13 100644 --- a/dataobj/scenario.h +++ b/dataobj/scenario.h @@ -13,12 +13,14 @@ #include "../utils/plainstring.h" #include "../script/dynamic_string.h" #include "../dataobj/ribi.h" +#include "../convoihandle_t.h" class loadsave_t; class stadt_t; class fabrik_t; class karte_t; class schedule_t; +class depot_t; /** * @class scenario_t @@ -46,12 +48,11 @@ class scenario_t /// the world we are scripting in karte_t *welt; - - /// name of scenario script file (without .nut extension) + /// name of scenario, files are searched in scenario_path/scenario_name/... /// e.g. my_scenario plainstring scenario_name; - /// path to scenario directory (relative to env_t::program_dir) + /// path to scenario directory (relative to env_t::user_dir) /// e.g. pak/scenario/my_scenario/ plainstring scenario_path; @@ -62,11 +63,6 @@ class scenario_t */ bool load_script(const char* filename); - /** - * loads necessary compatibility scripts - */ - void load_compatibility_script(); - /// is set, if an error occurred during loading of savegame /// e.g. re-starting of scenario failed due to script error bool rdwr_error; @@ -93,7 +89,7 @@ class scenario_t forbid_type type; uint8 player_nr; /// id of tool to be forbidden, as set by constructors of classes derived from - /// tool_t, @see simtool.h + /// tool_t, @see tool/simtool.h uint16 toolnr; /// waytype of tool, @see waytype_t sint16 waytype; @@ -162,7 +158,7 @@ class scenario_t vector_tplforbidden_tools; /// set to true if rules changed to update toolbars, - /// toolbars will be updated in next call to step() + /// toolbars and active tools will be updated in next call to step() bool need_toolbar_update; /** @@ -192,11 +188,6 @@ class scenario_t void call_forbid_tool(forbidden_t *test, bool forbid); /// @} - /// Stores how many times initial map was rotated. - /// Scripts do not take care of rotated maps. - /// Coordinates will be translated between in-game coordinates and script coordinates. - uint8 rotation; - /// bit set if player has won / lost uint16 won; uint16 lost; @@ -269,31 +260,11 @@ class scenario_t void rotate90(const sint16 y_size); - /// @{ - /// @name Coordinate and direction transform between script and world - /** - * rotate actual world coordinates back - * coordinates after transform are like in the - * scenario's original savegame - */ - void koord_w2sq(koord &) const; - /** * rotate original coordinates to actual world coordinates + * uses the methods in script_api */ - void koord_sq2w(koord &); - - /** - * rotate original direction to actual world coordinates direction - */ - void ribi_w2sq(ribi_t::ribi &r) const; - - /** - * rotate actual world coordinates direction to original direction - */ - void ribi_sq2w(ribi_t::ribi &r) const; - - /// @} + void koord_sq2w(koord &) const; /** * Text to be displayed in the finance info window @@ -318,9 +289,9 @@ class scenario_t void update_scenario_texts(); /** - * opens scenario info window at result tab + * opens scenario info window at tab @p tab. */ - bool open_info_win() const; + bool open_info_win(const char* tab = "result") const; /** @@ -426,6 +397,12 @@ class scenario_t */ void clear_rules(); + /** + * Toolbars/active tools need an update due to changed rules; update is done in step(). + * @ingroup squirrel-api + */ + void gui_needs_update() { need_toolbar_update = true; } + /** * Checks if player can use this tool at all. * Called for instance in karte_t::local_set_tool to change active tool or when filling toolbars. @@ -449,6 +426,26 @@ class scenario_t */ const char* is_schedule_allowed(const player_t* player, const schedule_t* schedule); + /** + * Checks if player can use this convoy. + * Called when player wants to start convoy at depot. + * + * @param player player + * @param cnv convoy + * @param depot depot + * + * @return null if allowed, an error message otherwise + */ + const char* is_convoy_allowed(const player_t* player, convoihandle_t cnv, depot_t* depot); + + /** + * Called when player click link in scenario windows, after position changed. + * + * @param pos coordinate go to in link + * + * @return an error message otherwise or null + */ + const char* jump_to_link_executed(koord3d pos); /// @return debug dump of forbidden tools const char* get_forbidden_text(); diff --git a/dataobj/schedule.cc b/dataobj/schedule.cc index 64cafb84758..bcf83844355 100644 --- a/dataobj/schedule.cc +++ b/dataobj/schedule.cc @@ -37,7 +37,7 @@ void schedule_t::copy_from(const schedule_t *src) dbg->fatal("schedule_t::copy_to()","cannot copy from NULL"); } entries.clear(); - FOR(minivec_tpl, const& i, src->entries) { + for(schedule_entry_t const& i : src->entries) { entries.append(i); } set_current_stop( src->get_current_stop() ); @@ -468,7 +468,7 @@ void schedule_t::rdwr(loadsave_t *file) void schedule_t::rotate90( sint16 y_size ) { // now we have to rotate all entries ... - FOR(minivec_tpl, & i, entries) { + for(schedule_entry_t & i : entries) { i.pos.rotate90(y_size); } } @@ -889,8 +889,7 @@ void schedule_t::gimme_short_stop_name(cbuffer_t& buf, karte_t* welt, player_t c bool schedule_t::is_contained (koord3d pos) { - FOR(minivec_tpl, entry, entries) - { + for(schedule_entry_t entry : entries) { if(pos == entry.pos) { return true; diff --git a/dataobj/settings.h b/dataobj/settings.h index 11906a8277a..ba23561d3ce 100644 --- a/dataobj/settings.h +++ b/dataobj/settings.h @@ -8,6 +8,7 @@ #include + #include "../simtypes.h" #include "../simconst.h" #include "../simunits.h" diff --git a/dataobj/tabfile.cc b/dataobj/tabfile.cc index d352255f444..b5523ba57e3 100644 --- a/dataobj/tabfile.cc +++ b/dataobj/tabfile.cc @@ -854,5 +854,6 @@ void tabfile_t::format_key(char *key) *t++ = *s; } } + *t = '\0'; } diff --git a/dataobj/translator.cc b/dataobj/translator.cc index f994b7fbae3..21c527e862b 100644 --- a/dataobj/translator.cc +++ b/dataobj/translator.cc @@ -201,7 +201,7 @@ void translator::clear_custom_list(vector_tpl>&name_list) } void translator::clear_custom_list(vector_tpl&name_list) { - FOR(vector_tpl, const i, name_list) { + for(char* const i : name_list) { free(i); } name_list.clear(); @@ -521,6 +521,20 @@ void translator::init_custom_names(int lang) /* now on to the translate stuff */ +static bool is_format_string(const char* str) +{ + // %._CITY_SYLL + if (*str == '%' && *(str+1) && strcmp(str+2, "_CITY_SYLL")==0) { + return false; + } + // .center, .suburb, .extern + if (*str && (strcmp(str+1, "center")==0 || strcmp(str+1, "suburb")==0 || strcmp(str+1, "extern")==0) ) { + return false; + } + + return true; +} + static void load_language_file_body(FILE* file, stringhashtable_tpl* table, bool language_is_utf, bool file_is_utf, bool language_is_latin2 ) { char buffer1 [4096]; @@ -540,14 +554,20 @@ static void load_language_file_body(FILE* file, stringhashtable_tplset( raw, translated ); - } - else { + // check format strings (only for unicode, ignore special strings) + if(language_is_utf && (is_format_string(raw) && !cbuffer_t::check_and_repair_format_strings(raw, translated, &repaired) ) ) { free(raw); free(translated); + continue; } + else if (repaired) { + free(translated); + translated = repaired; + } + + table->set( raw, translated ); } } } while (!feof(file)); @@ -589,7 +609,7 @@ void translator::load_language_file(FILE* file) //load up translations, putting them into //language table of index 'lang' load_language_file_body(file, &langs[single_instance.lang_count].texts, true, file_is_utf, langs[single_instance.lang_count].is_latin2_based ); - } +} static translator::lang_info* get_lang_by_iso(const char *iso) diff --git a/descriptor/building_desc.h b/descriptor/building_desc.h index 11b7290248a..8891fdbfd92 100644 --- a/descriptor/building_desc.h +++ b/descriptor/building_desc.h @@ -297,6 +297,7 @@ class building_desc_t : public obj_desc_timelined_t { bool is_city_building() const { return is_type(city_res) || is_type(city_com) || is_type(city_ind); } bool is_transport_building() const { return type > headquarters && type <= flat_dock; } bool is_signalbox() const { return is_type(signalbox); } + bool is_depot() const { return is_type(depot); } bool is_connected_with_town() const; diff --git a/descriptor/reader/obj_reader.cc b/descriptor/reader/obj_reader.cc index 570bca9bc9a..72872da586d 100644 --- a/descriptor/reader/obj_reader.cc +++ b/descriptor/reader/obj_reader.cc @@ -58,7 +58,7 @@ bool obj_reader_t::finish_rd() { resolve_xrefs(); - FOR(obj_map, const& i, *obj_reader) { + for(auto const& i : *obj_reader) { DBG_MESSAGE("obj_reader_t::finish_rd()","Checking %s objects...", i.value->get_type_name()); if (!i.value->successfully_loaded()) { @@ -100,7 +100,7 @@ bool obj_reader_t::load(const char *path, const char *message) } find.search(buf, "pak"); - FOR(searchfolder_t, const& i, find) { + for(const char* const& i : find) { read_file(i); } } @@ -148,9 +148,9 @@ DBG_MESSAGE("obj_reader_t::load()","big logo %p", skinverwaltung_t::biglogosymbo DBG_MESSAGE("obj_reader_t::load()", "reading from '%s'", name.c_str()); uint n = 0; - FORX(searchfolder_t, const& i, find, ++n) { + for(char* const& i : find) { read_file(i); - if ((n & step) == 0 && drawing) { + if ((n++ & step) == 0 && drawing) { ls.set_progress(n); } } @@ -276,7 +276,7 @@ void obj_reader_t::skip_nodes(FILE *fp,uint32 version) void obj_reader_t::resolve_xrefs() { slist_tpl xref_nodes; - FOR(unresolved_map, const& u, unresolved) { + for(auto const& u : unresolved) { for(auto const& i: u.value) { obj_desc_t *obj_loaded = NULL; @@ -290,7 +290,7 @@ void obj_reader_t::resolve_xrefs() dbg->warning("obj_reader_t::resolve_xrefs()", "cannot resolve '%4.4s-%s'", &u.key, i.key); } - FOR(slist_tpl, const x, i.value) { + for(obj_desc_t** const x : i.value) { if (!obj_loaded && fatals.get(x)) { dbg->fatal("obj_reader_t::resolve_xrefs()", "cannot resolve '%4.4s-%s'", &u.key, i.key); } diff --git a/display/font.cc b/display/font.cc index 315cc1e4ed5..d0eb0aa60d7 100644 --- a/display/font.cc +++ b/display/font.cc @@ -279,15 +279,8 @@ bool font_t::load_from_freetype(const char *fname, int pixel_height) for( uint32 glyph_nr=0; glyph_nr<0xFFFF; glyph_nr++ ) { - uint32 idx = FT_Get_Char_Index( face, glyph_nr ); - if( idx==0 && glyph_nr!=0 ) { - // glyph not there, we need to render glyph 0 instead - glyphs[glyph_nr].advance = 0xFF; - continue; - } - /* load glyph image into the slot (erase previous one) */ - if( FT_Load_Glyph( face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME ) != FT_Err_Ok ) { + if( FT_Load_Char( face, glyph_nr, FT_LOAD_RENDER | FT_LOAD_MONOCHROME ) != FT_Err_Ok ) { // glyph not there ... glyphs[glyph_nr].advance = 0xFF; continue; diff --git a/display/simgraph.h b/display/simgraph.h index 68e943a5965..c086c66ad7e 100644 --- a/display/simgraph.h +++ b/display/simgraph.h @@ -8,7 +8,6 @@ #include "../simcolor.h" -#include "../unicode.h" #include "../simtypes.h" #include "clip_num.h" #include "simimg.h" @@ -277,7 +276,7 @@ inline void display_set_image_proc( bool is_global ) PIXVAL display_blend_colors(PIXVAL background, PIXVAL foreground, int percent_blend); // blends a rectangular region -void display_blend_wh_rgb(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, int percent_blend); +void display_blend_wh_rgb(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, int percent_blend ); void display_linear_gradient_wh_rgb(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, int percent_blend_start, int percent_blend_end); void display_vlinear_gradient_wh_rgb(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, PIXVAL color, int percent_blend_start, int percent_blend_end); @@ -331,14 +330,6 @@ scr_coord_val display_get_char_width(utf32 c); /* returns true, if this is a valid character */ bool has_character( utf16 char_code ); -/** - * Returns the width of the widest character in a string. - * @param text pointer to a string of characters to evaluate. - * @param len length of text buffer to evaluate. If set to 0, - * evaluate until null termination. - */ -scr_coord_val display_get_char_max_width(const char* text, size_t len=0); - /** * For the next logical character in the text, returns the character code * as well as retrieves the char byte count and the screen pixel width @@ -358,7 +349,7 @@ utf32 get_prev_char_with_metrics(const char* &text, const char *const text_start * If an ellipsis len is given, it will only return the last character up to this len if the full length cannot be fitted * @returns index of next character. if text[index]==0 the whole string fits */ -size_t display_fit_proportional( const char *text, scr_coord_val max_width, scr_coord_val ellipsis_width=0 ); +size_t display_fit_proportional( const char *text, scr_coord_val max_width); /* routines for string len (macros for compatibility with old calls) */ #define proportional_string_width(text) display_calc_proportional_string_len_width(text, 0x7FFF) @@ -368,7 +359,7 @@ size_t display_fit_proportional( const char *text, scr_coord_val max_width, scr_ int display_calc_proportional_string_len_width(const char* text, size_t len); // box which will contain the multi (or single) line of text -void display_calc_proportional_multiline_string_len_width( int &xw, int &yh, const char *text, size_t len ); +void display_calc_proportional_multiline_string_len_width( int &xw, int &yh, const char *text); /* * len parameter added - use -1 for previous behaviour. diff --git a/display/simgraph0.cc b/display/simgraph0.cc index 96df9195e36..744a622cc2e 100644 --- a/display/simgraph0.cc +++ b/display/simgraph0.cc @@ -35,7 +35,6 @@ PIXVAL line_color_idx_to_rgb(uint8) return 0; } - void env_t_rgb_to_system_colors() { } @@ -300,29 +299,11 @@ void display_filled_roundbox_clip(scr_coord_val, scr_coord_val, scr_coord_val, s { } -size_t get_next_char(const char*, size_t pos) -{ - return pos + 1; -} - -sint32 get_prev_char(const char*, sint32 pos) -{ - if (pos <= 0) { - return 0; - } - return pos - 1; -} - scr_coord_val display_get_char_width(utf32) { return 0; } -scr_coord_val display_get_char_max_width(const char*, size_t) -{ - return 0; -} - utf32 get_next_char_with_metrics(const char* &, unsigned char &, unsigned char &) { return 0; @@ -338,7 +319,7 @@ bool has_character( utf16 ) return false; } -size_t display_fit_proportional(const char *, scr_coord_val, scr_coord_val) +size_t display_fit_proportional(const char *, scr_coord_val) { return 0; } @@ -349,7 +330,7 @@ int display_calc_proportional_string_len_width(const char*, size_t) } -void display_calc_proportional_multiline_string_len_width( int &xw, int &yh, const char *, size_t ) +void display_calc_proportional_multiline_string_len_width( int &xw, int &yh, const char *) { xw = yh = 0; } diff --git a/display/simgraph16.cc b/display/simgraph16.cc index 81357482edb..1faa3e640f4 100644 --- a/display/simgraph16.cc +++ b/display/simgraph16.cc @@ -23,6 +23,7 @@ #include "../unicode.h" #include "../simticker.h" #include "../utils/simstring.h" +#include "../unicode.h" #include "../io/raw_image.h" #include "../gui/simwin.h" @@ -255,7 +256,33 @@ int default_font_ascent = 0; int default_font_linespace = 0; -#define RGBMAPSIZE (0x8000+LIGHT_COUNT+MAX_PLAYER_COUNT) +#define RGBMAPSIZE (0x8000+LIGHT_COUNT+MAX_PLAYER_COUNT+1024 /* 343 transparent */) + +// RGB 555/565 specific functions + +// different masks needed for RGB 555 and RGB 565 for blending +#ifdef RGB555 +#define ONE_OUT (0x3DEF) // mask out bits after applying >>1 +#define TWO_OUT (0x1CE7) // mask out bits after applying >>2 +inline PIXVAL rgb(PIXVAL r, PIXVAL g, PIXVAL b) { return (r << 10) | (g << 5) | b; } +#define MASK_32 (0x03e0f81f) // mask out bits after transforming to 32bit +inline PIXVAL red(PIXVAL rgb) { return rgb >> 10; } +inline PIXVAL green(PIXVAL rgb) { return (rgb >> 5) & 0x1F; } +#else +#define ONE_OUT (0x7bef) // mask out bits after applying >>1 +#define TWO_OUT (0x39E7) // mask out bits after applying >>2 +#define MASK_32 (0x07e0f81f) // mask out bits after transforming to 32bit +inline PIXVAL rgb(PIXVAL r, PIXVAL g, PIXVAL b) { return (r << 11) | (g << 5) | b; } +inline PIXVAL red(PIXVAL rgb) { return rgb >> 11; } +inline PIXVAL green(PIXVAL rgb) { return (rgb >> 5) & 0x3F; } +#endif +inline PIXVAL blue(PIXVAL rgb) { return rgb & 0x1F; } +/** + * Implement shift-and-mask for rgb values: + * shift-right by 1 or 2, and mask it to a valid rgb number. + */ +inline PIXVAL rgb_shr1(PIXVAL c) { return (c >> 1) & ONE_OUT; } +inline PIXVAL rgb_shr2(PIXVAL c) { return (c >> 2) & TWO_OUT; } /* @@ -300,22 +327,6 @@ static PIXVAL specialcolormap_day_night[256]; PIXVAL specialcolormap_all_day[256]; -/* - * contains all color conversions for transparency - * 16 player colors, 15 special colors and 1024 3 4 3 encoded colors for transparent base - */ -static PIXVAL transparent_map_day_night[MAX_PLAYER_COUNT+LIGHT_COUNT+1024]; -static PIXVAL transparent_map_all_day[MAX_PLAYER_COUNT+LIGHT_COUNT+1024]; -//static PIXVAL *transparent_map_current; - -/* - * contains all color conversions for transparency - * 16 player colors, 15 special colors and 1024 3 4 3 encoded colors for transparent base - */ -static uint8 transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+1024)*4]; -static uint8 transparent_map_all_day_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+1024)*4]; -//static uint8 *transparent_map_current_rgb; - // offsets of first and second company color static uint8 player_offsets[MAX_PLAYER_COUNT][2]; @@ -354,14 +365,6 @@ struct imd { #define TRANSPARENT_RUN (0x8000u) -// different masks needed for RGB 555 and RGB 565 for blending -#define ONE_OUT_16 (0x7bef) -#define TWO_OUT_16 (0x39E7) -#define ONE_OUT_15 (0x3DEF) -#define TWO_OUT_15 (0x1CE7) - -static int bitdepth = 16; - static scr_coord_val disp_width = 640; static scr_coord_val disp_actual_width = 640; static scr_coord_val disp_height = 480; @@ -1201,27 +1204,6 @@ static void activate_player_color(sint8 player_nr, bool daynight) for(i=0; i<8; i++ ) { rgbmap_all_day[0x8000+i] = specialcolormap_all_day[player_offsets[player_day][0]+i]; rgbmap_all_day[0x8008+i] = specialcolormap_all_day[player_offsets[player_day][1]+i]; -#ifdef RGB555 - transparent_map_all_day[i] = (specialcolormap_all_day[player_offsets[player_day][0] + i] >> 2) & TWO_OUT_15; - transparent_map_all_day[i + 8] = (specialcolormap_all_day[player_offsets[player_day][1] + i] >> 2) & TWO_OUT_15; - // those save RGB components - transparent_map_all_day_rgb[i * 4 + 0] = specialcolormap_all_day[player_offsets[player_day][0] + i] >> 10; - transparent_map_all_day_rgb[i * 4 + 1] = (specialcolormap_all_day[player_offsets[player_day][0] + i] >> 5) & 0x31; - transparent_map_all_day_rgb[i * 4 + 2] = specialcolormap_all_day[player_offsets[player_day][0] + i] & 0x1F; - transparent_map_all_day_rgb[i * 4 + 0 + 32] = specialcolormap_all_day[player_offsets[player_day][1] + i] >> 10; - transparent_map_all_day_rgb[i * 4 + 1 + 32] = (specialcolormap_all_day[player_offsets[player_day][1] + i] >> 5) & 0x1F; - transparent_map_all_day_rgb[i * 4 + 2 + 32] = specialcolormap_all_day[player_offsets[player_day][1] + i] & 0x1F; -#else - transparent_map_all_day[i] = (specialcolormap_all_day[player_offsets[player_day][0] + i] >> 2) & TWO_OUT_16; - transparent_map_all_day[i + 8] = (specialcolormap_all_day[player_offsets[player_day][1] + i] >> 2) & TWO_OUT_16; - // those save RGB components - transparent_map_all_day_rgb[i * 4 + 0] = specialcolormap_all_day[player_offsets[player_day][0] + i] >> 11; - transparent_map_all_day_rgb[i * 4 + 1] = (specialcolormap_all_day[player_offsets[player_day][0] + i] >> 5) & 0x3F; - transparent_map_all_day_rgb[i * 4 + 2] = specialcolormap_all_day[player_offsets[player_day][0] + i] & 0x1F; - transparent_map_all_day_rgb[i * 4 + 0 + 32] = specialcolormap_all_day[player_offsets[player_day][1] + i] >> 11; - transparent_map_all_day_rgb[i * 4 + 1 + 32] = (specialcolormap_all_day[player_offsets[player_day][1] + i] >> 5) & 0x3F; - transparent_map_all_day_rgb[i * 4 + 2 + 32] = specialcolormap_all_day[player_offsets[player_day][1] + i] & 0x1F; -#endif } } rgbmap_current = rgbmap_all_day; @@ -1234,27 +1216,6 @@ static void activate_player_color(sint8 player_nr, bool daynight) for(i=0; i<8; i++ ) { rgbmap_day_night[0x8000+i] = specialcolormap_day_night[player_offsets[player_night][0]+i]; rgbmap_day_night[0x8008+i] = specialcolormap_day_night[player_offsets[player_night][1]+i]; -#ifdef RGB555 - transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 2) & TWO_OUT_15; - transparent_map_day_night[i + 8] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 2) & TWO_OUT_15; - // those save RGB components - transparent_map_day_night_rgb[i * 4 + 0] = specialcolormap_day_night[player_offsets[player_day][0] + i] >> 10; - transparent_map_day_night_rgb[i * 4 + 1] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 5) & 0x31; - transparent_map_day_night_rgb[i * 4 + 2] = specialcolormap_day_night[player_offsets[player_day][0] + i] & 0x1F; - transparent_map_day_night_rgb[i * 4 + 0 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] >> 10; - transparent_map_day_night_rgb[i * 4 + 1 + 32] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 5) & 0x1F; - transparent_map_day_night_rgb[i * 4 + 2 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] & 0x1F; -#else - transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 2) & TWO_OUT_16; - transparent_map_day_night[i+8] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 2) & TWO_OUT_16; - // those save RGB components - transparent_map_day_night_rgb[i*4+0] = specialcolormap_day_night[player_offsets[player_day][0]+i] >> 11; - transparent_map_day_night_rgb[i*4+1] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 5) & 0x3F; - transparent_map_day_night_rgb[i*4+2] = specialcolormap_day_night[player_offsets[player_day][0]+i] & 0x1F; - transparent_map_day_night_rgb[i*4+0+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] >> 11; - transparent_map_day_night_rgb[i*4+1+32] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 5) & 0x3F; - transparent_map_day_night_rgb[i*4+2+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] & 0x1F; -#endif } } rgbmap_current = rgbmap_day_night; @@ -1273,15 +1234,10 @@ static void recode() } -// to switch between 15 bit and 16 bit recoding ... -typedef void (*display_recode_img_src_target_proc)(scr_coord_val h, PIXVAL *src, PIXVAL *target); -static display_recode_img_src_target_proc recode_img_src_target = NULL; - - /** * Convert a certain image data to actual output data */ -static void recode_img_src_target_15(scr_coord_val h, PIXVAL *src, PIXVAL *target) +static void recode_img_src_target(scr_coord_val h, PIXVAL *src, PIXVAL *target) { if( h > 0 ) { do { @@ -1295,46 +1251,13 @@ static void recode_img_src_target_15(scr_coord_val h, PIXVAL *src, PIXVAL *targe while( runlen-- ) { if( *src < 0x8020+(31*16) ) { // expand transparent player color - PIXVAL rgb555 = rgbmap_day_night[(*src-0x8020)/31+0x8000]; PIXVAL alpha = (*src-0x8020) % 31; - PIXVAL pix = ((rgb555 >> 6) & 0x0380) | ((rgb555 >> 4) & 0x0038) | ((rgb555 >> 2) & 0x07); - *target++ = 0x8020 + 31*31 + pix*31 + alpha; - src ++; - } - else { - *target++ = *src++; - } - } - } - else { - // now just convert the color pixels - while( runlen-- ) { - *target++ = rgbmap_day_night[*src++]; - } - } - // next clear run or zero = end - } while( (runlen = *target++ = *src++) ); - } while( --h ); - } -} - -static void recode_img_src_target_16(scr_coord_val h, PIXVAL *src, PIXVAL *target) -{ - if( h > 0 ) { - do { - uint16 runlen = *target++ = *src++; - // decode rows - do { - // clear run is always ok - runlen = *target++ = *src++; - if( runlen & TRANSPARENT_RUN ) { - runlen &= ~TRANSPARENT_RUN; - while( runlen-- ) { - if( *src < 0x8020+(31*16) ) { - // expand transparent player color - PIXVAL rgb565 = rgbmap_day_night[(*src-0x8020)/31+0x8000]; - PIXVAL alpha = (*src-0x8020) % 31; - PIXVAL pix = ((rgb565 >> 6) & 0x0380) | ((rgb565 >> 3) & 0x0078) | ((rgb565 >> 2) & 0x07); + PIXVAL rgb = rgbmap_day_night[(*src-0x8020)/31+0x8000]; +#ifdef RGB555 + PIXVAL pix = ((rgb >> 6) & 0x0380) | ((rgb >> 4) & 0x0038) | ((rgb >> 2) & 0x07); +#else + PIXVAL pix = ((rgb >> 6) & 0x0380) | ((rgb >> 3) & 0x0078) | ((rgb >> 2) & 0x07); +#endif *target++ = 0x8020 + 31*31 + pix*31 + alpha; src ++; } @@ -1989,28 +1912,13 @@ static void calc_base_pal_from_night_shift(const int night) int R = (i & 0x0380) >> 2; int G = (i & 0x0078) << 1; int B = (i & 0x0007) << 5; - // lines generate all possible colors in 343RGB code - input // however the result is in 888RGB - 8bit per channel R = (int)(R * RG_night_multiplier); G = (int)(G * RG_night_multiplier); B = (int)(B * B_night_multiplier); - -#ifdef RGB555 - // 15 bit colors form here! PIXVAL color = get_system_color(R, G, B); - transparent_map_day_night[MAX_PLAYER_COUNT + LIGHT_COUNT + i] = (color >> 2) & TWO_OUT_15; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 0] = color >> 10; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 1] = (color >> 5) & 0x1F; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 2] = color & 0x1F; -#else - // 16 bit colors form here! - PIXVAL color = get_system_color(R, G, B); - transparent_map_day_night[MAX_PLAYER_COUNT + LIGHT_COUNT + i] = (color >> 2) & TWO_OUT_16; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 0] = color >> 11; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 1] = (color >> 5) & 0x3F; - transparent_map_day_night_rgb[(MAX_PLAYER_COUNT + LIGHT_COUNT + i) * 4 + 2] = color & 0x1F; -#endif + rgbmap_day_night[0x8000 +MAX_PLAYER_COUNT + LIGHT_COUNT + i] = color; } // player color map (and used for map display etc.) @@ -2036,28 +1944,6 @@ static void calc_base_pal_from_night_shift(const int night) for(i=0; i<8; i++ ) { rgbmap_day_night[0x8000+i] = specialcolormap_day_night[player_offsets[0][0]+i]; rgbmap_day_night[0x8008+i] = specialcolormap_day_night[player_offsets[0][1]+i]; -#ifdef RGB555 - // 15 bit colors from here! - transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 2) & TWO_OUT_15; - transparent_map_day_night[i + 8] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 2) & TWO_OUT_15; - transparent_map_day_night_rgb[i * 4 + 0] = specialcolormap_day_night[player_offsets[player_day][0] + i] >> 10; - transparent_map_day_night_rgb[i * 4 + 1] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 5) & 0x1F; - transparent_map_day_night_rgb[i * 4 + 2] = specialcolormap_day_night[player_offsets[player_day][0] + i] & 0x1F; - transparent_map_day_night_rgb[i * 4 + 0 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] >> 10; - transparent_map_day_night_rgb[i * 4 + 1 + 32] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 5) & 0x1F; - transparent_map_day_night_rgb[i * 4 + 2 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] & 0x1F; -#else - // 16 bit colors from here! - transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 2) & TWO_OUT_16; - transparent_map_day_night[i + 8] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 2) & TWO_OUT_16; - // save RGB components - transparent_map_day_night_rgb[i * 4 + 0] = specialcolormap_day_night[player_offsets[player_day][0] + i] >> 11; - transparent_map_day_night_rgb[i * 4 + 1] = (specialcolormap_day_night[player_offsets[player_day][0] + i] >> 5) & 0x3F; - transparent_map_day_night_rgb[i * 4 + 2] = specialcolormap_day_night[player_offsets[player_day][0] + i] & 0x1F; - transparent_map_day_night_rgb[i * 4 + 0 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] >> 11; - transparent_map_day_night_rgb[i * 4 + 1 + 32] = (specialcolormap_day_night[player_offsets[player_day][1] + i] >> 5) & 0x3F; - transparent_map_day_night_rgb[i * 4 + 2 + 32] = specialcolormap_day_night[player_offsets[player_day][1] + i] & 0x1F; -#endif } player_night = 0; @@ -2077,19 +1963,6 @@ static void calc_base_pal_from_night_shift(const int night) PIXVAL color = get_system_color(R > 0 ? R : 0, G > 0 ? G : 0, B > 0 ? B : 0); rgbmap_day_night[0x8000 + MAX_PLAYER_COUNT + i] = color; -#ifdef RGB555 - // 15 bit colors from here! - transparent_map_day_night[i + MAX_PLAYER_COUNT] = (color >> 2) & TWO_OUT_15; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 0] = color >> 10; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 1] = (color >> 5) & 0x1F; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 2] = color & 0x1F; -#else - // 16 bit colors from here! - transparent_map_day_night[i + MAX_PLAYER_COUNT] = (color >> 2) & TWO_OUT_16; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 0] = color >> 11; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 1] = (color >> 5) & 0x3F; - transparent_map_day_night_rgb[(i + MAX_PLAYER_COUNT) * 4 + 2] = color & 0x1F; -#endif } // convert to RGB xxx @@ -2118,8 +1991,6 @@ void display_set_player_color_scheme(const int player, const uint8 col1, const u // and recalculate map (and save it) calc_base_pal_from_night_shift(0); memcpy(rgbmap_all_day, rgbmap_day_night, RGBMAPSIZE * sizeof(PIXVAL)); - memcpy(transparent_map_all_day, transparent_map_day_night, lengthof(transparent_map_day_night) * sizeof(PIXVAL)); - memcpy(transparent_map_all_day_rgb, transparent_map_day_night_rgb, lengthof(transparent_map_day_night_rgb) * sizeof(uint8)); if(night_shift!=0) { calc_base_pal_from_night_shift(night_shift); } @@ -2261,6 +2132,8 @@ void display_get_base_image_offset(image_id image, scr_coord_val *xoff, scr_coor // ------------------ display all kind of images from here on ------------------------------ +// forward declaration, implementation is further below +PIXVAL colors_blend_alpha32(PIXVAL background, PIXVAL foreground, int alpha); /** * Copy Pixel from src to dest @@ -2275,45 +2148,6 @@ static inline void pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const -#ifdef RGB555 -/** - * Copy pixel, replace player color - */ -static inline void colorpixcopy(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) -{ - if (*src < 0x8020) { - while (src < end) { - *dest++ = rgbmap_current[*src++]; - } - } - else { - while (src < end) { - // a semi-transparent pixel - uint16 alpha = ((*src - 0x8020) % 31) + 1; - if ((alpha & 0x07) == 0) { - const PIXVAL colval = transparent_map_day_night[(*src++ - 0x8020) / 31]; - alpha /= 8; - *dest = alpha * colval + (4 - alpha) * (((*dest) >> 2) & TWO_OUT_15); - dest++; - } - else { - uint8* trans_rgb = transparent_map_day_night_rgb + ((*src++ - 0x8020) / 31) * 4; - const PIXVAL r_src = *trans_rgb++; - const PIXVAL g_src = *trans_rgb++; - const PIXVAL b_src = *trans_rgb++; - - const PIXVAL r_dest = (*dest >> 10); - const PIXVAL g_dest = (*dest >> 5) & 0x1F; - const PIXVAL b_dest = (*dest & 0x1F); - const PIXVAL r = r_dest + (((r_src - r_dest) * alpha) >> 5); - const PIXVAL g = g_dest + (((g_src - g_dest) * alpha) >> 5); - const PIXVAL b = b_dest + (((b_src - b_dest) * alpha) >> 5); - *dest++ = (r << 10) | (g << 5) | b; - } - } - } -} -#else /** * Copy pixel, replace player color */ @@ -2327,79 +2161,15 @@ static inline void colorpixcopy(PIXVAL* dest, const PIXVAL* src, const PIXVAL* c else { while (src < end) { // a semi-transparent pixel - uint16 alpha = ((*src - 0x8020) % 31) + 1; - //assert( *src>=0x8020+16*31 ); - if ((alpha & 0x07) == 0) { - const PIXVAL colval = transparent_map_day_night[(*src++ - 0x8020) / 31]; - alpha /= 8; - *dest = alpha * colval + (4 - alpha) * (((*dest) >> 2) & TWO_OUT_16); - dest++; - } - else { - uint8* trans_rgb = transparent_map_day_night_rgb + ((*src++ - 0x8020) / 31) * 4; - const PIXVAL r_src = *trans_rgb++; - const PIXVAL g_src = *trans_rgb++; - const PIXVAL b_src = *trans_rgb++; - - // const PIXVAL colval = transparent_map_day_night[(*src++-0x8020)/31]; - // const PIXVAL r_src = (colval >> 11); - // const PIXVAL g_src = (colval >> 5) & 0x3F; - // const PIXVAL b_src = colval & 0x1F; - // all other alphas - const PIXVAL r_dest = (*dest >> 11); - const PIXVAL g_dest = (*dest >> 5) & 0x3F; - const PIXVAL b_dest = (*dest & 0x1F); - const PIXVAL r = r_dest + (((r_src - r_dest) * alpha) >> 5); - const PIXVAL g = g_dest + (((g_src - g_dest) * alpha) >> 5); - const PIXVAL b = b_dest + (((b_src - b_dest) * alpha) >> 5); - *dest++ = (r << 11) | (g << 5) | b; - } + uint16 aux = *src++ - 0x8020; + uint16 alpha = (aux % 31) + 1; + *dest = colors_blend_alpha32(*dest, rgbmap_day_night[0x8000 + aux / 31], alpha); + dest++; } } } -#endif - -#ifdef RGB555 -/** - * Copy pixel, replace player color - */ -static inline void colorpixcopydaytime(PIXVAL* dest, const PIXVAL* src, const PIXVAL* const end) -{ - if (*src < 0x8020) { - while (src < end) { - *dest++ = rgbmap_current[*src++]; - } - } - else { - while (src < end) { - // a semi-transparent pixel - uint16 alpha = ((*src - 0x8020) % 31) + 1; - if ((alpha & 0x07) == 0) { - const PIXVAL colval = transparent_map_all_day[(*src++ - 0x8020) / 31]; - alpha /= 8; - *dest = alpha * colval + (4 - alpha) * (((*dest) >> 2) & TWO_OUT_15); - dest++; - } - else { - uint8* trans_rgb = transparent_map_all_day_rgb + ((*src++ - 0x8020) / 31) * 4; - const PIXVAL r_src = *trans_rgb++; - const PIXVAL g_src = *trans_rgb++; - const PIXVAL b_src = *trans_rgb++; - - const PIXVAL r_dest = (*dest >> 10); - const PIXVAL g_dest = (*dest >> 5) & 0x1F; - const PIXVAL b_dest = (*dest & 0x1F); - const PIXVAL r = r_dest + (((r_src - r_dest) * alpha) >> 5); - const PIXVAL g = g_dest + (((g_src - g_dest) * alpha) >> 5); - const PIXVAL b = b_dest + (((b_src - b_dest) * alpha) >> 5); - *dest++ = (r << 10) | (g << 5) | b; - } - } - } -} -#else /** * Copy pixel, replace player color */ @@ -2413,38 +2183,13 @@ static inline void colorpixcopydaytime(PIXVAL* dest, const PIXVAL* src, const PI else { while (src < end) { // a semi-transparent pixel - uint16 alpha = ((*src - 0x8020) % 31) + 1; - //assert( *src>=0x8020+16*31 ); - if ((alpha & 0x07) == 0) { - const PIXVAL colval = transparent_map_all_day[(*src++ - 0x8020) / 31]; - alpha /= 8; - *dest = alpha * colval + (4 - alpha) * (((*dest) >> 2) & TWO_OUT_16); - dest++; - } - else { - uint8* trans_rgb = transparent_map_all_day_rgb + ((*src++ - 0x8020) / 31) * 4; - const PIXVAL r_src = *trans_rgb++; - const PIXVAL g_src = *trans_rgb++; - const PIXVAL b_src = *trans_rgb++; - - // const PIXVAL colval = transparent_map_day_night[(*src++-0x8020)/31]; - // const PIXVAL r_src = (colval >> 11); - // const PIXVAL g_src = (colval >> 5) & 0x3F; - // const PIXVAL b_src = colval & 0x1F; - // all other alphas - const PIXVAL r_dest = (*dest >> 11); - const PIXVAL g_dest = (*dest >> 5) & 0x3F; - const PIXVAL b_dest = (*dest & 0x1F); - const PIXVAL r = r_dest + (((r_src - r_dest) * alpha) >> 5); - const PIXVAL g = g_dest + (((g_src - g_dest) * alpha) >> 5); - const PIXVAL b = b_dest + (((b_src - b_dest) * alpha) >> 5); - *dest++ = (r << 11) | (g << 5) | b; - } + uint16 aux = *src++ - 0x8020; + uint16 alpha = (aux % 31) + 1; + *dest = colors_blend_alpha32(*dest, rgbmap_all_day[0x8000 + aux / 31], alpha); + dest++; } } } -#endif - /** @@ -3157,6 +2902,36 @@ void display_base_img(const image_id n, scr_coord_val xp, scr_coord_val yp, cons } // number ok } +inline PIXVAL colors_blend25(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr2(background) + rgb_shr2(foreground); } +inline PIXVAL colors_blend50(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr1(foreground); } +inline PIXVAL colors_blend75(PIXVAL background, PIXVAL foreground) { return rgb_shr2(background) + rgb_shr1(foreground) + rgb_shr2(foreground); } +inline PIXVAL colors_blend_alpha32(PIXVAL background, PIXVAL foreground, int alpha) +{ + uint32 b = ((background << 16) | background) & MASK_32; + uint32 f = ((foreground << 16) | foreground) & MASK_32; + uint32 r = ((f * alpha + (32-alpha) * b) >> 5) & MASK_32; + return r | (r >> 16); +} + +// Blends two colors. Possible values for alpha: 0..32 +PIXVAL display_blend_colors_alpha32(PIXVAL background, PIXVAL foreground, int alpha) +{ + // alpha takes values 0 .. 32 + switch(alpha) { + case 0: // nothing to do ... + return background; + case 8: + return colors_blend25(background, foreground); + case 16: + return colors_blend50(background, foreground); + case 24: + return colors_blend75(background, foreground); + case 32: + return foreground; + default: + return colors_blend_alpha32(background, foreground, alpha); + } +} // Blends two colors @@ -3167,279 +2942,92 @@ PIXVAL display_blend_colors(PIXVAL background, PIXVAL foreground, int percent_bl switch( alpha ) { case 0: // nothing to do ... return background; -#ifdef RGB555 case 16: - { - const PIXVAL two = TWO_OUT_15; - return (3 * (((background) >> 2) & two)) + (((foreground) >> 2) & two); - } - case 32: - { - const PIXVAL one = ONE_OUT_15; - return ((((background) >> 1) & one)) + (((foreground) >> 1) & one); - } - case 48: - { - const PIXVAL two = TWO_OUT_15; - return ((((background) >> 2) & two)) + (3 * ((foreground) >> 2) & two); - } + return colors_blend25(background, foreground); - case 64: - return foreground; - - default: - // any percentage blending: SLOW! - // 555 BITMAPS - const PIXVAL r_src = (background >> 10) & 0x1F; - const PIXVAL g_src = (background >> 5) & 0x1F; - const PIXVAL b_src = background & 0x1F; - const PIXVAL r_dest = (foreground >> 10) & 0x1F; - const PIXVAL g_dest = (foreground >> 5) & 0x1F; - const PIXVAL b_dest = (foreground & 0x1F); - - const PIXVAL r = (r_dest * alpha + r_src * (64 - alpha) + 32) >> 6; - const PIXVAL g = (g_dest * alpha + g_src * (64 - alpha) + 32) >> 6; - const PIXVAL b = (b_dest * alpha + b_src * (64 - alpha) + 32) >> 6; - return (r << 10) | (g << 5) | b; -#else - case 16: - { - const PIXVAL two = TWO_OUT_16; - return (3 * (((background) >> 2) & two)) + (((foreground) >> 2) & two); - } case 32: - { - const PIXVAL one = ONE_OUT_16; - return ((((background) >> 1) & one)) + (((foreground) >> 1) & one); - } + return colors_blend50(background, foreground); + case 48: - { - const PIXVAL two = TWO_OUT_16; - return ((((background) >> 2) & two)) + (3 * ((foreground) >> 2) & two); - } + return colors_blend25(background, foreground); case 64: return foreground; default: // any percentage blending: SLOW! - // 565 colors - const PIXVAL r_src = (background >> 11); - const PIXVAL g_src = (background >> 5) & 0x3F; - const PIXVAL b_src = background & 0x1F; - const PIXVAL r_dest = (foreground >> 11); - const PIXVAL g_dest = (foreground >> 5) & 0x3F; - const PIXVAL b_dest = (foreground & 0x1F); + const PIXVAL r_src = red(background); + const PIXVAL g_src = green(background); + const PIXVAL b_src = blue(background); + const PIXVAL r_dest = red(foreground); + const PIXVAL g_dest = green(foreground); + const PIXVAL b_dest = blue(foreground); const PIXVAL r = (r_dest * alpha + r_src * (64 - alpha) + 32) >> 6; const PIXVAL g = (g_dest * alpha + g_src * (64 - alpha) + 32) >> 6; const PIXVAL b = (b_dest * alpha + b_src * (64 - alpha) + 32) >> 6; - return (r << 11) | (g << 5) | b; -#endif + return rgb(r, g, b); } + // ?? return display_blend_colors_alpha32(background, foreground, (percent_blend*32)/100); } /* from here code for transparent images */ typedef void (*blend_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL colour, const PIXVAL len); -static void pix_blend75_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (3*(((*src)>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15); - dest++; - src++; - } -} - - -static void pix_blend75_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (3*(((*src)>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16); - dest++; - src++; - } -} - - -static void pix_blend50_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((*src)>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15); - dest++; - src++; - } -} - - -static void pix_blend50_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((*src)>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16); - dest++; - src++; - } -} - - -static void pix_blend25_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((*src)>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15)); - dest++; - src++; - } -} +// templated structures to specialize for the different blend modes: 25/50/75 percent +struct blend25_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return 3 * rgb_shr2(background) + rgb_shr2(foreground); } }; +struct blend50_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return rgb_shr1(background) + rgb_shr1(foreground); } }; +struct blend75_t { static inline PIXVAL blend(PIXVAL background, PIXVAL foreground) { return rgb_shr2(background) + 3 * rgb_shr2(foreground); } }; -static void pix_blend25_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) +template void pix_blend_tpl(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { - *dest = (((*src)>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16)); + *dest = F::blend(*dest, *src); dest++; src++; } } - // the following 6 functions are for display_base_img_blend() -static void pix_blend_recode75_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) +template void pix_blend_recode_tpl(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { - *dest = (3*(((rgbmap_current[*src])>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15); + *dest = F::blend(*dest, rgbmap_current[*src]); dest++; src++; } } - -static void pix_blend_recode75_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) +template void pix_outline_tpl(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) { const PIXVAL *const end = dest + len; while (dest < end) { - *dest = (3*(((rgbmap_current[*src])>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16); + *dest = F::blend(*dest, colour); dest++; - src++; } } -static void pix_blend_recode50_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((rgbmap_current[*src])>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15); - dest++; - src++; - } -} +// save them for easier access +static blend_proc blend[3] = { + pix_blend_tpl, + pix_blend_tpl, + pix_blend_tpl }; +static blend_proc blend_recode[3] = { + pix_blend_recode_tpl, + pix_blend_recode_tpl, + pix_blend_recode_tpl}; -static void pix_blend_recode50_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((rgbmap_current[*src])>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16); - dest++; - src++; - } -} +static blend_proc outline[3] = { + pix_outline_tpl, + pix_outline_tpl, + pix_outline_tpl}; -static void pix_blend_recode25_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((rgbmap_current[*src])>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15)); - dest++; - src++; - } -} - - -static void pix_blend_recode25_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (((rgbmap_current[*src])>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16)); - dest++; - src++; - } -} - - -static void pix_outline75_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (3*((colour>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15); - dest++; - } -} - - -static void pix_outline75_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = (3*((colour>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16); - dest++; - } -} - - -static void pix_outline50_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = ((colour>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15); - dest++; - } -} - - -static void pix_outline50_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = ((colour>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16); - dest++; - } -} - - -static void pix_outline25_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = ((colour>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15)); - dest++; - } -} - - -static void pix_outline25_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - while (dest < end) { - *dest = ((colour>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16)); - dest++; - } -} - - -// will kept the actual values -static blend_proc blend[3]; -static blend_proc blend_recode[3]; -static blend_proc outline[3]; - /** * Blends a rectangular region with a color @@ -3473,41 +3061,21 @@ void display_blend_wh_rgb(scr_coord_val xp, scr_coord_val yp, scr_coord_val w, s default: // any percentage blending: SLOW! - if( blend[0] == pix_blend25_15 ) { - // 555 BITMAPS - const PIXVAL r_src = (colval >> 10) & 0x1F; - const PIXVAL g_src = (colval >> 5) & 0x1F; - const PIXVAL b_src = colval & 0x1F; - for( ; h>0; yp++, h-- ) { - PIXVAL *dest = textur + yp*disp_width + xp; - const PIXVAL *const end = dest + w; - while (dest < end) { - const PIXVAL r_dest = (*dest >> 10) & 0x1F; - const PIXVAL g_dest = (*dest >> 5) & 0x1F; - const PIXVAL b_dest = (*dest & 0x1F); - const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 6 ); - const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 6 ); - const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 6 ); - *dest++ = (r << 10) | (g << 5) | b; - } - } - } - else { - // 565 BITMAPS - const PIXVAL r_src = (colval >> 11); - const PIXVAL g_src = (colval >> 5) & 0x3F; - const PIXVAL b_src = colval & 0x1F; + { + const PIXVAL r_src = red(colval); + const PIXVAL g_src = green(colval); + const PIXVAL b_src = blue(colval); for( ; h>0; yp++, h-- ) { PIXVAL *dest = textur + yp*disp_width + xp; const PIXVAL *const end = dest + w; while (dest < end) { - const PIXVAL r_dest = (*dest >> 11); - const PIXVAL g_dest = (*dest >> 5) & 0x3F; - const PIXVAL b_dest = (*dest & 0x1F); + const PIXVAL r_dest = red(*dest); + const PIXVAL g_dest = green(*dest); + const PIXVAL b_dest = blue(*dest); const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 6 ); const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 6 ); const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 6 ); - *dest++ = (r << 11) | (g << 5) | b; + *dest++ = rgb(r, g, b); } } } @@ -3571,63 +3139,28 @@ static void display_img_blend_wc(scr_coord_val h, const scr_coord_val xp, const /* from here code for transparent images */ - -typedef void (*alpha_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL colour, const PIXVAL len); -static alpha_proc alpha; -static alpha_proc alpha_recode; - - -static void pix_alpha_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len) +static PIXVAL get_alpha_mask(const unsigned alpha_flags) { - const PIXVAL *const end = dest + len; - - const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0; - const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0; - const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0; - - while( dest < end ) { - // read mask components - always 15bpp - uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10); - - if( alpha_value > 30 ) { - // opaque, just copy source - *dest = *src; - } - else if( alpha_value > 0 ) { - alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value; - - //read screen components - 15bpp - const uint16 rbs = (*dest) & 0x7c1f; - const uint16 gs = (*dest) & 0x03e0; - - // read image components - 15bpp - const uint16 rbi = (*src) & 0x7c1f; - const uint16 gi = (*src) & 0x03e0; - - // calculate and write destination components - 16bpp - const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5; - const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5; - *dest = (rbd & 0x7c1f) | (gd & 0x03e0); - } - - dest++; - src++; - alphamap++; + PIXVAL mask = alpha_flags & ALPHA_RED ? 0x7c00 : 0; + if (alpha_flags & ALPHA_GREEN) { + mask |= 0x03e0; } + if (alpha_flags & ALPHA_BLUE) { + mask |= 0x001f; + } + return mask; } +typedef void (*alpha_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL colour, const PIXVAL len); -static void pix_alpha_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len) +static void alpha(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; - const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0; - const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0; - const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0; - while( dest < end ) { // read mask components - always 15bpp - uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10); + uint16 masked = *alphamap & alpha_mask; + uint16 alpha_value = (masked & 0x1f) + ((masked >> 5) & 0x1f) + ((masked >> 10) & 0x1f); if( alpha_value > 30 ) { // opaque, just copy source @@ -3636,18 +3169,7 @@ static void pix_alpha_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap else if( alpha_value > 0 ) { alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value; - //read screen components - 16bpp - const uint16 rbs = (*dest) & 0xf81f; - const uint16 gs = (*dest) & 0x07e0; - - // read image components 16bpp - const uint16 rbi = (*src) & 0xf81f; - const uint16 gi = (*src) & 0x07e0; - - // calculate and write destination components - 16bpp - const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5; - const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5; - *dest = (rbd & 0xf81f) | (gd & 0x07e0); + *dest = colors_blend_alpha32(*dest, *src, alpha_value); } dest++; @@ -3657,17 +3179,14 @@ static void pix_alpha_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap } -static void pix_alpha_recode_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len) +static void alpha_recode(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const PIXVAL alpha_mask, const PIXVAL , const PIXVAL len) { const PIXVAL *const end = dest + len; - const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0; - const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0; - const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0; - while( dest < end ) { // read mask components - always 15bpp - uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10); + uint16 masked = *alphamap & alpha_mask; + uint16 alpha_value = (masked & 0x1f) + ((masked >> 5) & 0x1f) + ((masked >> 10) & 0x1f); if( alpha_value > 30 ) { // opaque, just copy source @@ -3676,58 +3195,7 @@ static void pix_alpha_recode_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL *a else if( alpha_value > 0 ) { alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value; - //read screen components - 15bpp - const uint16 rbs = (*dest) & 0x7c1f; - const uint16 gs = (*dest) & 0x03e0; - - // read image components - 15bpp - const uint16 rbi = (rgbmap_current[*src]) & 0x7c1f; - const uint16 gi = (rgbmap_current[*src]) & 0x03e0; - - // calculate and write destination components - 16bpp - const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5; - const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5; - *dest = (rbd & 0x7c1f) | (gd & 0x03e0); - } - - dest++; - src++; - alphamap++; - } -} - - -static void pix_alpha_recode_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len) -{ - const PIXVAL *const end = dest + len; - - const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0; - const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0; - const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0; - - while( dest < end ) { - // read mask components - always 15bpp - uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10); - - if( alpha_value > 30 ) { - // opaque, just copy source - *dest = rgbmap_current[*src]; - } - else if( alpha_value > 0 ) { - alpha_value = alpha_value> 15 ? alpha_value + 1 : alpha_value; - - //read screen components - 16bpp - const uint16 rbs = (*dest) & 0xf81f; - const uint16 gs = (*dest) & 0x07e0; - - // read image components 16bpp - const uint16 rbi = (rgbmap_current[*src]) & 0xf81f; - const uint16 gi = (rgbmap_current[*src]) & 0x07e0; - - // calculate and write destination components - 16bpp - const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5; - const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5; - *dest = (rbd & 0xf81f) | (gd & 0x07e0); + *dest = colors_blend_alpha32(*dest, rgbmap_current[*src], alpha_value); } dest++; @@ -3737,7 +3205,7 @@ static void pix_alpha_recode_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *a } -static void display_img_alpha_wc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp, const PIXVAL *alphamap, const uint8 alpha_flags, int colour, alpha_proc p CLIP_NUM_DEF ) +static void display_img_alpha_wc(scr_coord_val h, const scr_coord_val xp, const scr_coord_val yp, const PIXVAL *sp, const PIXVAL *alphamap, const PIXVAL alpha_mask, int colour, alpha_proc p CLIP_NUM_DEF ) { if( h > 0 ) { PIXVAL *tp = textur + yp * disp_width; @@ -3761,7 +3229,7 @@ static void display_img_alpha_wc(scr_coord_val h, const scr_coord_val xp, const if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) { const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos); const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos); - p( tp + xpos + left, sp + left, alphamap + left, alpha_flags, colour, len - left ); + p( tp + xpos + left, sp + left, alphamap + left, alpha_mask, colour, len - left ); } sp += runlen; @@ -3840,21 +3308,11 @@ void display_rezoomed_img_blend(const image_id n, scr_coord_val xp, scr_coord_va // we use function pointer for the blend runs for the moment ... blend_proc pix_blend = (color_index&OUTLINE_FLAG) ? outline[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ] : blend[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ]; - // use horizontal clipping or skip it? - if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) { - // marking change? - if( dirty ) { - mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 ); - } - display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR ); - } - else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) { - display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR ); - // since height may be reduced, start marking here - if( dirty ) { - mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); - } + // marking change? + if( dirty ) { + mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); } + display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR ); } } } @@ -3929,21 +3387,11 @@ void display_rezoomed_img_alpha(const image_id n, const image_id alpha_n, const // get the real color const PIXVAL color = color_index & 0xFFFF; - // use horizontal clipping or skip it? - if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) { - // marking change? - if( dirty ) { - mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 ); - } - display_img_alpha_wc( h, xp, yp, sp, alphamap, alpha_flags, color, alpha CLIP_NUM_PAR ); - } - else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) { - display_img_alpha_wc( h, xp, yp, sp, alphamap, alpha_flags, color, alpha CLIP_NUM_PAR ); - // since height may be reduced, start marking here - if( dirty ) { - mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); - } + // marking change? + if( dirty ) { + mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 ); } + display_img_alpha_wc( h, xp, yp, sp, alphamap, get_alpha_mask(alpha_flags), color, alpha CLIP_NUM_PAR ); } } } @@ -4011,19 +3459,10 @@ void display_base_img_blend(const image_id n, scr_coord_val xp, scr_coord_val yp } } - // use horizontal clipping or skip it? - if( x >= CR.clip_rect.x && x + w <= CR.clip_rect.xx ) { - if( dirty ) { - mark_rect_dirty_nc( x, y, x + w - 1, y + h - 1 ); - } - display_img_blend_wc( h, x, y, sp, color, pix_blend CLIP_NUM_PAR ); - } - else { - if( dirty ) { - mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); - } - display_img_blend_wc( h, x, y, sp, color, pix_blend CLIP_NUM_PAR ); + if( dirty ) { + mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); } + display_img_blend_wc(h, x, y, sp, color, pix_blend CLIP_NUM_PAR); } } // number ok } @@ -4097,19 +3536,10 @@ void display_base_img_alpha(const image_id n, const image_id alpha_n, const unsi } } - // use horizontal clipping or skip it? - if( x >= CR.clip_rect.x && x + w <= CR.clip_rect.xx ) { - if( dirty ) { - mark_rect_dirty_nc( x, y, x + w - 1, y + h - 1 ); - } - display_img_alpha_wc( h, x, y, sp, alphamap, alpha_flags, color, alpha_recode CLIP_NUM_PAR ); - } - else { - if( dirty ) { - mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); - } - display_img_alpha_wc( h, x, y, sp, alphamap, alpha_flags, color, alpha_recode CLIP_NUM_PAR ); + if( dirty ) { + mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 ); } + display_img_alpha_wc( h, x, y, sp, alphamap, get_alpha_mask(alpha_flags), color, alpha_recode CLIP_NUM_PAR ); } } // number ok } @@ -4529,41 +3959,12 @@ bool display_load_font(const char *fname, bool reload) } -// unicode save moving in strings -size_t get_next_char(const char* text, size_t pos) -{ - return utf8_get_next_char((const utf8*)text, pos); -} - - -sint32 get_prev_char(const char* text, sint32 pos) -{ - if( pos <= 0 ) { - return 0; - } - return utf8_get_prev_char((const utf8*)text, pos); -} - - scr_coord_val display_get_char_width(utf32 c) { return default_font.get_glyph_advance(c); } -/* returns the width of this character or the default (Nr 0) character size */ -scr_coord_val display_get_char_max_width(const char* text, size_t len) { - - scr_coord_val max_len=0; - - for(unsigned n=0; (len && n do not advance text pointer // also stop at linebreaks byte_length = 0; @@ -4613,7 +4014,7 @@ bool has_character(utf16 char_code) * If an ellipsis len is given, it will only return the last character up to this len if the full length cannot be fitted * @returns index of next character. if text[index]==0 the whole string fits */ -size_t display_fit_proportional( const char *text, scr_coord_val max_width, scr_coord_val ellipsis_width ) +size_t display_fit_proportional( const char *text, scr_coord_val max_width) { size_t max_idx = 0; @@ -4622,28 +4023,11 @@ size_t display_fit_proportional( const char *text, scr_coord_val max_width, scr_ scr_coord_val current_offset = 0; const char *tmp_text = text; - while( get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_width > (current_offset+ellipsis_width+pixel_width) ) { + while( get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_width > (current_offset+pixel_width) ) { current_offset += pixel_width; max_idx += byte_length; } - size_t ellipsis_idx = max_idx; - - // now check if the text would fit completely - if( ellipsis_width && pixel_width > 0 ) { - // only when while above failed because of exceeding length - current_offset += pixel_width; - max_idx += byte_length; - // check the rest ... - while( get_next_char_with_metrics(tmp_text, byte_length, pixel_width) && max_width > (current_offset+pixel_width) ) { - current_offset += pixel_width; - max_idx += byte_length; - } - // if this fits, return end of string - if( max_width > (current_offset+pixel_width) ) { - return max_idx+byte_length; - } - } - return ellipsis_idx; + return max_idx; } @@ -4681,20 +4065,14 @@ utf32 get_prev_char_with_metrics(const char* &text, const char *const text_start */ int display_calc_proportional_string_len_width(const char *text, size_t len) { - const font_t* const fnt = &default_font; - unsigned int width = 0; - - // decode char - const char *const end = text + len; - while( text < end ) { - const utf8 *p = reinterpret_cast(text); - const utf32 iUnicode = utf8_decoder_t::decode(p); - text = reinterpret_cast(p); + uint8 byte_length = 0; + uint8 pixel_width = 0; + size_t idx = 0; + scr_coord_val width = 0; - if( iUnicode == UNICODE_NUL || iUnicode == '\n') { - return width; - } - width += fnt->get_glyph_advance(iUnicode); + while (get_next_char_with_metrics(text, byte_length, pixel_width) && idx < len) { + width += pixel_width; + idx += byte_length; } return width; @@ -4705,33 +4083,26 @@ int display_calc_proportional_string_len_width(const char *text, size_t len) /* display_calc_proportional_multiline_string_len_width * calculates the width and hieght of a box containing the text inside */ -void display_calc_proportional_multiline_string_len_width(int &xw, int &yh, const char *text, size_t len) +void display_calc_proportional_multiline_string_len_width(int &xw, int &yh, const char *text) { const font_t* const fnt = &default_font; int width = 0; xw = yh = 0; - // decode char - const char *const end = text + len; - while( text < end ) { - const utf8 *p = reinterpret_cast(text); - const utf32 iUnicode = utf8_decoder_t::decode(p); - text = reinterpret_cast(p); + const utf8 *p = reinterpret_cast(text); + while (const utf32 iUnicode = utf8_decoder_t::decode(p)) { if( iUnicode == '\n' ) { // new line: record max width xw = max( xw, width ); yh += LINESPACE; width = 0; - } - if( iUnicode == UNICODE_NUL ) { return; } width += fnt->get_glyph_advance(iUnicode); } - xw = max( xw, width ); yh += LINESPACE; } @@ -5185,8 +4556,8 @@ void display_ddd_proportional_clip(scr_coord_val xpos, scr_coord_val ypos, scr_c { const int halfheight = LINESPACE / 2 + 1; - PIXVAL lighter = display_blend_colors(ddd_color, color_idx_to_rgb(COL_WHITE), 25); - PIXVAL darker = display_blend_colors(ddd_color, color_idx_to_rgb(COL_BLACK), 25); + PIXVAL lighter = display_blend_colors_alpha32(ddd_color, color_idx_to_rgb(COL_WHITE), 8 /* 25% */); + PIXVAL darker = display_blend_colors_alpha32(ddd_color, color_idx_to_rgb(COL_BLACK), 8 /* 25% */); display_fillbox_wh_clip_rgb( xpos - 2, ypos - halfheight - 1 - hgt, width, halfheight * 2 + 2, ddd_color, dirty CLIP_NUM_PAR ); @@ -5942,8 +5313,6 @@ bool simgraph_init(scr_size window_size, sint16 full_screen) display_day_night_shift(0); memcpy(specialcolormap_all_day, specialcolormap_day_night, 256 * sizeof(PIXVAL)); memcpy(rgbmap_all_day, rgbmap_day_night, RGBMAPSIZE * sizeof(PIXVAL)); - memcpy(transparent_map_all_day, transparent_map_day_night, lengthof(transparent_map_day_night) * sizeof(PIXVAL)); - memcpy(transparent_map_all_day_rgb, transparent_map_day_night_rgb, lengthof(transparent_map_day_night_rgb) * sizeof(uint8)); // find out bit depth { @@ -5953,36 +5322,11 @@ bool simgraph_init(scr_size window_size, sint16 full_screen) } if(c==31) { // 15 bit per pixel - bitdepth = 15; - blend[0] = pix_blend25_15; - blend[1] = pix_blend50_15; - blend[2] = pix_blend75_15; - blend_recode[0] = pix_blend_recode25_15; - blend_recode[1] = pix_blend_recode50_15; - blend_recode[2] = pix_blend_recode75_15; - outline[0] = pix_outline25_15; - outline[1] = pix_outline50_15; - outline[2] = pix_outline75_15; - alpha = pix_alpha_15; - alpha_recode = pix_alpha_recode_15; - recode_img_src_target = recode_img_src_target_15; #ifndef RGB555 dr_fatal_notify( "Compiled for 16 bit color depth but using 15!" ); #endif } else { - blend[0] = pix_blend25_16; - blend[1] = pix_blend50_16; - blend[2] = pix_blend75_16; - blend_recode[0] = pix_blend_recode25_16; - blend_recode[1] = pix_blend_recode50_16; - blend_recode[2] = pix_blend_recode75_16; - outline[0] = pix_outline25_16; - outline[1] = pix_outline50_16; - outline[2] = pix_outline75_16; - alpha = pix_alpha_16; - alpha_recode = pix_alpha_recode_16; - recode_img_src_target = recode_img_src_target_16; #ifdef RGB555 dr_fatal_notify( "Compiled for 15 bit color depth but using 16!" ); #endif diff --git a/display/simimg.h b/display/simimg.h index 0204613ae4e..82b76f8c3c1 100644 --- a/display/simimg.h +++ b/display/simimg.h @@ -9,7 +9,6 @@ #include "../simtypes.h" - /* * Defines to handle images */ diff --git a/display/simview.cc b/display/simview.cc index 0f7bc49339b..06ccd93249a 100644 --- a/display/simview.cc +++ b/display/simview.cc @@ -21,7 +21,6 @@ #include "../boden/wasser.h" #include "../dataobj/environment.h" #include "../obj/zeiger.h" - #include "../utils/simrandom.h" uint16 win_get_statusbar_height(); // simwin.h @@ -446,7 +445,7 @@ void main_view_t::display_region( koord lt, koord wh, sint16 y_min, sint16 y_max if( env_t::draw_outside_tile ) { const sint16 yypos = ypos - tile_raster_scale_y( welt->min_height * TILE_HEIGHT_STEP, IMG_SIZE ); display_normal( ground_desc_t::outside->get_image(0), xpos, yypos, 0, true, false CLIP_NUM_PAR); - } + } } } } @@ -522,7 +521,7 @@ void main_view_t::display_region( koord lt, koord wh, sint16 y_min, sint16 y_max num_threads_paused--; } - if( shortest_distance( pos, cursor_pos ) < env_t::cursor_hide_range ) { + if( shortest_distance( pos, cursor_pos ) <= env_t::cursor_hide_range ) { // wait until all threads are paused threads_req_pause = true; while( num_threads_paused < env_t::num_threads - 1 ) { @@ -553,7 +552,7 @@ void main_view_t::display_region( koord lt, koord wh, sint16 y_min, sint16 y_max } else { #endif - if( shortest_distance( pos, cursor_pos ) < env_t::cursor_hide_range ) { + if( shortest_distance( pos, cursor_pos ) <= env_t::cursor_hide_range ) { const bool saved_hide_trees = env_t::hide_trees; const uint8 saved_hide_buildings = env_t::hide_buildings; env_t::hide_trees = true; diff --git a/display/simview.h b/display/simview.h index c425bcbe285..74af9dd1e49 100644 --- a/display/simview.h +++ b/display/simview.h @@ -68,6 +68,7 @@ class main_view_t void display_region( koord lt, koord wh, sint16 y_min, const sint16 y_max, bool force_dirty ); #endif +private: /** * Draws background in the specified rectangular screen coordinates. * @param xp X screen coordinate of the left-top corner. @@ -77,7 +78,6 @@ class main_view_t * @param dirty Mark the specified area as dirty. */ void display_background( scr_coord_val xp, scr_coord_val yp, scr_coord_val w, scr_coord_val h, bool dirty ); - }; #endif diff --git a/display/viewport.cc b/display/viewport.cc index fb4f5401a89..a434301efb1 100644 --- a/display/viewport.cc +++ b/display/viewport.cc @@ -107,7 +107,7 @@ void viewport_t::switch_underground_mode(const koord3d& pos) // position is underground (and not visible), change to sliced mode grund_t::set_underground_mode(grund_t::ugm_level, gr->get_hoehe()); } - else if (!gr->ist_karten_boden() || grund_t::underground_mode != grund_t::ugm_all) { + else if (!gr->ist_karten_boden() || grund_t::underground_mode != grund_t::ugm_all) { // position is overground, change to normal view // but not if we are in full underground view and gr is kartenboden grund_t::set_underground_mode(grund_t::ugm_none, 0); @@ -310,7 +310,7 @@ void viewport_t::metrics_updated() set_viewport_ij_offset(koord( - cached_disp_width/(2*cached_img_size) - cached_disp_height/cached_img_size, - cached_disp_width/(2*cached_img_size) - cached_disp_height/cached_img_size + cached_disp_width/(2*cached_img_size) - cached_disp_height/cached_img_size ) ); } diff --git a/display/viewport.h b/display/viewport.h index 4bad8aa2bef..d9bf4c55390 100644 --- a/display/viewport.h +++ b/display/viewport.h @@ -71,7 +71,7 @@ class viewport_t /** * This is the current offset for getting from tile to screen. - * @notes: Extra offset, if added to ij_off it will give us the 2D coordinate of the tile on the *BOTTOM-RIGHT* of screen. + * @note Extra offset, if added to ij_off it will give us the 2D coordinate of the tile on the *BOTTOM-RIGHT* of screen. */ koord view_ij_off; @@ -160,8 +160,8 @@ class viewport_t */ koord get_world_position() const { return ij_off; } - /** - * Gets current ij offsets of this viewport, depends of its proportions and the zoom level. + /** + * Offset from tile on center to tile in top-left corner of screen. */ koord get_viewport_ij_offset() const { return view_ij_off; } @@ -226,7 +226,6 @@ class viewport_t * @param intersect_grid Special case for the lower/raise tool, will return a limit border tile if we are on the south/east border of screen. * @param found_i out parameter, i-coordinate of the found tile. It's necessary because it might point to a grid position that doesn't map to a real in-map coordinate, on border tiles (south and east border). * @param found_j out parameter, j-coordinate of the found tile. It's necessary because it might point to a grid position that doesn't map to a real in-map coordinate, on border tiles (south and east border). - * @return the grund_t that's under the desired screen coordinate. NULL if we are outside map or we can't find it. */ grund_t* get_ground_on_screen_coordinate(scr_coord screen_pos, sint32 &found_i, sint32 &found_j, const bool intersect_grid=false) const; diff --git a/documentation/cherry-picked-commits.txt b/documentation/cherry-picked-commits.txt index 1946573ba07..3a8c8b6a10b 100644 --- a/documentation/cherry-picked-commits.txt +++ b/documentation/cherry-picked-commits.txt @@ -12,9 +12,148 @@ 8877 8922 +*9148 (only arra2d_tpl) -9404 ====Not exactly checked before here==== +9211 +9215 +9223 +|| +9231 +9235 +9236 +9238 +9242 +9252 +|| +9254 +9257 +9264 +|| +9266 +9268 +9269 +9271 +9273 +9275 +9276 +9287 +9290 +|| +9302 +9309 +9311 +9315 +|| +9317 +9320 +9322 +|| +9325 +9327 +9333 +|| +9335 +9337 +9339 +|| +9341 +9343 +9345 +|| +9348 +9351 +|| +9354 +9372 +9374 +9376 +|| +9378 +9384 +9386 +9391 +9393 +9394 +9398 +9400 +9402 +|| +9408 +9411 +|| +9414 +9416 +|| +9418 +9421 +9423 +|| +9426 +9432 +9433 +9435 +|| +9440 +9442 +9450 +9452 +|| +9460 +9464 +9469 +|| +9477 +9480 +9483 +9492 +9493 +9500 +9501 +9508 +9513 +9535 +9541 +9544 +|| +9546 +9549 +9551 +9552 +9554 +|| +9560 +9562 +9563 +9565 +|| +9567 +9571 +9573 +9574 +9576 +9577 +9582 +9584 +9585 +9587 +|| +9590 +9594 +|| +9600 +9603 +|| +9606 +9609 +9618 +9621 +9622 +9624 +9625 +9629 +|| +9633 9637 9638 9640 @@ -170,6 +309,8 @@ 10031 10032 10035 +10039 +10040 10044 10047 10048 @@ -248,6 +389,7 @@ 10231 || 10233 +10235 10238 10239 *10240 @@ -298,6 +440,9 @@ 10429 10430 10440 +10447(mostly) +|| +10449 10462 10463 10471 @@ -336,6 +481,7 @@ 10640 || 10642 +*10643 (only freelist_tpl/freelist_iter_tpl) 10645 10646 10650 @@ -352,6 +498,8 @@ 10686 10687 10710 +10712 +10717 10716 10719 10720 @@ -363,3 +511,29 @@ 10798 10801 10802 +10822 +10845 +|| +10853 +10858 +10860 +|| +10862 +10869 +10872 +|| +10878 +10880 +10882 +10883 +10885 +|| +10888 +10891 +10930 +10932 +10934 +|| +10937 +10946 +//11101 don't merge this, reverted later \ No newline at end of file diff --git a/gui/ai_selector.cc b/gui/ai_selector.cc index 68e6eb46d2c..a6343318352 100644 --- a/gui/ai_selector.cc +++ b/gui/ai_selector.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/baum_edit.cc b/gui/baum_edit.cc index fe193c53c44..69a914dfaa0 100644 --- a/gui/baum_edit.cc +++ b/gui/baum_edit.cc @@ -85,7 +85,7 @@ void baum_edit_frame_t::fill_list() tree_list.clear(); const bool is_sortedbyname = get_sortedby()==gui_sorting_item_t::BY_NAME_TRANSLATED; sortreverse = sort_order.pressed; - FOR(vector_tpl, const i, tree_builder_t::get_all_desc()) { + for(tree_desc_t const* const i : tree_builder_t::get_all_desc()) { if ( i && (i->get_allowed_climate_bits() & get_climate()) ) { tree_list.insert_ordered(i, is_sortedbyname ? compare_tree_desc_name : compare_tree_desc); } @@ -94,7 +94,7 @@ void baum_edit_frame_t::fill_list() // now build scrolled list scl.clear_elements(); scl.set_selection(-1); - FOR(vector_tpl, const i, tree_list) { + for(tree_desc_t const* const i : tree_list) { char const* const name = get_sortedby()==gui_sorting_item_t::BY_NAME_OBJECT ? i->get_name() : translator::translate(i->get_name()); scl.new_component(name, SYSCOL_TEXT); if (i == desc) { diff --git a/gui/city_info.cc b/gui/city_info.cc index 2c2e3ab2184..264cf59e072 100644 --- a/gui/city_info.cc +++ b/gui/city_info.cc @@ -380,7 +380,7 @@ city_info_t::~city_info_t() rename_city(); // save button state uint32 flags = 0; - FOR(const vector_tpl, b2c, button_to_chart.list()) { + for(gui_button_to_chart_t* b2c : button_to_chart.list()) { if (b2c->get_button()->pressed) { flags |= 1 << b2c->get_curve(); } diff --git a/gui/city_info.h b/gui/city_info.h index 355a4970b2f..0518d08e5ab 100644 --- a/gui/city_info.h +++ b/gui/city_info.h @@ -217,9 +217,9 @@ class city_info_t : public gui_frame_t, private action_listener_t void update_labels(); // stats table + gui_bandgraph_t transportation_last_year, transportation_this_year; gui_aligned_container_t cont_city_stats; gui_scrollpane_t scrolly_stats; - gui_bandgraph_t transportation_last_year, transportation_this_year; int transportation_pas[6]; // update trigger uint32 old_month = 0; diff --git a/gui/citybuilding_edit.cc b/gui/citybuilding_edit.cc index a5ce26c1134..974c71a7b98 100644 --- a/gui/citybuilding_edit.cc +++ b/gui/citybuilding_edit.cc @@ -187,19 +187,19 @@ void citybuilding_edit_frame_t::fill_list() building_list.clear(); if(bt_res.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_citybuilding_list(building_desc_t::city_res)) { + for(building_desc_t const* const desc : *hausbauer_t::get_citybuilding_list(building_desc_t::city_res)) { put_item_in_list(desc); } } if(bt_com.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_citybuilding_list(building_desc_t::city_com)) { + for(building_desc_t const* const desc : *hausbauer_t::get_citybuilding_list(building_desc_t::city_com)) { put_item_in_list(desc); } } if(bt_ind.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_citybuilding_list(building_desc_t::city_ind)) { + for(building_desc_t const* const desc : *hausbauer_t::get_citybuilding_list(building_desc_t::city_ind)) { put_item_in_list(desc); } } @@ -207,7 +207,7 @@ void citybuilding_edit_frame_t::fill_list() // now build scrolled list scl.clear_elements(); scl.set_selection(-1); - FOR(vector_tpl, const i, building_list) { + for(building_desc_t const* const i : building_list) { // color code for objects: BLACK: normal, YELLOW: consumer only, GREEN: source only PIXVAL color; switch (i->get_type()) { @@ -287,11 +287,12 @@ void citybuilding_edit_frame_t::change_item_info(sint32 entry) const uint16 allowed_region_bits = desc->get_allowed_region_bits(); if (allowed_region_bits < 65535) { uint32 region_idx = 0; - FORX(vector_tpl, region, welt->get_settings().regions, region_idx++) { + for(region_definition_t region : welt->get_settings().regions) { if (allowed_region_bits & (1 << region_idx)) { buf.printf(" - %s\n", translator::translate(region.name.c_str())); } + region_idx++; } } else { diff --git a/gui/citylist_frame_t.cc b/gui/citylist_frame_t.cc index a134a2f96b1..793047f59a2 100644 --- a/gui/citylist_frame_t.cc +++ b/gui/citylist_frame_t.cc @@ -8,6 +8,7 @@ #include "../dataobj/translator.h" +#include "../unicode.h" #include "../simcolor.h" #include "../dataobj/environment.h" #include "components/gui_button_to_chart.h" @@ -131,7 +132,7 @@ const uint8 citylist_frame_t::hist_type_type[karte_t::MAX_WORLD_COST] = gui_chart_t::PERCENT, gui_chart_t::STANDARD, gui_chart_t::PERCENT, - gui_chart_t::STANDARD, + gui_chart_t::TON_KM, gui_chart_t::PERCENT, gui_chart_t::TON_KM, gui_chart_t::PERCENT @@ -348,7 +349,7 @@ void citylist_frame_t::fill_list() { scrolly.clear_elements(); strcpy(last_name_filter, name_filter); - FOR(const weighted_vector_tpl, city, world()->get_cities()) { + for(stadt_t* city : world()->get_cities()) { if (citylist_stats_t::region_filter && (citylist_stats_t::region_filter-1) != world()->get_region(city->get_pos())) { continue; } diff --git a/gui/citylist_stats_t.cc b/gui/citylist_stats_t.cc index 7833836fb80..74ef31effdf 100644 --- a/gui/citylist_stats_t.cc +++ b/gui/citylist_stats_t.cc @@ -230,8 +230,8 @@ void gui_city_stats_t::update_table() } else { slist_tpl city_goods; - FOR(vector_tpl, city_fab, city->get_city_factories()) { - FOR(array_tpl, const& goods, mode == citylist_stats_t::goods_demand ? city_fab->get_input(): city_fab->get_output()) { + for(fabrik_t* city_fab : city->get_city_factories()) { + for(ware_production_t const& goods : mode == citylist_stats_t::goods_demand ? city_fab->get_input() : city_fab->get_output()) { city_goods.append_unique(goods.get_typ()); } } @@ -240,7 +240,7 @@ void gui_city_stats_t::update_table() add_table(city_goods.get_count()+1,1); { new_component(skinverwaltung_t::input_output ? skinverwaltung_t::input_output->get_image_id(mode == citylist_stats_t::goods_demand ? 0:1) : IMG_EMPTY, 0, ALIGN_CENTER_V, true); - FOR(slist_tpl, &goods, city_goods) { + for (const goods_desc_t* &goods : city_goods) { add_table(3,1); { new_component(goods->get_color())->set_size(GOODS_COLOR_BOX_SIZE); @@ -462,7 +462,7 @@ void citylist_stats_t::recalc_wold_max() #endif // DEBUG world_max_value = 0; - FOR(const weighted_vector_tpl, city, world()->get_cities()) { + for(stadt_t* const city : world()->get_cities()) { switch (display_mode) { case cl_population: diff --git a/gui/components/gui_action_creator.h b/gui/components/gui_action_creator.h index 8e7ea176501..611103089fc 100644 --- a/gui/components/gui_action_creator.h +++ b/gui/components/gui_action_creator.h @@ -28,7 +28,7 @@ class gui_action_creator_t */ void call_listeners(value_t v) { - FOR(slist_tpl, const l, listeners) { + for(action_listener_t* const l : listeners) { if (l->action_triggered(this, v)) break; } } diff --git a/gui/components/gui_aligned_container.cc b/gui/components/gui_aligned_container.cc index f5823fe9579..e152803e548 100644 --- a/gui/components/gui_aligned_container.cc +++ b/gui/components/gui_aligned_container.cc @@ -362,7 +362,7 @@ scr_size gui_aligned_container_t::get_size(const vector_tpl& col_ s.w += spacing.w * max(col_w.get_count()-1, 0); s.h += spacing.h * max(row_h.get_count()-1, 0); - FOR(vector_tpl, const w, col_w) { + for(scr_coord_val const w : col_w) { if (s.w > sinf - w) { s.w = sinf; break; @@ -372,7 +372,7 @@ scr_size gui_aligned_container_t::get_size(const vector_tpl& col_ } } - FOR(vector_tpl, const h, row_h) { + for(scr_coord_val const h : row_h) { if (s.h > sinf - h) { s.h = sinf; break; diff --git a/gui/components/gui_building.cc b/gui/components/gui_building.cc index b0c8636ad45..838e1a0a1b4 100644 --- a/gui/components/gui_building.cc +++ b/gui/components/gui_building.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ @@ -64,7 +64,7 @@ scr_size gui_building_t::get_min_size() const void gui_building_t::draw(scr_coord offset) { scr_coord pos = get_pos() + offset - tl; - FOR(vector_tpl, img, images) { + for(gui_image_t* img : images) { img->draw(pos); } } diff --git a/gui/components/gui_building.h b/gui/components/gui_building.h index 9aeed1344ac..b6c5a257d36 100644 --- a/gui/components/gui_building.h +++ b/gui/components/gui_building.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/components/gui_button_to_chart.cc b/gui/components/gui_button_to_chart.cc index d213b5e33eb..d607aab607e 100644 --- a/gui/components/gui_button_to_chart.cc +++ b/gui/components/gui_button_to_chart.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/components/gui_button_to_chart.h b/gui/components/gui_button_to_chart.h index 382b87423ba..d86aa588b11 100644 --- a/gui/components/gui_button_to_chart.h +++ b/gui/components/gui_button_to_chart.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/components/gui_chart.cc b/gui/components/gui_chart.cc index e672b0b9c43..bf820769e80 100644 --- a/gui/components/gui_chart.cc +++ b/gui/components/gui_chart.cc @@ -206,7 +206,7 @@ void gui_chart_t::draw(scr_coord offset) } // draw chart's curves - FOR(slist_tpl, const& c, curves) { + for(curve_t const& c : curves) { if (c.show) { double display_tmp; int start = abort_display_x ? (left_to_right_graph ? c.elements - abort_display_x : 0) : 0; @@ -319,7 +319,7 @@ void gui_chart_t::calc_gui_chart_values(sint64 *baseline, double *scale, char *c // bool convert_n_to_kn = false; // for force chart bool convert_time = false; // for comfort chart. dont use this with other units - FOR(slist_tpl, const& c, curves) { + for(curve_t const& c : curves) { if( c.show ) { for( int i=0; i, const c, components) { + for(gui_component_t* const c : components) { if (c == comp_focus) break; if (c->is_focusable()) { new_focus = c; @@ -100,7 +99,7 @@ bool gui_container_t::infowin_event(const event_t *ev) else { // or next input field bool valid = comp_focus==NULL; - FOR(vector_tpl, const c, components) { + for(gui_component_t* const c : components) { if (valid && c->is_focusable()) { new_focus = c; break; @@ -156,7 +155,7 @@ bool gui_container_t::infowin_event(const event_t *ev) if( !swallowed ) { vector_tplhandle_mouseover; - FOR( vector_tpl, const comp, components ) { + for(gui_component_t* const comp : components ) { if( list_dirty ) { break; @@ -187,7 +186,7 @@ bool gui_container_t::infowin_event(const event_t *ev) /* since the last drawn are overlaid over all others * the event-handling must go reverse too */ - FOR( vector_tpl, const comp, handle_mouseover ) { + for(gui_component_t* const comp : handle_mouseover ) { if (list_dirty) { break; @@ -296,7 +295,7 @@ void gui_container_t::draw(scr_coord offset) bool gui_container_t::is_focusable() { - FOR( vector_tpl, const c, components ) { + for(gui_component_t* const c : components ) { if( c->is_focusable() ) { return true; } diff --git a/gui/components/gui_convoiinfo.cc b/gui/components/gui_convoiinfo.cc index e88ede1b1f4..ceff2378232 100644 --- a/gui/components/gui_convoiinfo.cc +++ b/gui/components/gui_convoiinfo.cc @@ -92,8 +92,7 @@ gui_convoiinfo_t::gui_convoiinfo_t(convoihandle_t cnv, bool show_line_name): /** - * Events werden hiermit an die GUI-components - * gemeldet + * Events are notified to GUI components via this method */ bool gui_convoiinfo_t::infowin_event(const event_t *ev) { diff --git a/gui/components/gui_convoy_assembler.cc b/gui/components/gui_convoy_assembler.cc index 85bb992677e..021a05dee84 100644 --- a/gui/components/gui_convoy_assembler.cc +++ b/gui/components/gui_convoy_assembler.cc @@ -38,6 +38,7 @@ #include "../../player/simplay.h" #include "../../utils/cbuffer_t.h" +#include "../../unicode.h" #include "../../utils/for.h" #include "../../dataobj/settings.h" diff --git a/gui/components/gui_divider.cc b/gui/components/gui_divider.cc index b910f812edf..dafdc3536fc 100644 --- a/gui/components/gui_divider.cc +++ b/gui/components/gui_divider.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/components/gui_fixedwidth_textarea.cc b/gui/components/gui_fixedwidth_textarea.cc index 837a3f262cc..20d84bf3df1 100644 --- a/gui/components/gui_fixedwidth_textarea.cc +++ b/gui/components/gui_fixedwidth_textarea.cc @@ -9,7 +9,7 @@ #include "gui_fixedwidth_textarea.h" #include "../../dataobj/translator.h" #include "../../utils/cbuffer_t.h" - +#include "../../unicode.h" gui_fixedwidth_textarea_t::gui_fixedwidth_textarea_t(cbuffer_t* buf_, const sint16 width) : diff --git a/gui/components/gui_flowtext.cc b/gui/components/gui_flowtext.cc index b8bb538872d..1e716e96f88 100644 --- a/gui/components/gui_flowtext.cc +++ b/gui/components/gui_flowtext.cc @@ -9,6 +9,7 @@ #include "../../simevent.h" #include "../../display/simgraph.h" #include "../../utils/simstring.h" +#include "../../unicode.h" #include "../gui_theme.h" #include "gui_flowtext.h" @@ -375,7 +376,7 @@ scr_size gui_flowtext_intern_t::output(scr_coord offset, bool doit, bool return_ const int space_width = proportional_string_width(" "); required_width = 0; - FOR(slist_tpl, const& i, nodes) { + for(node_t const& i : nodes) { switch (i.att) { case ATT_NONE: case ATT_NO_SPACE: { @@ -527,7 +528,7 @@ bool gui_flowtext_intern_t::infowin_event(const event_t* ev) if (IS_LEFTCLICK(ev)) { // scan links for hit scr_coord evpos = scr_coord( ev->cx, ev->cy ); // - get_pos(); - FOR(slist_tpl, const& link, links) { + for(hyperlink_t const& link : links) { if( link.tl.y+LINESPACE == link.br.y ) { if( link.tl.x <= evpos.x && evpos.x < link.br.x && link.tl.y <= evpos.y && evpos.y < link.br.y ) { call_listeners((void const*)link.param.c_str()); diff --git a/gui/components/gui_image_list.cc b/gui/components/gui_image_list.cc index 7b9f5fa8066..0d54627b465 100644 --- a/gui/components/gui_image_list.cc +++ b/gui/components/gui_image_list.cc @@ -26,8 +26,7 @@ gui_image_list_t::gui_image_list_t(vector_tpl *images) : /** - * Events werden hiermit an die GUI-components - * gemeldet + * Events are notified to GUI components via this method */ bool gui_image_list_t::infowin_event(const event_t *ev) { @@ -84,7 +83,7 @@ void gui_image_list_t::draw(scr_coord parent_pos) display_blend_wh_rgb(xpos + grid.x*focus + 1, ypos + 1, grid.x - 2, grid.y - 2, SYSCOL_UPGRADEABLE, 25); } - FOR(vector_tpl, const& iptr, *images) { + for(image_data_t* const& iptr : *images) { image_data_t const& idata = *iptr; if(idata.count!=-32768) { diff --git a/gui/components/gui_label.cc b/gui/components/gui_label.cc index 64682becad1..e0c178c3515 100644 --- a/gui/components/gui_label.cc +++ b/gui/components/gui_label.cc @@ -186,6 +186,18 @@ void gui_label_buf_t::draw(scr_coord offset) } +void gui_label_buf_t::set_min_width(scr_coord_val w) +{ + min_width = w; +} +scr_size gui_label_buf_t::get_min_size() const +{ + scr_size min_size = gui_label_t::get_min_size(); + min_size.w = max(min_size.w, min_width); + return min_size; +} + + void gui_label_buf_t::append_money(double money) { buffer_write.append_money(money); diff --git a/gui/components/gui_label.h b/gui/components/gui_label.h index 1967599da49..b1c6f41291a 100644 --- a/gui/components/gui_label.h +++ b/gui/components/gui_label.h @@ -135,6 +135,7 @@ class gui_label_buf_t : public gui_label_t { bool buf_changed; cbuffer_t buffer_write, buffer_read; + scr_coord_val min_width = 0; public: gui_label_buf_t(PIXVAL color=SYSCOL_TEXT, align_t align=left) : gui_label_t(NULL, color, align), buf_changed(true) { } @@ -164,6 +165,10 @@ class gui_label_buf_t : public gui_label_t void draw(scr_coord offset) OVERRIDE; + void set_min_width(scr_coord_val w); + + scr_size get_min_size() const OVERRIDE; + protected: using gui_label_t::get_text_pointer; using gui_label_t::set_text; diff --git a/gui/components/gui_numberinput.cc b/gui/components/gui_numberinput.cc index 1f767ab23ef..8ae7416f258 100644 --- a/gui/components/gui_numberinput.cc +++ b/gui/components/gui_numberinput.cc @@ -107,10 +107,35 @@ void gui_numberinput_t::set_limits(sint32 _min, sint32 _max) { min_value = _min; max_value = _max; - char min_text[32], max_text[32]; - sprintf(min_text, "%d", _min); - sprintf(max_text, "%d", _max); - max_numbertext_width = max(proportional_string_width(min_text), proportional_string_width(max_text)); + // guess max width of numbers + const char* digits = "-0123456789"; + uint8 bytes, pixel; + // minus sign + if (min_value > 0) { + get_next_char_with_metrics(digits, bytes, pixel); + max_numbertext_width = pixel; + } + else { + digits++; + max_numbertext_width = 0; + } + // count digits + uint32 max_abs = max_value > 0 ? max_value : -max_value; + if (min_value < 0 && (uint32) (-min_value) > max_abs) { + max_abs = -min_value; + } + // width of digits + uint8 digit_width = 0; + while (*digits) { + get_next_char_with_metrics(digits, bytes, pixel); + if (pixel > digit_width) { + digit_width = pixel; + } + } + while (max_abs) { + max_numbertext_width += digit_width; + max_abs /= 10; + } } diff --git a/gui/components/gui_scrollpane.cc b/gui/components/gui_scrollpane.cc index 30d925a763f..e992fd7baba 100644 --- a/gui/components/gui_scrollpane.cc +++ b/gui/components/gui_scrollpane.cc @@ -138,7 +138,7 @@ scr_size gui_scrollpane_t::request_size(scr_size request) /** - * Events werden hiermit an die GUI-components gemeldet + * Events are notified to GUI components via this method gemeldet */ bool gui_scrollpane_t::infowin_event(const event_t *ev) { diff --git a/gui/components/gui_speedbar.cc b/gui/components/gui_speedbar.cc index 3f57a83901c..3bdd13f7739 100644 --- a/gui/components/gui_speedbar.cc +++ b/gui/components/gui_speedbar.cc @@ -255,7 +255,7 @@ void gui_bandgraph_t::draw(scr_coord offset) { offset = offset+pos+padding; total = 0; - FOR(slist_tpl, const& i, values) { + for(info_t const& i : values) { total += *i.value; } @@ -268,7 +268,7 @@ void gui_bandgraph_t::draw(scr_coord offset) offset.x += bar_width; sint32 temp = 0; scr_coord_val end = 0; - FOR(slist_tpl, const& i, values) { + for(info_t const& i : values) { if (*i.value>0) { temp += (*i.value); const scr_coord_val from = bar_width * temp / total + 0.5; diff --git a/gui/components/gui_tab_panel.cc b/gui/components/gui_tab_panel.cc index ee92de5c94a..67fbac2dcb8 100644 --- a/gui/components/gui_tab_panel.cc +++ b/gui/components/gui_tab_panel.cc @@ -50,7 +50,7 @@ void gui_tab_panel_t::set_size(scr_size size) required_size = scr_size( 8, required_size.h ); gui_component_t *last_component = NULL; - FOR(slist_tpl, & i, tabs) { + for(tab & i : tabs) { i.x_offset = required_size.w - 4; if( i.title ) { i.width = D_H_SPACE*2 + proportional_string_width( i.title ); @@ -85,7 +85,7 @@ scr_size gui_tab_panel_t::get_min_size() const scr_size t_size(0, required_size.h); scr_size c_size(0, 0); gui_component_t *last_component = NULL; - FOR(slist_tpl, const& iter, tabs) { + for(tab const& iter : tabs) { if (iter.title) { t_size.h = max(t_size.h, LINESPACE + D_V_SPACE); } @@ -130,7 +130,7 @@ bool gui_tab_panel_t::infowin_event(const event_t *ev) // tab selector was hit int text_x = (required_size.w>size.w ? D_ARROW_LEFT_WIDTH : 0) + D_H_SPACE; int k=0; - FORX(slist_tpl, const& i, tabs, ++k) { + for(tab const& i : tabs) { if( k >= offset_tab ) { if (text_x <= ev->mx && text_x + i.width > ev->mx) { // either tooltip or change @@ -140,6 +140,7 @@ bool gui_tab_panel_t::infowin_event(const event_t *ev) } text_x += i.width; } + k++; } return false; } @@ -197,7 +198,7 @@ void gui_tab_panel_t::draw(scr_coord parent_pos) int xx = required_size.w>get_size().w ? get_size().w-(D_ARROW_LEFT_WIDTH+2+D_ARROW_RIGHT_WIDTH) : get_size().w; int i=0; - FORX(slist_tpl, const& iter, tabs, ++i) { + for(tab const& iter : tabs) { if(i>=offset_tab) { // set clipping @@ -250,6 +251,7 @@ void gui_tab_panel_t::draw(scr_coord parent_pos) // reset clipping POP_CLIP(); } + i++; } display_fillbox_wh_clip_rgb(text_x, ypos+required_size.h-1, xpos+size.w-text_x, 1, SYSCOL_HIGHLIGHT, true); @@ -264,7 +266,7 @@ void gui_tab_panel_t::draw(scr_coord parent_pos) int mx = get_mouse_x()-parent_pos.x-pos.x; int text_x = D_H_SPACE; int i=0; - FORX(slist_tpl, const& iter, tabs, ++i) { + for(tab const& iter : tabs) { if( i>=offset_tab ) { if(text_x <= mx && text_x+iter.width > mx && (required_size.w<=get_size().w || mx < right.get_pos().x-12)) { // tooltip or change @@ -274,6 +276,7 @@ void gui_tab_panel_t::draw(scr_coord parent_pos) text_x += iter.width; } + i++; } } } diff --git a/gui/components/gui_textinput.cc b/gui/components/gui_textinput.cc index 865f51eade0..c198407ace3 100644 --- a/gui/components/gui_textinput.cc +++ b/gui/components/gui_textinput.cc @@ -9,6 +9,7 @@ #include "../simwin.h" #include "../../dataobj/translator.h" #include "../../utils/simstring.h" +#include "../../unicode.h" #include "../../sys/simsys.h" @@ -108,7 +109,7 @@ void gui_textinput_t::set_composition_status( char *c, int start, int length ) /** - * Events werden hiermit an die GUI-components gemeldet + * Events are notified to GUI components via this method gemeldet */ bool gui_textinput_t::infowin_event(const event_t *ev) { @@ -192,7 +193,7 @@ bool gui_textinput_t::infowin_event(const event_t *ev) } } else { - head_cursor_pos = get_prev_char(text, head_cursor_pos); + head_cursor_pos = utf8_get_prev_char(text, head_cursor_pos); } } // do not update tail cursor if SHIFT key is pressed -> enables text selection @@ -221,7 +222,7 @@ bool gui_textinput_t::infowin_event(const event_t *ev) } } else { - head_cursor_pos = get_next_char(text, head_cursor_pos); + head_cursor_pos = utf8_get_next_char(text, head_cursor_pos); } } // do not update tail cursor if SHIFT key is pressed -> enables text selection @@ -253,13 +254,13 @@ bool gui_textinput_t::infowin_event(const event_t *ev) if( !remove_selection() && head_cursor_pos>0 ) { if ( head_cursor_pos0; if( !remove_selection() && head_cursor_pos<=len ) { - size_t next_pos = get_next_char(text, head_cursor_pos); + size_t next_pos = utf8_get_next_char(text, head_cursor_pos); for( size_t pos=head_cursor_pos; pos, wi, all_ware) { + for(ware_item_t* wi : all_ware) { wi->pressed = wares->is_contained(wi->ware); if (wi->pressed) { active_ware.append(wi->ware); @@ -178,21 +178,21 @@ bool convoi_filter_frame_t::action_triggered( gui_action_creator_t *comp,value_t } } if(comp == &ware_alle) { - FOR( slist_tpl, wi, all_ware ) { + for(ware_item_t * wi : all_ware ) { wi->pressed = true; } sort_list(); return true; } if(comp == &ware_keine) { - FOR( slist_tpl, wi, all_ware ) { + for(ware_item_t * wi : all_ware ) { wi->pressed = false; } sort_list(); return true; } if( comp == &ware_invers ) { - FOR( slist_tpl, wi, all_ware ) { + for(ware_item_t * wi : all_ware ) { wi->pressed ^= true; } sort_list(); @@ -205,7 +205,7 @@ bool convoi_filter_frame_t::action_triggered( gui_action_creator_t *comp,value_t void convoi_filter_frame_t::sort_list() { active_ware.clear(); - FOR( slist_tpl, wi, all_ware ) { + for(ware_item_t * wi : all_ware ) { if( wi->pressed ) { /* uint8 catg = wi->ware->get_catg(); diff --git a/gui/convoi_frame.cc b/gui/convoi_frame.cc index f5878e180c8..75e56f502bb 100644 --- a/gui/convoi_frame.cc +++ b/gui/convoi_frame.cc @@ -228,7 +228,7 @@ void convoi_frame_t::fill_list() const bool all = owner->get_player_nr() == 1; scrolly->clear_elements(); - FOR(vector_tpl, const cnv, welt->convoys()) { + for(convoihandle_t const cnv : welt->convoys()) { if( all || cnv->get_owner()==owner ) { if( passes_filter( cnv ) ) { scrolly->new_component( cnv ); @@ -429,7 +429,7 @@ void convoi_frame_t::rdwr(loadsave_t *file) uint8 good_nr = get_filter(convoi_filter_frame_t::ware_filter) ? waren_filter->get_count() : 0; file->rdwr_byte(good_nr); if (good_nr > 0) { - FOR( slist_tpl, const i, *waren_filter ) { + for(const goods_desc_t * const i : *waren_filter ) { char *name = const_cast(i->get_name()); file->rdwr_str(name,256); } diff --git a/gui/convoi_frame.h b/gui/convoi_frame.h index d91bb020083..35425dc4a2d 100644 --- a/gui/convoi_frame.h +++ b/gui/convoi_frame.h @@ -108,7 +108,7 @@ class convoi_frame_t : virtual ~convoi_frame_t(); /** - * Events werden hiermit an die GUI-Komponenten gemeldet + * Events are notified to GUI components via this method gemeldet */ bool infowin_event(const event_t *ev) OVERRIDE; diff --git a/gui/curiosity_edit.cc b/gui/curiosity_edit.cc index 8ae7a8fb2bb..3af4eafdef2 100644 --- a/gui/curiosity_edit.cc +++ b/gui/curiosity_edit.cc @@ -174,19 +174,19 @@ void curiosity_edit_frame_t::fill_list() building_list.clear(); if(bt_city_attraction.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_list(building_desc_t::attraction_city)) { + for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::attraction_city)) { put_item_in_list(desc); } } if(bt_land_attraction.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_list(building_desc_t::attraction_land)) { + for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::attraction_land)) { put_item_in_list(desc); } } if(bt_monuments.pressed) { - FOR(vector_tpl, const desc, *hausbauer_t::get_list(building_desc_t::monument)) { + for(building_desc_t const* const desc : *hausbauer_t::get_list(building_desc_t::monument)) { put_item_in_list(desc); } } @@ -194,7 +194,7 @@ void curiosity_edit_frame_t::fill_list() // now build scrolled list scl.clear_elements(); scl.set_selection(-1); - FOR(vector_tpl, const i, building_list) { + for(building_desc_t const* const i : building_list) { // color code for objects: BLACK: normal, YELLOW: consumer only, GREEN: source only PIXVAL color; switch (i->get_type()) { diff --git a/gui/curiositylist_frame_t.cc b/gui/curiositylist_frame_t.cc index 3773ab8035e..0746e86c135 100644 --- a/gui/curiositylist_frame_t.cc +++ b/gui/curiositylist_frame_t.cc @@ -12,6 +12,7 @@ #include "../simworld.h" #include "../obj/gebaeude.h" #include "../descriptor/building_desc.h" +#include "../unicode.h" char curiositylist_frame_t::name_filter[256]; @@ -133,7 +134,7 @@ void curiositylist_frame_t::fill_list() const weighted_vector_tpl& world_attractions = welt->get_attractions(); attraction_count = world_attractions.get_count(); - FOR(const weighted_vector_tpl, const geb, world_attractions) { + for(gebaeude_t* const geb : world_attractions) { if (curiositylist_stats_t::region_filter && (curiositylist_stats_t::region_filter - 1) != welt->get_region(geb->get_pos().get_2d())) { continue; } diff --git a/gui/curiositylist_stats_t.cc b/gui/curiositylist_stats_t.cc index 3c9f4b94ee4..85a65696402 100644 --- a/gui/curiositylist_stats_t.cc +++ b/gui/curiositylist_stats_t.cc @@ -177,8 +177,7 @@ bool curiositylist_stats_t::is_valid() const /** - * Events werden hiermit an die GUI-components - * gemeldet + * Events are notified to GUI components via this method */ bool curiositylist_stats_t::infowin_event(const event_t * ev) { diff --git a/gui/depot_frame.cc b/gui/depot_frame.cc index 89c443f7c10..183713f347f 100644 --- a/gui/depot_frame.cc +++ b/gui/depot_frame.cc @@ -384,7 +384,7 @@ void depot_frame_t::update_data() convoy_selector.set_selection(0); // check all matching convoys - FOR(slist_tpl, const c, depot->get_convoy_list()) { + for(convoihandle_t const c : depot->get_convoy_list()) { convoy_selector.new_component(c) ; if( cnv.is_bound() && c == cnv ) { convoy_selector.set_selection( convoy_selector.count_elements() - 1 ); @@ -463,7 +463,7 @@ void depot_frame_t::build_line_list() vector_tpl lines; depot->get_owner()->simlinemgmt.get_lines(depot->get_line_type(), &lines, line_type_flags, true); std::sort(lines.begin(), lines.end(), compare_line); - FOR( vector_tpl, const line, lines ) { + for(linehandle_t const line : lines ) { line_selector.new_component(line) ; if( selected_line.is_bound() && selected_line == line ) { line_selector.set_selection( line_selector.count_elements() - 1 ); @@ -507,7 +507,7 @@ void depot_frame_t::reset_depot_name() sint64 depot_frame_t::calc_sale_value(const vehicle_desc_t *veh_type) { sint64 wert = 0; - FOR(slist_tpl, const v, depot->get_vehicle_list()) { + for(vehicle_t* const v : depot->get_vehicle_list()) { if( v->get_desc() == veh_type ) { wert += v->calc_sale_value(); } diff --git a/gui/depotlist_frame.cc b/gui/depotlist_frame.cc index 154ec2ffee2..1b84301f36a 100644 --- a/gui/depotlist_frame.cc +++ b/gui/depotlist_frame.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ @@ -352,7 +352,7 @@ void depotlist_frame_t::fill_list() { scrolly.clear_elements(); uint32 p_total = 0; - FOR(slist_tpl, const depot, depot_t::get_depot_list()) { + for(depot_t* const depot : depot_t::get_depot_list()) { if( depot->get_owner() == player ) { if( tabs.get_active_tab_index() == 0 || depot->get_waytype() == tabs.get_active_tab_waytype() ) { scrolly.new_component(depot); diff --git a/gui/factory_chart.cc b/gui/factory_chart.cc index 1e32bb68730..9a53973a612 100644 --- a/gui/factory_chart.cc +++ b/gui/factory_chart.cc @@ -92,7 +92,7 @@ static const uint8 ref_color[MAX_FAB_REF_LINE] = static const char *const label_text[MAX_PROD_LABEL+1] = { - "(MW)", "Boost (%)", "Max Boost (%)", "Demand", "Arrived", "sended", + "(MW)", "Boost (%%)", "Max Boost (%%)", "Demand", "Arrived", "sended", "(KW)" // put this at the end }; diff --git a/gui/factorylist_frame_t.cc b/gui/factorylist_frame_t.cc index 0d3755c5149..9d170722816 100644 --- a/gui/factorylist_frame_t.cc +++ b/gui/factorylist_frame_t.cc @@ -9,6 +9,7 @@ #include "../player/simplay.h" #include "../simworld.h" #include "../dataobj/environment.h" +#include "../unicode.h" char factorylist_frame_t::name_filter[256]; @@ -297,7 +298,7 @@ void factorylist_frame_t::fill_list() old_factories_count = world()->get_fab_list().get_count(); // to avoid too many redraws ... scrolly.clear_elements(); uint32 count = 0; - FOR(const vector_tpl,fab,world()->get_fab_list()) { + for(fabrik_t* fab : world()->get_fab_list()) { if (factorylist_stats_t::region_filter && (factorylist_stats_t::region_filter-1) != world()->get_region(fab->get_pos().get_2d())) { continue; } diff --git a/gui/goods_stats_t.cc b/gui/goods_stats_t.cc index 6099085b8b8..29e6d386aac 100644 --- a/gui/goods_stats_t.cc +++ b/gui/goods_stats_t.cc @@ -39,7 +39,7 @@ void goods_stats_t::update_goodslist(vector_tplgoods, uint3 remove_all(); set_table_layout(display_mode>0 ? 5:7, 0); - FOR(vector_tpl, wtyp, goods) { + for(const goods_desc_t* wtyp : goods) { new_component(wtyp->get_color())->set_size(GOODS_COLOR_BOX_SIZE); gui_label_buf_t *lb = new_component(SYSCOL_TEXT, gui_label_t::left); diff --git a/gui/groundobj_edit.cc b/gui/groundobj_edit.cc index 52d5120b657..253386853ef 100644 --- a/gui/groundobj_edit.cc +++ b/gui/groundobj_edit.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ @@ -89,7 +89,7 @@ void groundobj_edit_frame_t::fill_list() groundobj_list.clear(); const uint8 sortedby = get_sortedby(); sortreverse = sort_order.pressed; - FOR(vector_tpl, const i, groundobj_t::get_all_desc()) { + for(groundobj_desc_t const* const i : groundobj_t::get_all_desc()) { if ( i && (i->get_allowed_climate_bits() & get_climate()) ) { switch(sortedby) { case gui_sorting_item_t::BY_NAME_TRANSLATED: @@ -107,7 +107,7 @@ void groundobj_edit_frame_t::fill_list() // now build scrolled list scl.clear_elements(); scl.set_selection(-1); - FOR(vector_tpl, const i, groundobj_list) { + for(groundobj_desc_t const* const i : groundobj_list) { char const* const name = sortedby==gui_sorting_item_t::BY_NAME_OBJECT ? i->get_name() : translator::translate(i->get_name()); scl.new_component(name, SYSCOL_TEXT); if (i == desc) { diff --git a/gui/groundobj_edit.h b/gui/groundobj_edit.h index 2ec414f576c..797abe388a1 100644 --- a/gui/groundobj_edit.h +++ b/gui/groundobj_edit.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/gui_frame.cc b/gui/gui_frame.cc index 55ac3fa7a51..5d9e2404251 100644 --- a/gui/gui_frame.cc +++ b/gui/gui_frame.cc @@ -101,8 +101,7 @@ FLAGGED_PIXVAL gui_frame_t::get_titlecolor() const /** - * Events werden hiermit an die GUI-components - * gemeldet + * Events are notified to GUI components via this method */ bool gui_frame_t::infowin_event(const event_t *ev) { @@ -181,7 +180,7 @@ void gui_frame_t::draw(scr_coord pos, scr_size size) } dirty = false; - PUSH_CLIP_FIT(pos.x+1, pos.y+titlebar_size.h, size.w-2, size.h-titlebar_size.h); + PUSH_CLIP_FIT(pos.x+1, pos.y+titlebar_size.h+1, size.w-2, size.h-titlebar_size.h-2); gui_aligned_container_t::draw(pos); POP_CLIP(); diff --git a/gui/halt_info.cc b/gui/halt_info.cc index 3dc77fde486..cb41f7c0fa1 100644 --- a/gui/halt_info.cc +++ b/gui/halt_info.cc @@ -27,6 +27,7 @@ #include "../vehicle/vehicle.h" #include "../utils/simstring.h" +#include "../unicode.h" #include "../descriptor/skin_desc.h" @@ -1216,6 +1217,7 @@ void halt_info_t::update_components() // detail button: stops that don't handle any goods don't have a detail dialog detail_button.set_visible(!halt->is_not_enabled()); + detail_button.pressed = win_get_magic(magic_halt_detail + halt.get_id()); // chart buttons activate_chart_buttons(); diff --git a/gui/halt_list_stats.cc b/gui/halt_list_stats.cc index be087173d92..04b96fae1ea 100644 --- a/gui/halt_list_stats.cc +++ b/gui/halt_list_stats.cc @@ -463,8 +463,7 @@ void gui_halt_stats_t::draw(scr_coord offset) // main class static karte_ptr_t welt; /** - * Events werden hiermit an die GUI-components - * gemeldet + * Events are notified to GUI components via this method */ bool halt_list_stats_t::infowin_event(const event_t *ev) { diff --git a/gui/headquarter_info.cc b/gui/headquarter_info.cc index fc839cbf071..eac759689f9 100644 --- a/gui/headquarter_info.cc +++ b/gui/headquarter_info.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/help_frame.cc b/gui/help_frame.cc index d4c6411899e..997be3de68c 100644 --- a/gui/help_frame.cc +++ b/gui/help_frame.cc @@ -83,7 +83,7 @@ help_frame_t::help_frame_t(char const* const filename) : // enumerate toolbars bool special = false; add_helpfile( toolbars, NULL, "mainmenu.txt", false, 0 ); - FOR( vector_tpl, iter, tool_t::toolbar_tool ) { + for(toolbar_t * iter : tool_t::toolbar_tool ) { if( strstart(iter->get_tool_selector()->get_help_filename(),"list.txt" ) ) { continue; } @@ -329,7 +329,7 @@ void help_frame_t::set_helpfile(const char *filename, bool resize_frame ) buf.append( translator::translate( "Keyboard Help\n

Keyboard Help

\n" ) ); player_t *player = welt->get_active_player(); const char *trad_str = translator::translate( "%s - %s
\n" ); - FOR(vector_tpl, const i, tool_t::char_to_tool) { + for(tool_t* const i : tool_t::char_to_tool) { cbuffer_t c; char str[16]; if( i->command_flags & SIM_MOD_CTRL ) { diff --git a/gui/labellist_frame_t.cc b/gui/labellist_frame_t.cc index e37be6563e9..30e6044d37c 100644 --- a/gui/labellist_frame_t.cc +++ b/gui/labellist_frame_t.cc @@ -9,6 +9,7 @@ #include "../dataobj/translator.h" #include "../obj/label.h" #include "../simworld.h" +#include "../unicode.h" #include "components/gui_image.h" char labellist_frame_t::name_filter[256]; @@ -116,7 +117,7 @@ void labellist_frame_t::fill_list() label_count = welt->get_label_list().get_count(); scrolly.clear_elements(); - FOR(slist_tpl, const& pos, welt->get_label_list()) { + for(koord const& pos : welt->get_label_list()) { if (labellist_stats_t::region_filter && (labellist_stats_t::region_filter - 1) != welt->get_region(pos)) { continue; } @@ -138,7 +139,7 @@ void labellist_frame_t::fill_list() uint32 labellist_frame_t::count_label() { uint32 labelcount = 0; - FOR(slist_tpl, const& pos, welt->get_label_list()) { + for(koord const& pos : welt->get_label_list()) { label_t* label = welt->lookup_kartenboden(pos)->find(); const char* name = welt->lookup_kartenboden(pos)->get_text(); // some old version games don't have label nor name. diff --git a/gui/labellist_stats_t.cc b/gui/labellist_stats_t.cc index 8ab1034b4b1..6eeab92dbb1 100644 --- a/gui/labellist_stats_t.cc +++ b/gui/labellist_stats_t.cc @@ -10,7 +10,6 @@ #include "../simcity.h" #include "../player/simplay.h" #include "../obj/label.h" -#include "../utils/simstring.h" #include "../utils/cbuffer_t.h" #include "../dataobj/environment.h" diff --git a/gui/loadfont_frame.cc b/gui/loadfont_frame.cc index c52dd5aa4ef..c2bf7a9afda 100644 --- a/gui/loadfont_frame.cc +++ b/gui/loadfont_frame.cc @@ -208,7 +208,7 @@ void loadfont_frame_t::fill_list() savegame_frame_t::fill_list(); // mark current fonts - FOR(slist_tpl, const& i, entries) { + for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; } @@ -226,6 +226,7 @@ void loadfont_frame_t::fill_list() strcat( name, " "); strcat( name, face->style_name ); i.button->set_text(name); + i.button->pressed = strstr( env_t::fontname.c_str(), i.info ); FT_Done_Face( face ); } } @@ -244,7 +245,7 @@ void loadfont_frame_t::fill_list() void loadfont_frame_t::draw(scr_coord pos, scr_size size) { // mark current fonts - FOR(slist_tpl, const& i, entries) { + for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; } diff --git a/gui/loadfont_frame.h b/gui/loadfont_frame.h index 28f8ff983a9..2e99352913a 100644 --- a/gui/loadfont_frame.h +++ b/gui/loadfont_frame.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/loadsoundfont_frame.cc b/gui/loadsoundfont_frame.cc index 32f8671ce65..3f7e325be3f 100644 --- a/gui/loadsoundfont_frame.cc +++ b/gui/loadsoundfont_frame.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/loadsoundfont_frame.h b/gui/loadsoundfont_frame.h index 893a6d1aae7..01636e6f0b6 100644 --- a/gui/loadsoundfont_frame.h +++ b/gui/loadsoundfont_frame.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/gui/message_frame_t.cc b/gui/message_frame_t.cc index 74fdca19c08..1118a594f66 100644 --- a/gui/message_frame_t.cc +++ b/gui/message_frame_t.cc @@ -130,7 +130,7 @@ void message_frame_t::fill_list() { uint32 id = 0; scrolly.clear_elements(); - FOR( slist_tpl, const i, welt->get_message()->get_list() ) { + for(message_t::node* const i : welt->get_message()->get_list() ) { scrolly.new_component(i, id++); } @@ -184,7 +184,7 @@ bool message_frame_t::action_triggered( gui_action_creator_t *comp, value_t v ) cbuffer_t clipboard; const sint32 message_type = tab_categories[ tabs.get_active_tab_index() ]; int count = 20; // just copy the last 20 - FOR( slist_tpl, const i, welt->get_message()->get_list() ) { + for(message_t::node* const i : welt->get_message()->get_list() ) { if( i->get_type_shifted() & message_type ) { // add them to clipboard char msg_no_break[ 258 ]; diff --git a/gui/minimap.cc b/gui/minimap.cc index 4d43419c408..2641eb70363 100644 --- a/gui/minimap.cc +++ b/gui/minimap.cc @@ -222,7 +222,7 @@ void minimap_t::add_to_schedule_cache( convoihandle_t cnv, bool with_waypoints ) bool last_diagonal = false; const bool add_schedule = schedule->get_waytype() != air_wt; - FOR( minivec_tpl, cur, schedule->entries ) { + for(schedule_entry_t cur : schedule->entries ) { //cycle on stops //try to read station's coordinates if there's a station at this schedule stop @@ -921,12 +921,12 @@ void minimap_t::calc_map_pixel(const koord k) break; } - cargo = w->get_statistics(WAY_STAT_GOODS); + cargo = w->get_statistics(WAY_STAT_LAST_MONTH,WAY_STAT_GOODS); // maximum two ways for one ground if( transport_type_showed_on_map==simline_t::line ) { const weg_t *w=gr->get_weg_nr(1); if(w) { - cargo += w->get_statistics(WAY_STAT_GOODS); + cargo += w->get_statistics(WAY_STAT_LAST_MONTH,WAY_STAT_GOODS); } } if( cargo > max_cargo ) { @@ -960,11 +960,11 @@ void minimap_t::calc_map_pixel(const koord k) break; } - passed = w->get_statistics(WAY_STAT_CONVOIS); + passed = w->get_statistics(WAY_STAT_LAST_MONTH,WAY_STAT_CONVOIS); // maximum two ways for one ground if( transport_type_showed_on_map==simline_t::line ){ if( weg_t *w = gr->get_weg_nr(1) ) { - passed += w->get_statistics(WAY_STAT_CONVOIS); + passed += w->get_statistics(WAY_STAT_LAST_MONTH,WAY_STAT_CONVOIS); } } if( passed > max_passed ) { @@ -1358,14 +1358,14 @@ void minimap_t::calc_map() const weighted_vector_tpl &world_attractions = world->get_attractions(); // find the current maximum max_tourist_ziele = 1; - FOR(weighted_vector_tpl, const i, world_attractions) { + for(gebaeude_t* const i : world_attractions) { int const pax = i->get_adjusted_visitor_demand(); if (max_tourist_ziele < pax) { max_tourist_ziele = pax; } } // draw them - FOR(weighted_vector_tpl, const g, world_attractions) { + for(gebaeude_t* const g : world_attractions) { koord pos = g->get_pos().get_2d(); set_map_color( pos, calc_severity_color(g->get_adjusted_visitor_demand(), max_tourist_ziele)); } @@ -1375,7 +1375,7 @@ void minimap_t::calc_map() // since we do iterate the factory info list, this must be done here if(mode==MAP_FACTORIES) { - FOR(vector_tpl, const f, world->get_fab_list()) { + for(fabrik_t* const f : world->get_fab_list() ){ koord const pos = f->get_pos().get_2d(); set_map_color( pos, color_idx_to_rgb(COL_BLACK) ); set_map_color(pos, f->get_color()); @@ -1384,7 +1384,7 @@ void minimap_t::calc_map() } if(mode==MAP_DEPOT) { - FOR(slist_tpl, const d, depot_t::get_depot_list()) { + for(depot_t* const d : depot_t::get_depot_list() ) { if (d->get_owner() == world->get_active_player()) { koord const pos = d->get_pos().get_2d(); // offset of one to avoid @@ -1707,7 +1707,7 @@ void minimap_t::draw(scr_coord pos) if (player_showed_on_map != -1) { required_vehicle_owner = world->get_player(player_showed_on_map); } - FOR( vector_tpl, cnv, world->convoys() ) { + for(convoihandle_t cnv : world->convoys() ) { if( !cnv.is_bound() || cnv->get_line().is_bound() ) { // not there or already part of a line continue; @@ -1788,7 +1788,7 @@ void minimap_t::draw(scr_coord pos) scr_coord k1,k2; bool already_show_lettercode=false; // DISPLAY STATIONS AND AIRPORTS: moved here so station spots are not overwritten by lines drawn - FOR( vector_tpl, seg, schedule_cache ) { + for(line_segment_t seg : schedule_cache ) { PIXVAL colval = color_idx_to_rgb(seg.colorcount); if( event_get_last_control_shift()==2 || current_cnv.is_bound() ) { // on control / single convoi use only player colors @@ -1899,7 +1899,7 @@ void minimap_t::draw(scr_coord pos) } // now draw stop cache // if needed to get new values - FOR( vector_tpl, station, stop_cache ) { + for(halthandle_t station : stop_cache ) { if( !station.is_bound() ) { // maybe deleted in the meanwhile @@ -2104,7 +2104,7 @@ void minimap_t::draw(scr_coord pos) const weighted_vector_tpl& staedte = world->get_cities(); const PIXVAL col = color_idx_to_rgb(showing_schedule ? COL_BLACK : COL_WHITE); - FOR( weighted_vector_tpl, const city, staedte ) { + for(stadt_t* const city : staedte ) { const char * name = city->get_name(); scr_coord p = map_to_screen_coord( city->get_pos() ); @@ -2117,7 +2117,7 @@ void minimap_t::draw(scr_coord pos) if( mode & MAP_CITYLIMIT ) { // for all cities - FOR( weighted_vector_tpl, const city, world->get_cities() ) { + for(stadt_t* const city : world->get_cities() ) { koord k[4]; k[0] = city->get_linksoben(); // top left k[2] = city->get_rechtsunten(); // bottom right @@ -2154,7 +2154,7 @@ void minimap_t::draw(scr_coord pos) // since we do iterate the tourist info list, this must be done here // find tourist spots if( mode & MAP_TOURIST ) { - FOR( weighted_vector_tpl, const gb, world->get_attractions() ) { + for(gebaeude_t* const gb : world->get_attractions() ) { if( gb->get_first_tile() == gb ) { scr_coord gb_pos = map_to_screen_coord( gb->get_pos().get_2d() ); gb_pos = gb_pos + pos; @@ -2172,7 +2172,7 @@ void minimap_t::draw(scr_coord pos) } if( mode & MAP_FACTORIES ) { - FOR( vector_tpl, const f, world->get_fab_list() ) { + for(fabrik_t* const f : world->get_fab_list() ) { // filter check if (freight_type_group_index_showed_on_map == goods_manager_t::passengers) { if (!f->get_building()->get_adjusted_visitor_demand() && !f->get_building()->get_adjusted_jobs()) { @@ -2207,7 +2207,7 @@ void minimap_t::draw(scr_coord pos) } if( mode & MAP_DEPOT ) { - FOR( slist_tpl, const d, depot_t::get_depot_list() ) { + for(depot_t* const d : depot_t::get_depot_list() ) { if( d->get_owner() == world->get_active_player() ) { scr_coord depot_pos = map_to_screen_coord( d->get_pos().get_2d() ); depot_pos = depot_pos + pos; diff --git a/gui/money_frame.cc b/gui/money_frame.cc index 8af4083a229..8ec645e534f 100644 --- a/gui/money_frame.cc +++ b/gui/money_frame.cc @@ -75,7 +75,7 @@ static const char *cost_type_name[MAX_PLAYER_COST_BUTTON] = "Net Wealth", "Credit Limit", "Solvency Limit", - "Margin (%)", + "Margin (%%)", "Mail-km", "Freight-km" }; @@ -741,14 +741,14 @@ money_frame_t::~money_frame_t() { bFilterStates.clear(); // save button states - FOR(vector_tpl, b2c, button_to_chart.list()) { + for(gui_button_to_chart_t* b2c : button_to_chart.list()) { bFilterStates.append( b2c->get_button()->pressed ? 1 : 0); } } void money_frame_t::update_labels() { - FOR(vector_tpl, lb, money_labels) { + for(money_frame_label_t* lb : money_labels) { lb->update(this); } @@ -896,7 +896,7 @@ void money_frame_t::update_stats() uint64 total_signalbox_maintenance = 0; // - stations - FOR(vector_tpl, const halt, haltestelle_t::get_alle_haltestellen()) { + for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if (halt->get_owner() == player) { for (uint8 i = 0; i < TT_MAX_VEH-1; i++) { if (finance_t::translate_tt_to_waytype((transport_type)(i+1)) ==road_wt) { @@ -913,7 +913,7 @@ void money_frame_t::update_stats() } } // - depot & vehicle - FOR(slist_tpl, const depot, depot_t::get_depot_list()) { + for(depot_t* const depot : depot_t::get_depot_list()) { if (depot->get_owner_nr() == player->get_player_nr()) { const uint8 tt_idx = finance_t::translate_waytype_to_tt(depot->get_waytype())-1; tt_depot_counts[tt_idx]++; @@ -923,14 +923,14 @@ void money_frame_t::update_stats() // all vehicles stored in depot not part of a convoi tt_vehicle_counts[tt_idx] += depot->get_vehicle_list().get_count(); total_own_vehicles += depot->get_vehicle_list().get_count(); - FOR(slist_tpl, vehicle, depot->get_vehicle_list()) { + for(vehicle_t* vehicle : depot->get_vehicle_list()) { total_vehicle_maintenance += vehicle->get_fixed_cost(welt); tt_vehicle_maint[tt_idx] += vehicle->get_fixed_cost(welt); } } } // - convoys - FOR(vector_tpl, const cnv, welt->convoys()) { + for(convoihandle_t const cnv : welt->convoys()) { if (cnv->get_owner() == player) { const uint8 tt_idx = finance_t::translate_waytype_to_tt(cnv->front()->get_desc()->get_waytype())-1; tt_convoy_counts[tt_idx]++; @@ -945,14 +945,14 @@ void money_frame_t::update_stats() } } // - signalboxes - FOR(slist_tpl, const sigb, signalbox_t::all_signalboxes) { + for(signalbox_t* const sigb : signalbox_t::all_signalboxes) { if (sigb->get_owner() == player && sigb->get_first_tile() == sigb) { total_signalbox_maintenance += (uint32)welt->lookup(sigb->get_pos())->get_building()->get_tile()->get_desc()->get_maintenance(); total_own_signalboxes++; } } // - way & electrification - FOR(vector_tpl, const way, weg_t::get_alle_wege()) { + for(auto & way : weg_t::get_alle_wege()) { const uint8 tt_idx = finance_t::translate_waytype_to_tt(way->get_desc()->get_finance_waytype())-1; if (tt_idx >= TT_MAX_VEH-1) { continue; diff --git a/gui/pakselector.cc b/gui/pakselector.cc index 5ff46f99667..c9687e4e57f 100644 --- a/gui/pakselector.cc +++ b/gui/pakselector.cc @@ -92,7 +92,7 @@ void pakselector_t::fill_list() entries.sort(dir_entry_t::compare); - FOR(slist_tpl, &i, entries) { + for(dir_entry_t &i : entries) { if (i.type == LI_HEADER) { continue; diff --git a/gui/savegame_frame.cc b/gui/savegame_frame.cc index efb7ae7683a..700fe18f8bb 100644 --- a/gui/savegame_frame.cc +++ b/gui/savegame_frame.cc @@ -127,7 +127,7 @@ savegame_frame_t::savegame_frame_t(const char *suffix, bool only_directories, co */ savegame_frame_t::~savegame_frame_t() { - FOR(slist_tpl, const& i, entries) { + for(dir_entry_t const& i : entries) { if(i.button) { delete [] const_cast(i.button->get_text()); delete i.button; @@ -237,7 +237,7 @@ void savegame_frame_t::fill_list( void ) } // for each path, we search. - FOR(vector_tpl, &path, paths){ + for(std::string &path : paths){ const char *path_c = path.c_str(); const size_t path_c_len = strlen(path_c); @@ -247,7 +247,7 @@ void savegame_frame_t::fill_list( void ) bool section_added = false; // Add the entries that pass the check - FOR(searchfolder_t, const &name, sf) { + for(const char* const &name : sf) { fullname = new char [path_c_len+strlen(name)+1]; sprintf(fullname,"%s%s",path_c,name); @@ -285,7 +285,9 @@ void savegame_frame_t::list_filled( void ) button_frame.set_table_layout(1,0); button_frame.add_table(cols,0)->set_spacing(scr_size(D_H_SPACE,D_FILELIST_V_SPACE)); // change space between entries to zero to see more on screen - FOR(slist_tpl, const& i, entries) { + button_t *pressed_button = NULL; + + for(dir_entry_t const& i : entries) { button_t* const delete_button = i.del; button_t* const action_button = i.button; gui_label_t* const label = i.label; @@ -316,6 +318,10 @@ void savegame_frame_t::list_filled( void ) if (label_enabled) { button_frame.add_component(label); } + + if (pressed_button == NULL && action_button->pressed) { + pressed_button = action_button; + } } } @@ -328,6 +334,10 @@ void savegame_frame_t::list_filled( void ) // TODO do something smarter here size.w = max(size.w, button_frame.get_min_size().w + D_SCROLLBAR_WIDTH); set_windowsize(size); + + if (pressed_button) { + scrolly.set_scroll_position(0, max(0, pressed_button->get_pos().y - 2 * D_BUTTON_HEIGHT) ); + } } @@ -511,7 +521,7 @@ bool savegame_frame_t::action_triggered(gui_action_creator_t *component, value_t else { // File in list selected //-------------------------- - FOR(slist_tpl, const& i, entries) { + for(dir_entry_t const& i : entries) { if(in_action){ break; } diff --git a/gui/settings_stats.cc b/gui/settings_stats.cc index ea72b83049c..9c349f652ce 100644 --- a/gui/settings_stats.cc +++ b/gui/settings_stats.cc @@ -1036,7 +1036,7 @@ void settings_climates_stats_t::init(settings_t* const sets) INIT_NUM_NEW( "minimum length of rivers", sets->get_min_river_length(), 0, max(16,sets->get_max_river_length())-16, gui_numberinput_t::AUTOLINEAR, false ); INIT_NUM_NEW( "maximum length of rivers", sets->get_max_river_length(), sets->get_min_river_length()+16, 8196, gui_numberinput_t::AUTOLINEAR, false ); // add listener to all of them - FOR(slist_tpl, const n, numinp) { + for(gui_numberinput_t* const n : numinp) { n->add_listener(this); } // the following are independent and thus need no listener @@ -1099,11 +1099,12 @@ bool settings_climates_stats_t::action_triggered(gui_action_creator_t *comp, val welt_gui_t *welt_gui = dynamic_cast(win_get_magic( magic_welt_gui_t )); read( local_sets ); uint i = 0; - FORX(slist_tpl, const n, numinp, ++i) { + for(gui_numberinput_t* const n : numinp) { if (n == comp && i < 3 && welt_gui) { // update world preview welt_gui->update_preview(); } + i++; } return true; } diff --git a/gui/signal_connector_gui.cc b/gui/signal_connector_gui.cc index 8cf8ab92ad2..4e659e36419 100644 --- a/gui/signal_connector_gui.cc +++ b/gui/signal_connector_gui.cc @@ -99,7 +99,7 @@ void signal_connector_gui_t::build_list(const signal_t* sig) { sb_selections.clear(); const player_t *player = sig->get_owner(); - FOR(slist_tpl, const sigb, signalbox_t::all_signalboxes) { + for(signalbox_t* const sigb : signalbox_t::all_signalboxes) { if(sigb->get_owner() == player && sigb->get_first_tile() == sigb && sigb->can_add_signal(sig) ) { gui_signalbox_changer_t *sel = new gui_signalbox_changer_t(sigb, sig); sb_selections.append(sel); @@ -130,7 +130,7 @@ void signal_connector_gui_t::update(const signal_t* sig) new_component("keine"); } else { - FOR(slist_tpl, &selection, sb_selections) { + for(gui_signalbox_changer_t* &selection : sb_selections) { add_component(selection); } } diff --git a/gui/signal_info.cc b/gui/signal_info.cc index df13429fb4a..2c4c5e5ce4b 100644 --- a/gui/signal_info.cc +++ b/gui/signal_info.cc @@ -102,7 +102,7 @@ void signal_info_t::update_data() for (int l = 0; l < max_lines; l++) { char temp_name[1024] = { '\0' }; - next_char_index = display_fit_proportional(sb_name, max_width, 0); + next_char_index = display_fit_proportional(sb_name, max_width); if (sb_name[next_char_index] == '\0') { lb_sb_name.buf().append(sb_name); diff --git a/gui/signalboxlist_frame.cc b/gui/signalboxlist_frame.cc index 7778fb228ba..080bafed581 100644 --- a/gui/signalboxlist_frame.cc +++ b/gui/signalboxlist_frame.cc @@ -256,7 +256,7 @@ bool signalboxlist_frame_t::action_triggered( gui_action_creator_t *comp,value_t void signalboxlist_frame_t::fill_list() { scrolly.clear_elements(); - FOR(slist_tpl, const sigb, signalbox_t::all_signalboxes) { + for(signalbox_t* const sigb : signalbox_t::all_signalboxes) { if (filter_has_vacant_slot && !sigb->can_add_more_signals()) { continue; } diff --git a/gui/simwin.cc b/gui/simwin.cc index fda97a7932e..3186e140442 100644 --- a/gui/simwin.cc +++ b/gui/simwin.cc @@ -477,8 +477,7 @@ void rdwr_win_settings(loadsave_t *file) } while (magic != magic_none); } else { - typedef inthashtable_tpl stupid_table_t; - FOR(stupid_table_t, it, saved_windowsizes) { + for(auto it : saved_windowsizes) { sint64 m = it.key; file->rdwr_longlong(m); file->rdwr_long(it.value.w); @@ -497,7 +496,7 @@ gui_frame_t *win_get_magic(ptrdiff_t magic) { if( magic!=-1 && magic!=0 ) { // there is at most one window with a positive magic number - FOR( vector_tpl, const& i, wins ) { + for(simwin_t const& i : wins ) { if( i.magic_number == magic ) { // if 'special' magic number, return it return i.gui; @@ -513,7 +512,7 @@ bool win_set_magic( gui_frame_t *gui, ptrdiff_t magic ) { if( magic!=-1 && magic!=0 ) { // there is at most one window with a positive magic number - FOR( vector_tpl, &i, wins ) { + for(simwin_t &i : wins ) { if( i.gui == gui ) { i.magic_number = magic; return true; @@ -597,7 +596,7 @@ void rdwr_all_win(loadsave_t *file) { if( file->is_version_ex_atleast(14, 32) ) { if( file->is_saving() ) { - FOR(vector_tpl, & i, wins) { + for(simwin_t & i : wins) { uint32 id = i.gui->get_rdwr_id(); if( id!=magic_reserved ) { file->rdwr_long( id ); @@ -909,6 +908,17 @@ int create_win(scr_coord_val x, scr_coord_val y, gui_frame_t* const gui, wintype } } +static void save_win_position(const simwin_t &win) +{ + if( win.magic_number < magic_max ) { + if( scr_coord *k = old_win_pos.access(win.magic_number) ) { + *k = win.pos; + } + else { + old_win_pos.put( win.magic_number, win.pos ); + } + } +} /* sometimes a window cannot destroyed while it is still handled; * in those cases it will added to kill list and it is only destructed @@ -916,8 +926,9 @@ int create_win(scr_coord_val x, scr_coord_val y, gui_frame_t* const gui, wintype */ static void process_kill_list() { - FOR(vector_tpl, & i, kill_list) { + for(simwin_t & i : kill_list) { if (inside_event_handling != i.gui) { + save_win_position(i); destroy_framed_win(&i); wins.remove(i); } @@ -1015,15 +1026,7 @@ bool destroy_win(const gui_frame_t *gui, bool destroy_locked) else { simwin_t win = wins[i]; wins.remove_at(i); - if( win.magic_number < magic_max ) { - // save last pos - if( scr_coord *k = old_win_pos.access(win.magic_number) ) { - *k = win.pos; - } - else { - old_win_pos.put( win.magic_number, win.pos ); - } - } + save_win_position(win); destroy_framed_win(&win); } return true; @@ -1037,6 +1040,7 @@ void destroy_all_win(bool destroy_sticky) { for( sint32 curWin = 0; curWin < (sint32)wins.get_count(); curWin++ ) { if( destroy_sticky || (!wins[curWin].sticky && !wins[curWin].locked) ) { + save_win_position(wins[curWin]); if( inside_event_handling == wins[curWin].gui ) { // only add this, if not already added kill_list.append_unique(wins[curWin]); @@ -1217,7 +1221,7 @@ void display_all_win() void win_rotate90( sint16 new_ysize ) { - FOR(vector_tpl, const& i, wins) { + for(simwin_t const& i : wins) { i.gui->map_rotate90(new_ysize); } } @@ -1429,9 +1433,9 @@ void resize_win(int win, event_t *ev) // returns true, if gui is a open window handle bool win_is_open(gui_frame_t *gui) { - FOR(vector_tpl, const& i, wins) { + for(simwin_t const& i : wins) { if (i.gui == gui) { - FOR(vector_tpl, const& j, kill_list) { + for(simwin_t const& j : kill_list) { if (j.gui == gui) { return false; } @@ -1769,7 +1773,7 @@ void win_poll_event(event_t* const ev) if( ev->ev_class==EVENT_SYSTEM && ev->ev_code==SYSTEM_THEME_CHANGED ) { // called when font is changed ev->mx = ev->my = ev->cx = ev->cy = 0; - FOR(vector_tpl, const& i, wins) { + for(simwin_t const& i : wins) { i.gui->infowin_event(ev); } ev->ev_class = IGNORE_EVENT; diff --git a/gui/simwin.h b/gui/simwin.h index 54d9712c098..1d83dbdf8ed 100644 --- a/gui/simwin.h +++ b/gui/simwin.h @@ -133,6 +133,8 @@ enum magic_numbers { magic_pier_rotation_select, magic_depot, // only used to load/save magic_replace_line, + magic_consist_order, + magic_script_error, magic_max }; diff --git a/gui/themeselector.cc b/gui/themeselector.cc index 11e4c6b3a3f..7af725116b2 100644 --- a/gui/themeselector.cc +++ b/gui/themeselector.cc @@ -106,7 +106,7 @@ void themeselector_t::fill_list() // do the search ... savegame_frame_t::fill_list(); - FOR(slist_tpl, const& i, entries) { + for(dir_entry_t const& i : entries) { if (i.type == LI_HEADER) { continue; diff --git a/gui/tool_selector.cc b/gui/tool_selector.cc index 25fe6b30780..d408ff09d9c 100644 --- a/gui/tool_selector.cc +++ b/gui/tool_selector.cc @@ -96,8 +96,7 @@ void tool_selector_t::reset_tools() void tool_selector_t::rotate_tools(sint16 y_diff) { - FOR(vector_tpl, t, tools) - { + for(tool_data_t t : tools) { t.tool->rotate90(y_diff); } } @@ -231,7 +230,7 @@ bool tool_selector_t::infowin_event(const event_t *ev) } // this resets to query-tool, when closing toolsbar - but only for selected general tools in the closing toolbar else if(ev->ev_class==INFOWIN && ev->ev_code==WIN_CLOSE) { - FOR(vector_tpl, const i, tools) { + for(tool_data_t const i : tools) { if (i.tool->is_selected() && i.tool->get_id() & GENERAL_TOOL) { welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() ); break; @@ -403,7 +402,7 @@ void tool_selector_t::draw(scr_coord pos, scr_size sz) bool tool_selector_t::empty(player_t *player) const { - FOR(vector_tpl, w, tools) { + for(tool_data_t w : tools) { if (w.tool->get_icon(player) != IMG_EMPTY) { return false; } diff --git a/gui/vehiclelist_frame.cc b/gui/vehiclelist_frame.cc index c2c0abd4a11..29ad8aa6251 100644 --- a/gui/vehiclelist_frame.cc +++ b/gui/vehiclelist_frame.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ @@ -23,6 +23,7 @@ #include "../descriptor/vehicle_desc.h" #include "../utils/simstring.h" +#include "../unicode.h" int vehiclelist_stats_t::sort_mode = vehicle_builder_t::sb_intro_date; diff --git a/gui/way_info.cc b/gui/way_info.cc index 3891453545c..a24446498b3 100644 --- a/gui/way_info.cc +++ b/gui/way_info.cc @@ -1054,11 +1054,11 @@ void way_info_t::update() cont.new_component("convoi passed last month:"); lb = cont.new_component(SYSCOL_TEXT, gui_label_t::right); - lb->buf().printf("%u", way1->get_statistics(WAY_STAT_CONVOIS)); + lb->buf().printf("%u", way1->get_statistics(WAY_STAT_LAST_MONTH, WAY_STAT_CONVOIS)); lb->update(); if (way2) { lb = cont.new_component(SYSCOL_TEXT, gui_label_t::right); - lb->buf().printf("%u", way2->get_statistics(WAY_STAT_CONVOIS)); + lb->buf().printf("%u", way2->get_statistics(WAY_STAT_LAST_MONTH, WAY_STAT_CONVOIS)); lb->update(); } else { diff --git a/gui/welt.cc b/gui/welt.cc index 250de488aa6..f493385bc3d 100644 --- a/gui/welt.cc +++ b/gui/welt.cc @@ -88,7 +88,6 @@ welt_gui_t::welt_gui_t(settings_t* const sets_par) : game_ends = min( game_ends, (way_builder_t::get_latest_way(road_wt)->get_retire_year_month()+11)/12 ); loaded_heightfield = load_heightfield = false; - load = start = close = scenario = quit = false; sets->heightfield = ""; //****************************************************************** @@ -126,8 +125,9 @@ welt_gui_t::welt_gui_t(settings_t* const sets_par) : add_component(&inp_y_size); // Map size label - size_label.init(); - size_label.buf().printf(translator::translate("Size (%d MB):"), 9999); + size_label.buf().printf(translator::translate("Size (%d MB):"), 99999); + size_label.update(); + size_label.set_min_width(size_label.get_min_size().w); // Make sure to not make the component size too small when the window is opened with a small map size that is increased afterwards size_label.update(); add_component( &size_label, 3 ); @@ -631,18 +631,6 @@ bool welt_gui_t::action_triggered( gui_action_creator_t *comp,value_t v) } -bool welt_gui_t::infowin_event(const event_t *ev) -{ - gui_frame_t::infowin_event(ev); - - if(ev->ev_class==INFOWIN && ev->ev_code==WIN_CLOSE) { - close = true; - } - - return true; -} - - void welt_gui_t::draw(scr_coord pos, scr_size size) { // Coordinates are relative to parent (TITLEHEIGHT **NOT** subtracted) diff --git a/gui/welt.h b/gui/welt.h index 6036cefbd7a..40a621b91ab 100644 --- a/gui/welt.h +++ b/gui/welt.h @@ -26,22 +26,14 @@ class welt_gui_t : public gui_frame_t, private action_listener_t { -private: settings_t* sets; - /** - * Mini Map-Preview - */ + /// Mini Map-Preview array2d_tpl map; scr_size map_size; bool load_heightfield; bool loaded_heightfield; - bool load; - bool start; - bool close; - bool scenario; - bool quit; double city_density; double industry_density; @@ -85,9 +77,9 @@ class welt_gui_t : quit_game; /** - * Calculates preview from height map - * @param filename name of heightfield file - */ + * Calculates preview from height map + * @param filename name of heightfield file + */ bool update_from_heightfield(const char *filename); void resize_preview(); @@ -98,6 +90,7 @@ class welt_gui_t : public: welt_gui_t(settings_t*); +public: /** * Berechnet Preview-Karte neu. Inititialisiert RNG neu! * public, because also the climate dialog need it @@ -117,8 +110,6 @@ class welt_gui_t : // does not work during new world dialog bool has_sticky() const OVERRIDE { return false; } - bool infowin_event(event_t const*) OVERRIDE; - /** * Draw new component. The values to be passed refer to the window * i.e. It's the screen coordinates of the window where the diff --git a/ifc/simtestdriver.h b/ifc/simtestdriver.h index 0668f32cd7e..ff848f72d5d 100644 --- a/ifc/simtestdriver.h +++ b/ifc/simtestdriver.h @@ -33,7 +33,7 @@ class test_driver_t virtual waytype_t get_waytype() const = 0; // how expensive to go here (for way search) with the maximum convoi speed as second parameter - virtual int get_cost(const grund_t *, const sint32, koord from_pos) = 0; + virtual int get_cost(const grund_t *, const sint32 max_speed, ribi_t::ribi from) = 0; // returns true for the way search to an unknown target. // first is current ground, second is starting ground diff --git a/io/classify_file.h b/io/classify_file.h index 72b62dc34fd..51ce04f0d85 100644 --- a/io/classify_file.h +++ b/io/classify_file.h @@ -7,7 +7,6 @@ #define IO_CLASSIFY_FILE_H -#include "../unicode.h" #include "../simtypes.h" diff --git a/io/rdwr/adler32_stream.cc b/io/rdwr/adler32_stream.cc index 6aa94834395..3ddfcaf0ef2 100644 --- a/io/rdwr/adler32_stream.cc +++ b/io/rdwr/adler32_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/adler32_stream.h b/io/rdwr/adler32_stream.h index 79f3663f5e9..5386229fb38 100644 --- a/io/rdwr/adler32_stream.h +++ b/io/rdwr/adler32_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/bzip2_file_rdwr_stream.cc b/io/rdwr/bzip2_file_rdwr_stream.cc index b0c28d8abfa..7b3b37ad644 100644 --- a/io/rdwr/bzip2_file_rdwr_stream.cc +++ b/io/rdwr/bzip2_file_rdwr_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/bzip2_file_rdwr_stream.h b/io/rdwr/bzip2_file_rdwr_stream.h index 7bb6bca9057..9ea8edb3303 100644 --- a/io/rdwr/bzip2_file_rdwr_stream.h +++ b/io/rdwr/bzip2_file_rdwr_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/compare_file_rd_stream.cc b/io/rdwr/compare_file_rd_stream.cc index db1f8a4ab97..9934fb912ab 100644 --- a/io/rdwr/compare_file_rd_stream.cc +++ b/io/rdwr/compare_file_rd_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/compare_file_rd_stream.h b/io/rdwr/compare_file_rd_stream.h index cbb34e600c2..6a99e82b98f 100644 --- a/io/rdwr/compare_file_rd_stream.h +++ b/io/rdwr/compare_file_rd_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/raw_file_rdwr_stream.cc b/io/rdwr/raw_file_rdwr_stream.cc index 12018f3e4c5..fd0ef65b15f 100644 --- a/io/rdwr/raw_file_rdwr_stream.cc +++ b/io/rdwr/raw_file_rdwr_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/raw_file_rdwr_stream.h b/io/rdwr/raw_file_rdwr_stream.h index ba23aa6d37c..9401ef4b478 100644 --- a/io/rdwr/raw_file_rdwr_stream.h +++ b/io/rdwr/raw_file_rdwr_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/rdwr_stream.cc b/io/rdwr/rdwr_stream.cc index e2d263047d0..01c45a4751d 100644 --- a/io/rdwr/rdwr_stream.cc +++ b/io/rdwr/rdwr_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/rdwr_stream.h b/io/rdwr/rdwr_stream.h index 60f612f9f25..ae7cfe6e1ff 100644 --- a/io/rdwr/rdwr_stream.h +++ b/io/rdwr/rdwr_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/zlib_file_rdwr_stream.cc b/io/rdwr/zlib_file_rdwr_stream.cc index 3fa2714fcdb..53610f026e5 100644 --- a/io/rdwr/zlib_file_rdwr_stream.cc +++ b/io/rdwr/zlib_file_rdwr_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/zlib_file_rdwr_stream.h b/io/rdwr/zlib_file_rdwr_stream.h index 16474024a66..f0912b6e109 100644 --- a/io/rdwr/zlib_file_rdwr_stream.h +++ b/io/rdwr/zlib_file_rdwr_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/zstd_file_rdwr_stream.cc b/io/rdwr/zstd_file_rdwr_stream.cc index 18ac7b24e8a..3d78e3dec9d 100644 --- a/io/rdwr/zstd_file_rdwr_stream.cc +++ b/io/rdwr/zstd_file_rdwr_stream.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/io/rdwr/zstd_file_rdwr_stream.h b/io/rdwr/zstd_file_rdwr_stream.h index ad36ed18f81..23dd98f252a 100644 --- a/io/rdwr/zstd_file_rdwr_stream.h +++ b/io/rdwr/zstd_file_rdwr_stream.h @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ diff --git a/makeobj/CMakeLists.txt b/makeobj/CMakeLists.txt index b56c4036649..b876c56a11e 100644 --- a/makeobj/CMakeLists.txt +++ b/makeobj/CMakeLists.txt @@ -1,5 +1,5 @@ # -# This file is part of the Simutrans project under the artistic licence. +# This file is part of the Simutrans-Extended project under the artistic licence. # (see licence.txt) # diff --git a/makeobj/Makefile b/makeobj/Makefile index 83de4c97f9d..bd4e3dbe0a7 100644 --- a/makeobj/Makefile +++ b/makeobj/Makefile @@ -83,7 +83,7 @@ ifneq ($(PROFILE),) endif CFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wpadded $(OS_INC) $(OS_OPT) $(FLAGS) -CXXFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -Wpadded -std=c++11 $(OS_INC) $(OS_OPT) $(FLAGS) +CXXFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -Wpadded -std=c++14 $(OS_INC) $(OS_OPT) $(FLAGS) # SOLO_SOURCES contains files which are unique to makeobj; # SHARED_SOURCES contains those with the exact same object code in makeobj and simutrans; diff --git a/music/fluidsynth.cc b/music/fluidsynth.cc index 7021090942d..b6c91433dd1 100644 --- a/music/fluidsynth.cc +++ b/music/fluidsynth.cc @@ -1,5 +1,5 @@ /* - * This file is part of the Simutrans project under the Artistic License. + * This file is part of the Simutrans-Extended project under the Artistic License. * (see LICENSE.txt) */ #include diff --git a/nettools/CMakeLists.txt b/nettools/CMakeLists.txt index 716a415d5f4..825ac195534 100644 --- a/nettools/CMakeLists.txt +++ b/nettools/CMakeLists.txt @@ -1,5 +1,5 @@ # -# This file is part of the Simutrans project under the artistic licence. +# This file is part of the Simutrans-Extended project under the artistic licence. # (see licence.txt) # diff --git a/nettools/Makefile b/nettools/Makefile index 65abd19eebc..2049062f987 100644 --- a/nettools/Makefile +++ b/nettools/Makefile @@ -72,7 +72,7 @@ ifneq ($(PROFILE),) endif CFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -Wstrict-prototypes $(OS_INC) $(OS_OPT) $(FLAGS) -CXXFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -std=c++11 $(OS_INC) $(OS_OPT) $(FLAGS) +CXXFLAGS += -Wall -Wextra -Wcast-qual -Wpointer-arith -Wcast-align -std=c++14 $(OS_INC) $(OS_OPT) $(FLAGS) # SOLO_SOURCES contains files which are unique to nettool; # SHARED_SOURCES contains those with the exact same object code in nettool and simutrans; diff --git a/network/network.cc b/network/network.cc index c034244eab6..720106c4375 100644 --- a/network/network.cc +++ b/network/network.cc @@ -5,6 +5,11 @@ /* basic network functionality, borrowed from OpenTTD */ +#if defined(__amiga__) +// warning: IPv6 will only work on Windows XP and up ... +#define USE_IP4_ONLY +#endif + #include #include #include @@ -26,13 +31,10 @@ #include "../dataobj/environment.h" #endif -#ifdef NETTOOL -#include "../tpl/vector_tpl.h" -#endif - #include "../utils/simstring.h" #include "../tpl/slist_tpl.h" + static bool network_active = false; uint16 network_server_port = 0; @@ -44,7 +46,7 @@ address_list_t blacklist; void clear_command_queue() { - while (!received_command_queue.empty()) { + while(!received_command_queue.empty()) { network_command_t *nwc = received_command_queue.remove_first(); delete nwc; } @@ -74,19 +76,19 @@ uint32 network_get_client_id() /** -* Initializes the network core (as that is needed for some platforms -* @return true if the core has been initialized, false otherwise -*/ + * Initializes the network core (as that is needed for some platforms + * @return true if the core has been initialized, false otherwise + */ static bool network_initialize() { - if (!network_active) { + if(!network_active) { socket_list_t::reset(); #if USE_WINSOCK /* Let's load the network in windows */ WSADATA wsa; - if (int err = WSAStartup(MAKEWORD(2, 2), &wsa)) { - dbg->warning("NetworkInitialize()", "failed loading windows socket library with %i", err); + if(int err = WSAStartup( MAKEWORD(2, 2), &wsa)) { + dbg->warning("NetworkInitialize()","failed loading windows socket library with %i", err); return false; } #endif @@ -97,10 +99,10 @@ static bool network_initialize() /** -* Open a socket connected to a remote address -* In case of failure err is populated with a meaningful error -* @return a valid socket or INVALID_SOCKET if the connection fails -*/ + * Open a socket connected to a remote address + * In case of failure err is populated with a meaningful error + * @return a valid socket or INVALID_SOCKET if the connection fails + */ SOCKET network_open_address(char const* cp, char const*& err) { err = NULL; @@ -115,17 +117,17 @@ SOCKET network_open_address(char const* cp, char const*& err) char address[1024]; static char err_str[256]; uint16 port = 13353; - const char *cp2 = strrchr(cp, ':'); - if (cp2 != NULL) { - port = atoi(cp2 + 1); + const char *cp2 = strrchr(cp,':'); + if(cp2!=NULL) { + port=atoi(cp2+1); // Copy the address part - tstrncpy(address, cp, cp2 - cp>sizeof(address) - 1 ? sizeof(address) - 1 : cp2 - cp + 1); + tstrncpy(address,cp,cp2-cp>sizeof(address)-1?sizeof(address)-1:cp2-cp+1); cp = address; } struct sockaddr_in server_name; - memset(&server_name, 0, sizeof(server_name)); - server_name.sin_family = AF_INET; + memset(&server_name,0,sizeof(server_name)); + server_name.sin_family=AF_INET; #if USE_WINSOCK bool ok = true; server_name.sin_addr.s_addr = inet_addr(cp); // for windows we must first try to resolve the number @@ -138,26 +140,26 @@ SOCKET network_open_address(char const* cp, char const*& err) ok = true; } } - if (!ok) { + if(!ok) { #else /* inet_anon does not work on BeOS; but since gethostbyname() can - * do this job on all other systems too, we use it only: - * instead of if(inet_aton(cp,&server_name.sin_addr)==0) { // Bad address - */ + * do this job on all other systems too, we use it only: + * instead of if(inet_aton(cp,&server_name.sin_addr)==0) { // Bad address + */ struct hostent *theHost; - theHost = gethostbyname(cp); - if (theHost) { + theHost = gethostbyname( cp ); + if(theHost) { server_name.sin_addr = *(struct in_addr *)theHost->h_addr_list[0]; } else {// Bad address #endif - sprintf(err_str, "Bad address %s", cp); + sprintf( err_str, "Bad address %s", cp ); RET_ERR_STR; } - server_name.sin_port = htons(port); + server_name.sin_port=htons(port); - SOCKET my_client_socket = socket(AF_INET, SOCK_STREAM, 0); - if (my_client_socket == INVALID_SOCKET) { + SOCKET my_client_socket = socket(AF_INET,SOCK_STREAM,0); + if(my_client_socket==INVALID_SOCKET) { err = "Cannot create socket"; return INVALID_SOCKET; } @@ -173,13 +175,13 @@ SOCKET network_open_address(char const* cp, char const*& err) static char err_str[256]; // scan address string for key features - const char *cp2 = strrchr(cp, ':'); - const char *ipv6end = strrchr(cp, ']'); - const char *ipv6start = strchr(cp, '['); + const char *cp2 = strrchr( cp, ':' ); + const char *ipv6end = strrchr( cp, ']' ); + const char *ipv6start = strchr( cp, '[' ); // extract port if available std::string cpport; - if (cp2 != NULL && cp2 > ipv6end) { + if ( cp2 != NULL && cp2 > ipv6end ) { cpport = std::string(cp2 + 1); } else { @@ -219,7 +221,7 @@ SOCKET network_open_address(char const* cp, char const*& err) // Set up remote addrinfo struct addrinfo *remote; struct addrinfo remote_hints; - memset(&remote_hints, 0, sizeof(struct addrinfo)); + memset( &remote_hints, 0, sizeof( struct addrinfo ) ); remote_hints.ai_socktype = SOCK_STREAM; remote_hints.ai_family = PF_UNSPEC; @@ -237,45 +239,45 @@ SOCKET network_open_address(char const* cp, char const*& err) // Set up local addrinfo struct addrinfo *local; struct addrinfo local_hints; - memset(&local_hints, 0, sizeof(struct addrinfo)); + memset( &local_hints, 0, sizeof( struct addrinfo ) ); local_hints.ai_socktype = SOCK_STREAM; local_hints.ai_family = PF_UNSPEC; // Insert listen address into local_hints struct to influence local address to bind to - DBG_MESSAGE("network_open_address()", "Preparing to bind address: %s", ip.c_str()); - if ((ret = getaddrinfo(ip.c_str(), 0, &local_hints, &local)) != 0) { - dbg->warning("network_open_address()", "Failed to getaddrinfo for %s, error was: %s", ip.c_str(), gai_strerror(ret)); + DBG_MESSAGE( "network_open_address()", "Preparing to bind address: %s", ip.c_str() ); + if ( (ret = getaddrinfo( ip.c_str(), 0, &local_hints, &local )) != 0 ) { + dbg->warning( "network_open_address()", "Failed to getaddrinfo for %s, error was: %s", ip.c_str(), gai_strerror(ret) ); #ifndef NETTOOL - env_t::listen.remove_at(i); + env_t::listen.remove_at( i ); #endif - i--; + i --; continue; } // Now try and open a socket to communicate with our remote endpoint struct addrinfo *walk_local; - for (walk_local = local; !connected && walk_local != NULL; walk_local = walk_local->ai_next) { + for( walk_local = local; !connected && walk_local != NULL; walk_local = walk_local->ai_next ) { char ipstr_local[INET6_ADDRSTRLEN]; // Validate address + get string representation for logging - if ((ret = getnameinfo((walk_local->ai_addr), (socklen_t)walk_local->ai_addrlen, ipstr_local, sizeof(ipstr_local), NULL, 0, NI_NUMERICHOST)) != 0) { - dbg->warning("network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret)); + if ( (ret = getnameinfo( (walk_local->ai_addr), (socklen_t)walk_local->ai_addrlen, ipstr_local, sizeof(ipstr_local), NULL, 0, NI_NUMERICHOST )) !=0 ) { + dbg->warning( "network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } - DBG_MESSAGE("network_open_address()", "Potential local address: %s", ipstr_local); + DBG_MESSAGE( "network_open_address()", "Potential local address: %s", ipstr_local ); - my_client_socket = socket(walk_local->ai_family, walk_local->ai_socktype, walk_local->ai_protocol); + my_client_socket = socket( walk_local->ai_family, walk_local->ai_socktype, walk_local->ai_protocol ); - if (my_client_socket == INVALID_SOCKET) { - DBG_MESSAGE("network_open_address()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR())); + if ( my_client_socket == INVALID_SOCKET ) { + DBG_MESSAGE( "network_open_address()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); continue; } // Bind socket to local IP - if (::bind(my_client_socket, walk_local->ai_addr, walk_local->ai_addrlen) == -1) { - DBG_MESSAGE("network_open_address()", "Unable to bind socket to local IP address! Error: \"%s\"", strerror(GET_LAST_ERROR())); - network_close_socket(my_client_socket); + if ( ::bind( my_client_socket, walk_local->ai_addr, walk_local->ai_addrlen ) ) { + DBG_MESSAGE( "network_open_address()", "Unable to bind socket to local IP address! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); + network_close_socket( my_client_socket ); my_client_socket = INVALID_SOCKET; continue; } @@ -286,170 +288,227 @@ SOCKET network_open_address(char const* cp, char const*& err) char ipstr_remote[INET6_ADDRSTRLEN]; // Validate remote address + get string representation for logging - if ((ret = getnameinfo(walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen, ipstr_remote, sizeof(ipstr_remote), NULL, 0, NI_NUMERICHOST)) != 0) { - dbg->warning("network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret)); + if ( (ret = getnameinfo( walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen, ipstr_remote, sizeof(ipstr_remote), NULL, 0, NI_NUMERICHOST )) !=0 ) { + dbg->warning( "network_open_address()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } - DBG_MESSAGE("network_open_address()", "Potential remote address: %s", ipstr_remote); + DBG_MESSAGE( "network_open_address()", "Potential remote address: %s", ipstr_remote ); - if (connect(my_client_socket, walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen) != 0) { - DBG_MESSAGE("network_open_address()", "Could not connect using this socket. Error: \"%s\"", strerror(GET_LAST_ERROR())); + // put socked in non-blocking mode... +#ifdef WIN32 + u_long block = 1; + if( ioctlsocket(my_client_socket, FIONBIO, &block) != 0 ) { + my_client_socket = INVALID_SOCKET; continue; } +#else + if (fcntl(my_client_socket, F_SETFL, fcntl(my_client_socket, F_GETFL, 0) | O_NONBLOCK) != 0) { + my_client_socket = INVALID_SOCKET; + continue; + } +#endif + + if (connect(my_client_socket, walk_remote->ai_addr, (socklen_t)walk_remote->ai_addrlen) != 0) { + + if( GET_LAST_ERROR() != EINPROGRESS) { + // connection failed + network_close_socket(my_client_socket); + continue; + } + + // connection pending + fd_set setW, setE; + + FD_ZERO(&setW); + FD_SET(my_client_socket, &setW); + FD_ZERO(&setE); + FD_SET(my_client_socket, &setE); + + timeval time_out; + time_out.tv_sec = 2; // 2 seconds or joining a server would not make sense + time_out.tv_usec = 0; + + int ret = select(my_client_socket+1, NULL, &setW, &setE, &time_out); + if (ret <= 0) { + // select() failed or connection timed out + DBG_MESSAGE("network_open_address()", "Could not connect using this socket. select() Error: \"%s\"", strerror(GET_LAST_ERROR())); + network_close_socket(my_client_socket); + continue; + } + + if( FD_ISSET(my_client_socket, &setE) ) { + // connection failed + DBG_MESSAGE("network_open_address()", "Could not connect FD_ISSET failed."); + network_close_socket(my_client_socket); + continue; + } + // connection successful + + // put socket in blocking mode... +#ifdef WIN32 + block = 0; + bool blocking_mode = ioctlsocket(my_client_socket, FIONBIO, &block) == 0; +#else + // on linux clear O_NONBLOCK flag + const int flags = fcntl(my_client_socket, F_GETFL, 0); + bool blocking_mode = fcntl(my_client_socket, F_SETFL, flags & (~O_NONBLOCK)) == 0; +#endif + if (!blocking_mode) { + DBG_MESSAGE("network_open_address()", "Could not reset to non-blocking."); + network_close_socket(my_client_socket); + continue; + } + + + } + connected = true; } // If no connection throw away this socket and try the next one - if (!connected) { - network_close_socket(my_client_socket); + if ( !connected ) { + network_close_socket( my_client_socket ); my_client_socket = INVALID_SOCKET; } } - freeaddrinfo(local); - freeaddrinfo(remote); + freeaddrinfo( local ); + freeaddrinfo( remote ); } - if (my_client_socket == INVALID_SOCKET) { - sprintf(err_str, "Could not connect to %s", cp); + if ( my_client_socket == INVALID_SOCKET ) { + sprintf( err_str, "Could not connect to %s", cp ); RET_ERR_STR; } #endif return my_client_socket; - } +} /** -* Start up server on the port specified -* Server will listen on all addresses specified in env_t::listen -* @return true on success -*/ -bool network_init_server(int port) + * Start up server on the port specified + * Server will listen on all addresses specified in @p listen_addrs + * @return true on success + */ +bool network_init_server( int port, const vector_tpl &listen_addrs ) { if ( port==0 ) { dbg->fatal( "network_init_server()", "Cannot host on port 0!" ); } // First activate network - if (!network_initialize()) { - dbg->fatal("network_init_server()", "Failed to initialize network!"); + if ( !network_initialize() ) { + dbg->fatal( "network_init_server()", "Failed to initialize network!" ); } socket_list_t::reset(); #ifdef USE_IP4_ONLY + (void)listen_addrs; - SOCKET my = socket(PF_INET, SOCK_STREAM, 0); - if (my == INVALID_SOCKET) { - dbg->fatal("network_init_server()", "Failed to open socket!"); + const SOCKET my = socket( PF_INET, SOCK_STREAM, 0 ); + if ( my == INVALID_SOCKET ) { + dbg->fatal( "network_init_server()", "Failed to open socket!" ); return false; } struct sockaddr_in name; name.sin_family = AF_INET; - name.sin_port = htons(port); - name.sin_addr.s_addr = htonl(INADDR_ANY); + name.sin_port = htons( port ); + name.sin_addr.s_addr = htonl( INADDR_ANY ); - if (::bind(my, (struct sockaddr *)&name, sizeof(name)) == -1) { - dbg->fatal("network_init_server()", "Bind failed!"); + if ( bind( my, (struct sockaddr *)&name, sizeof( name ) ) == -1 ) { + dbg->fatal( "network_init_server()", "Bind failed!" ); return false; } /* Max pending connections */ - if (listen(my, MAX_PLAYER_COUNT) == -1) { - dbg->fatal("network_init_server()", "Listen failed for %i sockets!", MAX_PLAYER_COUNT); + if ( listen( my, MAX_PLAYER_COUNT ) == -1 ) { + dbg->fatal( "network_init_server()", "Listen failed for %i sockets!", MAX_PLAYER_COUNT ); return false; } - socket_list_t::add_server(my); + socket_list_t::add_server( my ); #else -#ifdef NETTOOL - // Nettool doesn't have env, so fake it - vector_tpl ips; - ips.append_unique("::"); - ips.append_unique("0.0.0.0"); -#else - vector_tpl const& ips = env_t::listen; -#endif // For each address in the list of listen addresses try and create a socket to listen on - FOR(vector_tpl, const& ip, ips) { + for(std::string const& ip : listen_addrs) { int ret; char port_nr[16]; - sprintf(port_nr, "%u", port); + sprintf( port_nr, "%u", port ); struct addrinfo *server; struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); + memset( &hints, 0, sizeof( struct addrinfo ) ); hints.ai_socktype = SOCK_STREAM; // Insert potential listen address into hints struct to influence local address to bind to - DBG_MESSAGE("network_init_server()", "Preparing to bind address: \"%s\"", ip.c_str()); + DBG_MESSAGE( "network_init_server()", "Preparing to bind address: \"%s\"", ip.c_str() ); hints.ai_family = PF_UNSPEC; - if ((ret = getaddrinfo(ip.c_str(), port_nr, &hints, &server)) != 0) { - dbg->fatal("network_init_server()", "Call to getaddrinfo() failed for: \"%s\", error was: \"%s\" - check listen directive in simuconf.tab!", ip.c_str(), gai_strerror(ret)); + if ( (ret = getaddrinfo( ip.c_str(), port_nr, &hints, &server )) != 0 ) { + dbg->fatal( "network_init_server()", "Call to getaddrinfo() failed for: \"%s\", error was: \"%s\" - check listen directive in simuconf.tab!", ip.c_str(), gai_strerror(ret) ); } else { - dbg->message("network_init_server()", "Attempting to bind listening sockets for: \"%s\"\n", ip.c_str()); + dbg->message( "network_init_server()", "Attempting to bind listening sockets for: \"%s\"\n", ip.c_str() ); } SOCKET server_socket; struct addrinfo *walk; // Open a listen socket for each IP address specified by this entry in the listen list - for (walk = server; walk != NULL; walk = walk->ai_next) { + for ( walk = server; walk != NULL; walk = walk->ai_next ) { char ipstr[INET6_ADDRSTRLEN]; // Validate address + get string representation for logging - if ((ret = getnameinfo(walk->ai_addr, (socklen_t)walk->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST)) != 0) { - dbg->warning("network_init_server()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret)); + if ( (ret = getnameinfo( walk->ai_addr, (socklen_t)walk->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST )) != 0 ) { + dbg->warning( "network_init_server()", "Call to getnameinfo() failed with error: \"%s\"", gai_strerror(ret) ); continue; } - DBG_MESSAGE("network_init_server()", "Potential bind address: %s", ipstr); + DBG_MESSAGE( "network_init_server()", "Potential bind address: %s", ipstr ); - server_socket = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol); + server_socket = socket( walk->ai_family, walk->ai_socktype, walk->ai_protocol ); - if (server_socket == INVALID_SOCKET) { - dbg->warning("network_init_server()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR())); + if ( server_socket == INVALID_SOCKET ) { + dbg->warning( "network_init_server()", "Could not create socket! Error: \"%s\"", strerror(GET_LAST_ERROR()) ); continue; } /* Disable IPv4-mapped IPv6 addresses for this IPv6 listen socket - This ensures that we are using separate sockets for dual-stack, one for v4, one for v6 */ - if (walk->ai_family == AF_INET6) { + This ensures that we are using separate sockets for dual-stack, one for v4, one for v6 */ + if ( walk->ai_family == AF_INET6 ) { int const on = 1; - if (setsockopt(server_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&on), sizeof(on)) != 0) { - dbg->warning("network_init_server()", "Call to setsockopt(IPV6_V6ONLY) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR())); - network_close_socket(server_socket); + if ( setsockopt(server_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&on), sizeof(on)) != 0 ) { + dbg->warning( "network_init_server()", "Call to setsockopt(IPV6_V6ONLY) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR()) ); + network_close_socket( server_socket ); server_socket = INVALID_SOCKET; continue; } } // Enable reusing of local addresses int const enable = 1; - if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&enable), sizeof(enable)) != 0) { - dbg->warning("network_init_server()", "Call to setsockopt(SO_REUSEADDR) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR())); + if ( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&enable), sizeof(enable)) != 0 ) { + dbg->warning( "network_init_server()", "Call to setsockopt(SO_REUSEADDR) failed for: \"%s\", error was: \"%s\"", ip.c_str(), strerror(GET_LAST_ERROR()) ); } - if (::bind(server_socket, walk->ai_addr, walk->ai_addrlen) == -1) { + if ( ::bind( server_socket, walk->ai_addr, walk->ai_addrlen ) ) { /* Unable to bind a socket - abort execution as we are supposed to be a server on this interface */ - dbg->fatal("network_init_server()", "Unable to bind socket to IP address: \"%s\", error was: \"%s\"", ipstr, strerror(GET_LAST_ERROR())); + dbg->fatal( "network_init_server()", "Unable to bind socket to IP address: \"%s\", error was: \"%s\"", ipstr, strerror(GET_LAST_ERROR()) ); } - if (listen(server_socket, 32) == -1) { + if ( listen( server_socket, 32 ) == -1 ) { /* Unable to listen on bound socket - abort execution as we are supposed to be a server on this interface */ - dbg->fatal("network_init_server()", "Unable to set socket to listen for incoming connections on: \"%s\"", ipstr); + dbg->fatal( "network_init_server()", "Unable to set socket to listen for incoming connections on: \"%s\"", ipstr ); } - dbg->message("network_init_server()", "Added valid listen socket for address: \"%s\"\n", ipstr); - socket_list_t::add_server(server_socket); + dbg->message( "network_init_server()", "Added valid listen socket for address: \"%s\"\n", ipstr); + socket_list_t::add_server( server_socket ); } - freeaddrinfo(server); + freeaddrinfo( server ); } // Fatal error if no server sockets could be opened, since we're supposed to be a server! - if (socket_list_t::get_server_sockets() == 0) { - dbg->fatal("network_init_server()", "Unable to add any server sockets!"); + if ( socket_list_t::get_server_sockets() == 0 ) { + dbg->fatal( "network_init_server()", "Unable to add any server sockets!" ); } else { dbg->message("network_init_server", "Server started, added %d server sockets", socket_list_t::get_server_sockets()); @@ -465,16 +524,16 @@ bool network_init_server(int port) } -void network_set_socket_nodelay(SOCKET sock) +void network_set_socket_nodelay( SOCKET sock ) { #if (defined(TCP_NODELAY) || COLOUR_DEPTH == 0) // do not wait to join small (command) packets when sending (may cause 200ms delay!) // force this for dedicated servers int b = 1; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)); + setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b) ); #else - #warning TCP_NODELAY not defined.Expect multiplayer problems. - (void)sock; +#warning TCP_NODELAY not defined. Expect multiplayer problems. + (void)sock; #endif } @@ -489,9 +548,9 @@ network_command_t* network_get_received_command() /* do appropriate action for network games: -* - server: accept connection to a new client -* - all: receive commands and puts them to the received_command_queue -*/ + * - server: accept connection to a new client + * - all: receive commands and puts them to the received_command_queue + */ network_command_t* network_check_activity(karte_t *, int timeout) { fd_set fds; @@ -504,39 +563,39 @@ network_command_t* network_check_activity(karte_t *, int timeout) tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000ul; - int action = select(FD_SETSIZE, &fds, NULL, NULL, &tv); - if (action <= 0) { + int action = select( FD_SETSIZE, &fds, NULL, NULL, &tv ); + if( action<=0 ) { // timeout: return command from the queue return network_get_received_command(); } // accept new connection socket_list_t::server_socket_iterator_t iter_s(&fds); - while (iter_s.next()) { + while(iter_s.next()) { SOCKET accept_sock = iter_s.get_current(); - if (accept_sock != INVALID_SOCKET) { + if( accept_sock!=INVALID_SOCKET ) { struct sockaddr_in client_name; socklen_t size = sizeof(client_name); SOCKET s = accept(accept_sock, (struct sockaddr *)&client_name, &size); - if (s != INVALID_SOCKET) { + if( s!=INVALID_SOCKET ) { #if USE_WINSOCK uint32 ip = ntohl((uint32)client_name.sin_addr.S_un.S_addr); #else uint32 ip = ntohl((uint32)client_name.sin_addr.s_addr); #endif - if (blacklist.contains(net_address_t(ip))) { + if (blacklist.contains(net_address_t( ip ))) { // refuse connection network_close_socket(s); continue; } #ifdef __BEOS__ char name[256]; - sprintf(name, "%lh", client_name.sin_addr.s_addr); + sprintf(name, "%lh", client_name.sin_addr.s_addr ); #else const char *name = inet_ntoa(client_name.sin_addr); #endif - dbg->message("check_activity()", "Accepted connection from: %s.", name); + dbg->message("check_activity()", "Accepted connection from: %s.", name); socket_list_t::add_client(s, ip); } } @@ -544,7 +603,7 @@ network_command_t* network_check_activity(karte_t *, int timeout) // receive from clients socket_list_t::client_socket_iterator_t iter_c(&fds); - while (iter_c.next()) { + while(iter_c.next()) { SOCKET sender = iter_c.get_current(); if (sender != INVALID_SOCKET && socket_list_t::has_client(sender)) { @@ -552,7 +611,7 @@ network_command_t* network_check_activity(karte_t *, int timeout) network_command_t *nwc = socket_list_t::get_client(client_id).receive_nwc(); if (nwc) { received_command_queue.append(nwc); - dbg->message( "network_check_activity()", "received cmd %s (id %d) from socket[%d]", nwc->get_name(), nwc->get_id(), sender ); + dbg->warning( "network_check_activity()", "received cmd %s (id %d) from socket[%d]", nwc->get_name(), nwc->get_id(), sender ); } // errors are caught and treated in socket_info_t::receive_nwc } @@ -573,16 +632,16 @@ void network_process_send_queues(int timeout) tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000ul; - int action = select(FD_SETSIZE, NULL, &fds, NULL, &tv); + int action = select( FD_SETSIZE, NULL, &fds, NULL, &tv ); - if (action <= 0) { + if( action<=0 ) { // timeout: return return; } // send to clients socket_list_t::client_socket_iterator_t iter_c(&fds); - while (iter_c.next() && action>0) { + while(iter_c.next() && action>0) { SOCKET sock = iter_c.get_current(); if (sock != INVALID_SOCKET && socket_list_t::has_client(sock)) { @@ -590,18 +649,18 @@ void network_process_send_queues(int timeout) socket_list_t::get_client(client_id).process_send_queue(); // errors are caught and treated in socket_info_t::process_send_queue } - action--; + action --; } } bool network_check_server_connection() { - if (!network_server_port) { + if( !network_server_port ) { // I am client // If I am playing, playing_clients should be at least one. - if (socket_list_t::get_playing_clients() == 0) { + if( socket_list_t::get_playing_clients()==0 ) { return false; } @@ -614,8 +673,8 @@ bool network_check_server_connection() FD_ZERO(&fds_write); socket_list_t::fill_set(&fds_write); - int action = select(FD_SETSIZE, &fds_read, &fds_write, NULL, &tv); - if (action < 0) { + int action = select( FD_SETSIZE, &fds_read, &fds_write, NULL, &tv ); + if( action < 0 ) { // error - connection lost return false; } @@ -626,12 +685,12 @@ bool network_check_server_connection() // send data to all PLAYING clients // nwc is invalid after the call -void network_send_all(network_command_t* nwc, bool exclude_us) +void network_send_all(network_command_t* nwc, bool exclude_us, uint8 player_nr) { if (nwc) { nwc->prepare_to_send(); - socket_list_t::send_all(nwc, true); - if (!exclude_us && network_server_port) { + socket_list_t::send_all(nwc, true, player_nr); + if( !exclude_us && network_server_port ) { // I am the server nwc->get_packet()->sent_by_server(); received_command_queue.append(nwc); @@ -645,11 +704,11 @@ void network_send_all(network_command_t* nwc, bool exclude_us) // send data to server // nwc is invalid after the call -void network_send_server(network_command_t* nwc) +void network_send_server(network_command_t* nwc ) { if (nwc) { nwc->prepare_to_send(); - if (!network_server_port) { + if( !network_server_port ) { // I am client socket_list_t::send_all(nwc, true); delete nwc; @@ -664,12 +723,12 @@ void network_send_server(network_command_t* nwc) /** -* send data to dest -* @param buf the data -* @param size length of buffer and number of bytes to be sent -* @return true if data was completely send, false if an error occurs and connection needs to be closed -*/ -bool network_send_data(SOCKET dest, const char *buf, const uint16 size, uint16 &count, const int timeout_ms) + * send data to dest + * @param buf the data + * @param size length of buffer and number of bytes to be sent + * @return true if data was completely send, false if an error occurs and connection needs to be closed + */ +bool network_send_data( SOCKET dest, const char *buf, const uint16 size, uint16 &count, const int timeout_ms ) { count = 0; @@ -679,7 +738,7 @@ bool network_send_data(SOCKET dest, const char *buf, const uint16 size, uint16 & #endif while (count < size) { - int sent = send(dest, buf + count, size - count, 0); + int sent = send(dest, buf+count, size-count, 0); if (sent == -1) { int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { @@ -694,7 +753,7 @@ bool network_send_data(SOCKET dest, const char *buf, const uint16 size, uint16 & // try again, test whether sending is possible fd_set fds; FD_ZERO(&fds); - FD_SET(dest, &fds); + FD_SET(dest,&fds); struct timeval tv; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000ul; @@ -708,11 +767,11 @@ bool network_send_data(SOCKET dest, const char *buf, const uint16 size, uint16 & } if (sent == 0) { // connection closed - dbg->warning("network_send_data", "connection [%d] already closed (sent %d of &d)", dest, count, size); + dbg->warning("network_send_data", "connection [%d] already closed (sent %hu of %hu)", dest, count, size ); return false; } count += sent; - DBG_DEBUG4("network_send_data", "sent %d bytes to socket[%d]; size=%d, left=%d", count, dest, size, size - count); + DBG_DEBUG4("network_send_data", "sent %d bytes to socket[%d]; size=%d, left=%d", count, dest, size, size-count ); } #if USE_WINSOCK == 0 @@ -725,13 +784,13 @@ bool network_send_data(SOCKET dest, const char *buf, const uint16 size, uint16 & /** -* receive data from sender -* @param dest the destination buffer -* @param len length of destination buffer and number of bytes to be received -* @param received number of received bytes is returned here -* @return true if connection is still valid, false if an error occurs and connection needs to be closed -*/ -bool network_receive_data(SOCKET sender, void *dest, const uint16 len, uint16 &received, const int timeout_ms) + * receive data from sender + * @param dest the destination buffer + * @param len length of destination buffer and number of bytes to be received + * @param received number of received bytes is returned here + * @return true if connection is still valid, false if an error occurs and connection needs to be closed + */ +bool network_receive_data( SOCKET sender, void *dest, const uint16 len, uint16 &received, const int timeout_ms ) { received = 0; char *ptr = (char *)dest; @@ -739,16 +798,16 @@ bool network_receive_data(SOCKET sender, void *dest, const uint16 len, uint16 &r do { fd_set fds; FD_ZERO(&fds); - FD_SET(sender, &fds); + FD_SET(sender,&fds); struct timeval tv; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000ul; // can we read? - if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) != 1) { + if( select( FD_SETSIZE, &fds, NULL, NULL, &tv )!=1 ) { return true; } // now receive - int res = recv(sender, ptr + received, len - received, 0); + int res = recv( sender, ptr+received, len-received, 0 ); if (res == -1) { int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { @@ -761,38 +820,35 @@ bool network_receive_data(SOCKET sender, void *dest, const uint16 len, uint16 &r if (res == 0) { // connection closed // output warning / throw fatal error depending on heavy mode setting -#ifdef NETTOOL - const int heavy_mode = 0; -#else - const int heavy_mode = env_t::network_heavy_mode; -#endif - void (log_t::*outfn)(const char*, const char*, ...) = (heavy_mode == 2 ? &log_t::fatal : &log_t::warning); +#ifndef NETTOOL + void (log_t::*outfn)(const char*, const char*, ...) = (env_t::network_heavy_mode == 2 ? &log_t::fatal : &log_t::warning); (dbg->*outfn)("network_receive_data", "Connection [%d] already closed", sender); +#endif return false; } received += res; - } while (received -# undef EWOULDBLOCK -# define EWOULDBLOCK WSAEWOULDBLOCK +# undef EINPROGRESS +# define EINPROGRESS WSAEWOULDBLOCK #else -// beos specific headers + // beos specific headers # ifdef __BEOS__ # include # include -// non-beos / non-windows + // non-beos / non-windows # else # include # include @@ -55,14 +55,18 @@ // all non-windows # include # include -// to keep compatibility to MS windows -typedef int SOCKET; + // to keep compatibility to MS windows + typedef int SOCKET; # define INVALID_SOCKET -1 # define GET_LAST_ERROR() (errno) #endif +#include "../tpl/vector_tpl.h" +#include "../simconst.h" #include "../simtypes.h" #include "../utils/cbuffer_t.h" + + // version of network protocol code #define NETWORK_VERSION (1) @@ -70,30 +74,30 @@ class network_command_t; class gameinfo_t; class karte_t; -void network_close_socket(SOCKET sock); +void network_close_socket( SOCKET sock ); -void network_set_socket_nodelay(SOCKET sock); +void network_set_socket_nodelay( SOCKET sock ); // open a socket or give a decent error message SOCKET network_open_address(char const* cp, char const*& err); // if successful, starts a server on this port -bool network_init_server(int port); +bool network_init_server( int port, const vector_tpl &listen_addrs ); /** -* returns pointer to command or NULL -*/ + * returns pointer to command or NULL + */ network_command_t* network_get_received_command(); /** -* do appropriate action for network games: -* - server: accept connection to a new client -* - all: receive commands and puts them to the received_command_queue -* -* @param timeout in milliseconds -* @return pointer to first received command -* more commands can be obtained by call to network_get_received_command -*/ + * do appropriate action for network games: + * - server: accept connection to a new client + * - all: receive commands and puts them to the received_command_queue + * + * @param timeout in milliseconds + * @return pointer to first received command + * more commands can be obtained by call to network_get_received_command + */ network_command_t* network_check_activity(karte_t *welt, int timeout); /** @@ -112,27 +116,30 @@ network_command_t* network_check_activity(karte_t *welt, int timeout); bool network_send_data( SOCKET dest, const char *buf, const uint16 size, uint16 &count, const int timeout_ms ); /** -* receive data from sender -* @param dest the destination buffer -* @param len length of destination buffer and number of bytes to be received -* @param received number of received bytes is returned here -* @param timeout_ms time-out in milliseconds -* @return true if connection is still valid, false if an error occurs and connection needs to be closed -*/ -bool network_receive_data(SOCKET sender, void *dest, const uint16 len, uint16 &received, const int timeout_ms); + * receive data from sender + * @param dest the destination buffer + * @param len length of destination buffer and number of bytes to be received + * @param received number of received bytes is returned here + * @param timeout_ms time-out in milliseconds + * @return true if connection is still valid, false if an error occurs and connection needs to be closed + */ +bool network_receive_data( SOCKET sender, void *dest, const uint16 len, uint16 &received, const int timeout_ms ); void network_process_send_queues(int timeout); // true, if I can write on the server connection bool network_check_server_connection(); -// send data to all clients (even us) -// nwc is invalid after the call -void network_send_all(network_command_t* nwc, bool exclude_us); +/** + * send command to all clients (even us). + * if @p player_nr is valid, then send only to clients with this player unlocked + * @note nwc is invalid after the call + */ +void network_send_all(network_command_t* nwc, bool exclude_us, uint8 player_nr = PLAYER_UNOWNED); // send data to server only // nwc is invalid after the call -void network_send_server(network_command_t* nwc); +void network_send_server(network_command_t* nwc ); void network_reset_server(); diff --git a/network/network_address.h b/network/network_address.h index 2b3a9a391d1..fdf09344f30 100644 --- a/network/network_address.h +++ b/network/network_address.h @@ -61,7 +61,7 @@ class address_list_t : public vector_tpl { address_list_t() : vector_tpl(10) {} bool contains(const net_address_t &other) { - FOR(address_list_t, const& i, *this) { + for(net_address_t const& i : *this) { if (i.matches(other)) { return true; } diff --git a/network/network_cmd_ingame.cc b/network/network_cmd_ingame.cc index a10b6a07c3a..408a2023330 100644 --- a/network/network_cmd_ingame.cc +++ b/network/network_cmd_ingame.cc @@ -221,7 +221,7 @@ void nwc_nick_t::server_tools(karte_t *welt, uint32 client_id, uint8 what, const // record nickname change for(uint8 i=0; i, &iter, nwc_chg_player_t::company_active_clients[i]) { + for(connection_info_t &iter : nwc_chg_player_t::company_active_clients[i]) { if (iter == info) { iter.nickname = nick; } @@ -507,7 +507,7 @@ bool nwc_ready_t::execute(karte_t *welt) return true; } // check the validity of the map counter - FOR(vector_tpl, const i, all_map_counters) { + for(uint32 const i : all_map_counters) { if (i == map_counter) { // unpause the sender by sending nwc_ready_t back nwc_ready_t nwc(sync_step, map_counter, checklist); @@ -772,7 +772,7 @@ void nwc_sync_t::do_command(karte_t *welt) // ok, now sending game // this sends nwc_game_t - const char *err = network_send_file( client_id, fn ); + const char *err = network_send_file( socket_list_t::get_socket(client_id), fn ); if (err) { dbg->warning("nwc_sync_t::do_command","send game failed with: %s", err); } @@ -1557,7 +1557,7 @@ bool nwc_service_t::execute(karte_t *welt) } // print clients who played for this company uint32 j=0; - FOR(slist_tpl, &iter, nwc_chg_player_t::company_active_clients[i]) { + for(connection_info_t &iter : nwc_chg_player_t::company_active_clients[i]) { if (!detailed && j > 3 && nwc_chg_player_t::company_active_clients[i].get_count() > 5) { buf.printf(" .. and %d more.\n", nwc_chg_player_t::company_active_clients[i].get_count()-j); break; diff --git a/network/network_cmd_scenario.cc b/network/network_cmd_scenario.cc index 4dc34cf9bc4..be395ab5c59 100644 --- a/network/network_cmd_scenario.cc +++ b/network/network_cmd_scenario.cc @@ -52,12 +52,18 @@ bool nwc_scenario_t::execute(karte_t *welt) } script_vm_t *script = scen->script; + if (what == OPEN_SCEN_WIN) { + // open window on server and clients + scen->open_info_win(function); + return true; + } + if (env_t::server) { switch (what) { case CALL_SCRIPT: case CALL_SCRIPT_ANSWER: { // register callback to send result back to client if script is delayed. - script->prepare_callback("nwc_scenario_t_record_result", 2, function, "", socket_list_t::get_client_id( packet->get_sender() ) ); + script->prepare_callback("nwc_scenario_t_record_result", 2, function, (const char *)"", socket_list_t::get_client_id( packet->get_sender() ) ); plainstring res = dynamic_string::fetch_result(function, script, NULL, what==CALL_SCRIPT_ANSWER); // clear callback, in case function call was successfull script->clear_pending_callback(); diff --git a/network/network_cmd_scenario.h b/network/network_cmd_scenario.h index 90bcff013eb..fbd0a793108 100644 --- a/network/network_cmd_scenario.h +++ b/network/network_cmd_scenario.h @@ -27,7 +27,8 @@ class nwc_scenario_t : public network_command_t { UNKNOWN, CALL_SCRIPT, /// client asks for an update CALL_SCRIPT_ANSWER, /// client wants string, server sends string - UPDATE_WON_LOST /// update win/lose flags of the scenario + UPDATE_WON_LOST, /// update win/lose flags of the scenario + OPEN_SCEN_WIN /// open scenario info window }; uint16 what; diff --git a/network/network_cmp_pakset.cc b/network/network_cmp_pakset.cc index 7cbe1463373..0916f83d6c1 100644 --- a/network/network_cmp_pakset.cc +++ b/network/network_cmp_pakset.cc @@ -145,7 +145,7 @@ void network_compare_pakset_with_server(const char* cp, std::string &msg) // ie treatall our paks as if they were not present on the server stringhashtable_tpl addons; { - for(auto const & i : pakset_info_t::get_info()) { + for(auto const& i : pakset_info_t::get_info()) { addons.put(i.key, i.value); } } @@ -256,7 +256,7 @@ void network_compare_pakset_with_server(const char* cp, std::string &msg) msg.append("

"); msg.append(translator::translate("Pak(s) not on server:")); msg.append("


\n"); - for(auto const & i : addons) { + for(auto const& i : addons) { dbg->warning("network_compare_pakset_with_server", "PAK NOT ON SERVER: %s", i.key); msg.append(translator::translate(i.key+3)); msg.append("
\n"); @@ -267,7 +267,7 @@ void network_compare_pakset_with_server(const char* cp, std::string &msg) msg.append("

"); msg.append(translator::translate("Pak(s) different:")); msg.append("


\n"); - FOR(vector_tpl, const& i, different) { + for(const char * const& i : different) { dbg->warning("network_compare_pakset_with_server", "PAK DIFFERENT: %s", i); msg.append(translator::translate(i+3)); // the first three letters are the type ... msg.append("
\n"); @@ -278,7 +278,7 @@ void network_compare_pakset_with_server(const char* cp, std::string &msg) msg.append("

"); msg.append(translator::translate("Pak(s) missing on client:")); msg.append("


\n"); - FOR(vector_tpl, const& i, missing) { + for(const char * const& i : missing) { dbg->warning("network_compare_pakset_with_server", "PAK MISSING: %s", i); msg.append(translator::translate(i+3)); // the first three letters are the type ... msg.append("
\n"); diff --git a/network/network_file_transfer.cc b/network/network_file_transfer.cc index bff596d8b32..0ddc2d56787 100644 --- a/network/network_file_transfer.cc +++ b/network/network_file_transfer.cc @@ -25,7 +25,7 @@ * Functions required by both Simutrans and Nettool */ -char const* network_receive_file( SOCKET const s, char const* const save_as, sint32 const length, sint32 const timeout ) +const char *network_receive_file(const SOCKET src_sock, const char *const save_as, sint32 const length, sint32 const timeout ) { // ok, we have a socket to connect dr_remove(save_as); @@ -45,11 +45,11 @@ char const* network_receive_file( SOCKET const s, char const* const save_as, sin if( timeout > 0 ) { /** 10s for 4096 bytes: * As long as you are not connected with less than 1200 Baud that should be fine - * otherwise upgrade you acustic coppler to 56k ... + * otherwise upgrade your acoustic coupler to 56k ... */ fd_set fds; FD_ZERO(&fds); - FD_SET(s,&fds); + FD_SET(src_sock,&fds); struct timeval tv; // 10 s timeout tv.tv_sec = 10000 / 1000; tv.tv_usec = (10000 % 1000) * 1000ul; @@ -60,7 +60,7 @@ char const* network_receive_file( SOCKET const s, char const* const save_as, sin } } // ok, now here should be something new to read - int i = recv(s, rbuf, length_read + 4096 < length ? 4096 : length - length_read, 0); + int i = recv(src_sock, rbuf, length_read + 4096 < length ? 4096 : length - length_read, 0); if (i > 0) { fwrite(rbuf, 1, i, f); length_read += i; @@ -276,7 +276,7 @@ const char *network_connect(const char *cp, karte_t *world) } -const char *network_send_file( uint32 client_id, const char *filename ) +const char *network_send_file( const SOCKET dst_sock, const char *filename ) { FILE *fp = dr_fopen(filename,"rb"); if (fp == NULL) { @@ -293,8 +293,7 @@ const char *network_send_file( uint32 client_id, const char *filename ) // send size of file nwc_game_t nwc(length); - SOCKET s = socket_list_t::get_socket(client_id); - if (s==INVALID_SOCKET || !nwc.send(s)) { + if (dst_sock==INVALID_SOCKET || !nwc.send(dst_sock)) { goto error; } @@ -305,19 +304,20 @@ const char *network_send_file( uint32 client_id, const char *filename ) while( !feof(fp) ) { int bytes_read = (int)fread( buffer, 1, sizeof(buffer), fp ); uint16 dummy; - if( !network_send_data(s, buffer, bytes_read, dummy, 250) ) { - socket_list_t::remove_client(s); + if( !network_send_data(dst_sock, buffer, bytes_read, dummy, 250) ) { + socket_list_t::remove_client(dst_sock); goto error; } + bytes_sent += bytes_read; ls.set_progress( bytes_sent ); } - } // ok, new client has savegame fclose(fp); return NULL; + error: // an error occurred: close file fclose(fp); diff --git a/network/network_file_transfer.h b/network/network_file_transfer.h index 1dab72c083e..a1d1eb5f923 100644 --- a/network/network_file_transfer.h +++ b/network/network_file_transfer.h @@ -22,13 +22,13 @@ class gameinfo_t; const char *network_gameinfo(const char *cp, gameinfo_t *gi); // connects to server at (cp), receives game, save to client%i-network.sve -const char* network_connect(const char *cp, karte_t *world); +const char *network_connect(const char *cp, karte_t *world); -// sending file over network -const char *network_send_file( uint32 client_id, const char *filename ); +/// Send file over network +const char *network_send_file(const SOCKET dst_sock, const char *filename); -// receive file (directly to disk) -char const* network_receive_file(SOCKET const s, char const* const save_as, const sint32 length, const sint32 timeout=10000 ); +/// Receive file (directly to disk) +const char *network_receive_file(const SOCKET src_sock, const char *const save_as, const sint32 length, const sint32 timeout=10000); /** * Use HTTP POST request to submit poststr to an HTTP server diff --git a/network/network_socket_list.cc b/network/network_socket_list.cc index f100437f2a9..3d8213a4297 100644 --- a/network/network_socket_list.cc +++ b/network/network_socket_list.cc @@ -122,26 +122,26 @@ uint32 socket_list_t::server_sockets; /** * book-keeping for the number of connected / playing clients */ -void socket_list_t::book_state_change(uint8 state, sint8 incr) +void socket_list_t::book_state_change(socket_info_t::connection_state_t state, sint8 incr) { switch (state) { - case socket_info_t::server: - // do not change - break; case socket_info_t::connected: connected_clients += incr; break; case socket_info_t::playing: playing_clients += incr; break; + + // do not change case socket_info_t::inactive: + case socket_info_t::server: default: break; } } -void socket_list_t::change_state(uint32 id, uint8 new_state) +void socket_list_t::change_state(uint32 id, socket_info_t::connection_state_t new_state) { book_state_change(list[id]->state, -1); list[id]->state = new_state; @@ -152,16 +152,12 @@ void socket_list_t::change_state(uint32 id, uint8 new_state) void socket_list_t::reset() { - FOR(vector_tpl, const i, list) { + for(socket_info_t* const i : list) { i->reset(); } connected_clients = 0; playing_clients = 0; server_sockets = 0; -#ifndef NETTOOL - // Knightly : clear the limit sets - nwc_routesearch_t::reset(); -#endif } @@ -172,10 +168,6 @@ void socket_list_t::reset_clients() } connected_clients = 0; playing_clients = 0; -#ifndef NETTOOL - // Knightly : clear the limit sets - nwc_routesearch_t::reset(); -#endif } @@ -257,10 +249,7 @@ bool socket_list_t::remove_client( SOCKET sock ) change_state(j, socket_info_t::inactive); } list[j]->reset(); -#ifndef NETTOOL - // Knightly : remove the corresponding limit set - nwc_routesearch_t::remove_client_entry(j); -#endif + network_close_socket(sock); return true; } @@ -321,14 +310,17 @@ void socket_list_t::unlock_player_all(uint8 player_nr, bool unlock, uint32 excep } -void socket_list_t::send_all(network_command_t* nwc, bool only_playing_clients) +void socket_list_t::send_all(network_command_t* nwc, bool only_playing_clients, uint8 player_nr) { if (nwc == NULL) { return; } for(uint32 i=server_sockets; iis_active() && list[i]->socket!=INVALID_SOCKET - && (!only_playing_clients || list[i]->state == socket_info_t::playing || list[i]->state == socket_info_t::connected)) { + && (!only_playing_clients || list[i]->state == socket_info_t::playing || list[i]->state == socket_info_t::connected) + && (player_nr >= PLAYER_UNOWNED || list[i]->is_player_unlocked(player_nr)) + ) { + packet_t *p = nwc->copy_packet(); list[i]->send_queue_append(p); } @@ -339,7 +331,7 @@ void socket_list_t::send_all(network_command_t* nwc, bool only_playing_clients) SOCKET socket_list_t::fill_set(fd_set *fds) { SOCKET s_max = 0; - FOR(vector_tpl, const i, list) { + for(socket_info_t* const i : list) { if (i->state != socket_info_t::inactive && i->socket != INVALID_SOCKET) { SOCKET const s = i->socket; s_max = max( s, s_max ); @@ -404,8 +396,12 @@ void socket_list_t::rdwr(packet_t *p, vector_tpl *list) if (p->is_loading()) { list->append(new socket_info_t()); } - p->rdwr_byte((*list)[i]->state); - if ( (*list)[i]->state==socket_info_t::playing) { + + uint8 s = (*list)[i]->state; + p->rdwr_byte(s); + (*list)[i]->state = socket_info_t::connection_state_t(s); + + if ( s==socket_info_t::playing) { (*list)[i]->rdwr(p); } } diff --git a/network/network_socket_list.h b/network/network_socket_list.h index 014602b8ace..d2e38f8dc67 100644 --- a/network/network_socket_list.h +++ b/network/network_socket_list.h @@ -9,10 +9,10 @@ #include "network.h" #include "network_address.h" +#include "../simconst.h" #include "../tpl/slist_tpl.h" #include "../tpl/vector_tpl.h" #include "../utils/plainstring.h" -#include "../simconst.h" class network_command_t; class packet_t; @@ -45,25 +45,29 @@ class connection_info_t { }; -class socket_info_t : public connection_info_t { +class socket_info_t : public connection_info_t +{ +public: + enum connection_state_t + { + inactive = 0, ///< client disconnected + server = 1, ///< server socket + connected = 2, ///< connection established but client does not participate in the game yet + playing = 3, ///< client actively plays + has_left = 4, ///< was playing but left + admin = 5 ///< admin connection + }; + private: packet_t *packet; slist_tpl send_queue; - public: - enum { - inactive = 0, // client disconnected - server = 1, // server socket - connected = 2, // connection established but client does not participate in the game yet - playing = 3, // client actively plays - has_left = 4, // was playing but left - admin = 5 // admin connection - }; - uint8 state; - + connection_state_t state; SOCKET socket; + uint16 player_unlocked; +public: socket_info_t() : connection_info_t(), packet(0), send_queue(), state(inactive), socket(INVALID_SOCKET), player_unlocked(0) {} ~socket_info_t(); @@ -95,8 +99,6 @@ class socket_info_t : public connection_info_t { */ void rdwr(packet_t *p); - - uint16 player_unlocked; /** * human players on this connection can play with in-game companies/players? */ @@ -183,10 +185,11 @@ class socket_list_t { /** * send command to all clients * @param only_playing_clients if true then send only to playing clients + * @param player_nr if != PLAYER_UNOWNED then only send to clients with this player unlocked */ - static void send_all(network_command_t* nwc, bool only_playing_clients); + static void send_all(network_command_t* nwc, bool only_playing_clients, uint8 player_nr = PLAYER_UNOWNED); - static void change_state(uint32 id, uint8 new_state); + static void change_state(uint32 id, socket_info_t::connection_state_t new_state); /** * rdwr client-list information to packet @@ -194,7 +197,7 @@ class socket_list_t { static void rdwr(packet_t *p, vector_tpl *writeto=&list); private: - static void book_state_change(uint8 state, sint8 incr); + static void book_state_change(socket_info_t::connection_state_t state, sint8 incr); public: // from now stuff to deal with fd_set's diff --git a/network/pakset_info.cc b/network/pakset_info.cc index aead4a74498..b249f10876c 100644 --- a/network/pakset_info.cc +++ b/network/pakset_info.cc @@ -59,7 +59,7 @@ void pakset_info_t::calculate_checksum() sorted.insert_ordered(entry_t(i.key, i.value), entry_cmp); } // now loop - FOR(vector_tpl, const& i, sorted) { + for(entry_t const& i : sorted) { i.chk->calc_checksum(&general); } general.finish(); diff --git a/obj/gebaeude.cc b/obj/gebaeude.cc index ec5c5a02a44..656f086c3cf 100644 --- a/obj/gebaeude.cc +++ b/obj/gebaeude.cc @@ -327,7 +327,7 @@ void gebaeude_t::check_road_tiles(bool del) } } - FOR(vector_tpl, gb, building_list) + for(gebaeude_t* gb : building_list) { for (uint8 i = 0; i < 8; i++) { @@ -792,6 +792,32 @@ bool gebaeude_t::is_signalbox() const } +uint32 gebaeude_t::get_tile_list( vector_tpl &list ) const +{ + koord size = get_tile()->get_desc()->get_size( get_tile()->get_layout() ); + const koord3d pos0 = get_pos() - get_tile()->get_offset(); // get origin + koord k; + + list.clear(); + + // add all tiles + for( k.y = 0; k.y < size.y; k.y++ ) { + for( k.x = 0; k.x < size.x; k.x++ ) { + if( grund_t* gr = welt->lookup( pos0+k ) ) { + if( gebaeude_t* const add_gb = gr->find() ) { + if( is_same_building( add_gb ) ) { + list.append( gr ); + } + } + } + } + } + + return list.get_count(); +} + + + void gebaeude_t::show_info() { if (get_fabrik()) { diff --git a/obj/gebaeude.h b/obj/gebaeude.h index 21bf7cae6d2..50b60124867 100644 --- a/obj/gebaeude.h +++ b/obj/gebaeude.h @@ -227,6 +227,10 @@ class gebaeude_t : public obj_t, sync_steppable bool is_signalbox() const; + /// fills vector with a list of all tiles with this building + /// @return number of actual tiles + uint32 get_tile_list( vector_tpl& list ) const; + /// @copydoc obj_t::info void info(cbuffer_t & buf) const OVERRIDE; diff --git a/obj/groundobj.cc b/obj/groundobj.cc index 604ffd41ff9..6e6176453fc 100644 --- a/obj/groundobj.cc +++ b/obj/groundobj.cc @@ -141,7 +141,7 @@ const groundobj_desc_t *groundobj_t::random_groundobj_for_climate(climate_bits c } int weight = 0; - FOR( vector_tpl, const i, groundobj_typen ) { + for(groundobj_desc_t const* const i : groundobj_typen ) { if( i->is_allowed_climate_bits(cl) && (slope == slope_t::flat || (i->get_phases() >= slope && i->get_image_id(0,slope)!=IMG_EMPTY ) ) ) { weight += i->get_distribution_weight(); } @@ -151,7 +151,7 @@ const groundobj_desc_t *groundobj_t::random_groundobj_for_climate(climate_bits c if( weight > 0 ) { const int w=simrand(weight, "const groundobj_desc_t *groundobj_t::random_groundobj_for_climate(climate_bits cl, slope_t::type slope )"); weight = 0; - FOR(vector_tpl, const i, groundobj_typen) { + for(groundobj_desc_t const* const i : groundobj_typen) { if( i->is_allowed_climate_bits(cl) && (slope == slope_t::flat || (i->get_phases() >= slope && i->get_image_id(0,slope)!=IMG_EMPTY ) ) ) { weight += i->get_distribution_weight(); if(weight>=w) { diff --git a/obj/leitung2.cc b/obj/leitung2.cc index b1a45b81739..28fcaa9cf86 100644 --- a/obj/leitung2.cc +++ b/obj/leitung2.cc @@ -553,7 +553,7 @@ void pumpe_t::new_world() void pumpe_t::step_all(uint32 delta_t) { - FOR(slist_tpl, const p, pumpe_list) { + for(pumpe_t* const p : pumpe_list) { p->step(delta_t); } } @@ -687,7 +687,7 @@ void senke_t::new_world() void senke_t::step_all(uint32 delta_t) { - FOR(slist_tpl, const s, senke_list) { + for(senke_t* const s : senke_list) { s->step(delta_t); } } @@ -751,9 +751,8 @@ senke_t::~senke_t() if(city && !welt->is_destroying()) { city->remove_substation(this); - FOR(vector_tpl, factory, city->get_city_factories()) - { - factory->set_transformer_connected( NULL ); + for(fabrik_t* city_fab : city->get_city_factories()) { + city_fab->set_transformer_connected( NULL ); } } } @@ -798,8 +797,7 @@ void senke_t::step(uint32 delta_t) if(city) { - FOR(vector_tpl, city_fab, city->get_city_factories()) - { + for(fabrik_t* city_fab : city->get_city_factories()) { if(city_fab->get_desc()->is_electricity_producer()) { continue; @@ -824,7 +822,7 @@ void senke_t::step(uint32 delta_t) uint64 supply; vector_tpl checked_substations; - FOR(vector_tpl, substation, *city_substations) + for(senke_t* substation : *city_substations) { // Must use two passes here: first, check all those that don't have enough to supply // an equal share, then check those that do. @@ -850,16 +848,16 @@ void senke_t::step(uint32 delta_t) uint32 demand_distribution; uint8 count = 0; - FOR(vector_tpl, sub, *city_substations) + for(senke_t* substation : *city_substations) { // Now check those that have more than enough power. - if(sub == this || checked_substations.is_contained(sub)) + if(substation == this || checked_substations.is_contained(substation)) { continue; } - supply = sub->get_power_load(); + supply = substation->get_power_load(); demand_distribution = shared_power_demand / (city_substations_number - count); if(supply < demand_distribution) { @@ -926,8 +924,7 @@ void senke_t::step(uint32 delta_t) if(city) { // Everyone else splits power on a proportional basis -- brownouts! - FOR(vector_tpl, factory, city->get_city_factories()) - { + for(fabrik_t* factory : city->get_city_factories()) { //city_factories[i]->set_transformer_connected(this); const uint32 current_factory_demand = (factory->step_power_demand() * load_proportion) / 100; const uint32 current_factory_load = municipal_power_demand == 0 ? current_factory_demand : diff --git a/obj/roadsign.cc b/obj/roadsign.cc index 650eada0466..1d505a28140 100644 --- a/obj/roadsign.cc +++ b/obj/roadsign.cc @@ -1105,7 +1105,7 @@ void roadsign_t::fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint1 matching.insert_ordered( desc, compare_roadsign_desc ); } } - FOR(vector_tpl, const i, matching) { + for(roadsign_desc_t const* const i : matching) { tool_selector->add_tool_selector(i->get_builder()); } } @@ -1163,7 +1163,7 @@ const roadsign_desc_t* roadsign_t::find_best_upgrade(bool underground) void roadsign_t::set_scale(uint16 scale_factor) { // Called from the world's set_scale method so as to avoid having to export the internal data structures of this class. - FOR(vector_tpl, sign, list) + for(roadsign_desc_t* sign : list) { sign->set_scale(scale_factor); } diff --git a/obj/wayobj.cc b/obj/wayobj.cc index ed5ae97a337..658fc794acc 100644 --- a/obj/wayobj.cc +++ b/obj/wayobj.cc @@ -595,7 +595,7 @@ void wayobj_t::fill_menu(tool_selector_t *tool_selector, waytype_t wtyp, sint16 } // sort the tools before adding to menu std::sort(matching.begin(), matching.end(), compare_wayobj_desc); - FOR(vector_tpl, const i, matching) { + for(way_obj_desc_t const* const i : matching) { tool_selector->add_tool_selector(i->get_builder()); } } diff --git a/obj/wolke.cc b/obj/wolke.cc index 1a1ec6ac68e..ea2dd3354d9 100644 --- a/obj/wolke.cc +++ b/obj/wolke.cc @@ -21,7 +21,7 @@ vector_tplwolke_t::all_clouds(0); bool wolke_t::register_desc(const skin_desc_t* desc) { // avoid duplicates with same name - FOR(vector_tpl, & i, all_clouds) { + for(skin_desc_t const* & i : all_clouds) { if (strcmp(i->get_name(), desc->get_name()) == 0) { i = desc; return true; diff --git a/old_blockmanager.cc b/old_blockmanager.cc index 7393540554e..1bf2a376f27 100644 --- a/old_blockmanager.cc +++ b/old_blockmanager.cc @@ -160,7 +160,7 @@ void old_blockmanager_t::finish_rd(karte_t *welt) uint8 directions=0; waytype_t wt=gr->hat_weg(track_wt) ? track_wt : monorail_wt; if( gr->get_neighbour(to,wt,os1->get_dir()) ) { - FOR(slist_tpl, const s, signale) { + for(oldsignal_t* const s : signale) { if (s->get_pos() == to->get_pos()) { os2 = s; break; diff --git a/player/finance.h b/player/finance.h index 2708292aa4e..6e8e4910753 100644 --- a/player/finance.h +++ b/player/finance.h @@ -577,7 +577,7 @@ class finance_t { /** * @returns maintenance - * @param tt transport type (Truck, Ship Air, ...) + * @param tt transport type (Truck, Ship Aircraft, ...) */ sint64 get_maintenance(transport_type tt=TT_ALL) const { assert(ttget_viewport(); - FOR(slist_tpl, const m, messages) { + for(income_message_t* const m : messages) { const scr_coord scr_pos = vp->get_screen_coord(koord3d(m->pos,welt->lookup_hgt(m->pos)),koord(0,m->alter >> 4)) + scr_coord((get_tile_raster_width()-display_calc_proportional_string_len_width(m->str, 0x7FFF))/2,0); @@ -359,6 +359,10 @@ void player_t::set_player_color_no_message(uint8 col1, uint8 col2) player_color_1 = col1; player_color_2 = col2; display_set_player_color_scheme( player_nr, col1, col2 ); + // update player window + if (ki_kontroll_t* frame = dynamic_cast(win_get_magic(magic_ki_kontroll_t))) { + frame->update_data(); + } // update player ranking window if (player_ranking_gui_t *frame = dynamic_cast( win_get_magic(magic_player_ranking) ) ) { frame->update_buttons(); @@ -371,7 +375,7 @@ void player_t::step() { /* NOTE: This would need updating to the new FOR iterators to work now. - // die haltestellen m�Esen die Fahrpl�ne rgelmaessig pruefen + // die haltestellen m?Esen die Fahrpl?ne rgelmaessig pruefen uint8 i = (uint8)(welt->get_steps()+player_nr); //slist_iterator_tpl iter( halt_list ); //while(iter.next()) { @@ -579,7 +583,7 @@ void player_t::calc_assets() assets[i] = 0; } // all convois - FOR(vector_tpl, const cnv, welt->convoys()) { + for(convoihandle_t const cnv : welt->convoys()) { if( cnv->get_owner() == this ) { sint64 restwert = cnv->calc_sale_value(); assets[TT_ALL] += restwert; @@ -588,9 +592,9 @@ void player_t::calc_assets() } // all vehicles stored in depot not part of a convoi - FOR(slist_tpl, const depot, depot_t::get_depot_list()) { + for(depot_t* const depot : depot_t::get_depot_list()) { if( depot->get_owner_nr() == player_nr ) { - FOR(slist_tpl, const veh, depot->get_vehicle_list()) { + for(vehicle_t* const veh : depot->get_vehicle_list()) { sint64 restwert = veh->calc_sale_value(); assets[TT_ALL] += restwert; assets[finance->translate_waytype_to_tt(veh->get_waytype())] += restwert; @@ -693,7 +697,7 @@ void player_t::complete_liquidation() // remove all stops // first generate list of our stops slist_tpl halt_list; - FOR(vector_tpl, const halt, haltestelle_t::get_alle_haltestellen()) { + for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if( halt->get_owner()==this ) { halt_list.append(halt); } @@ -705,10 +709,10 @@ void player_t::complete_liquidation() } // transfer all ways in public stops belonging to me to no one - FOR(vector_tpl, const halt, haltestelle_t::get_alle_haltestellen()) { + for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if( halt->get_owner()==welt->get_public_player() ) { // only concerns public stops tiles - FOR(slist_tpl, const& i, halt->get_tiles()) { + for(haltestelle_t::tile_t const& i : halt->get_tiles()) { grund_t const* const gr = i.grund; for( uint8 wnr=0; wnr<2; wnr++ ) { weg_t *w = gr->get_weg_nr(wnr); @@ -1162,7 +1166,7 @@ sint64 player_t::undo() return false; } // check, if we can still do undo - FOR(vector_tpl, const& i, last_built) { + for(koord3d const& i : last_built) { grund_t* const gr = welt->lookup(i); if(gr==NULL || gr->get_typ()!=grund_t::boden) { // well, something was built here ... so no undo @@ -1209,7 +1213,7 @@ sint64 player_t::undo() // ok, now remove everything last built sint64 cost=0; - FOR(vector_tpl, const& i, last_built) { + for(koord3d const& i : last_built) { grund_t* const gr = welt->lookup(i); if( undo_type != powerline_wt ) { cost += gr->weg_entfernen(undo_type,true); @@ -1430,7 +1434,7 @@ void player_t::take_over(player_t* target_player) // Transfer vehicles in a depot not in a convoy, then fall through dep = (depot_t*)obj; - FOR(slist_tpl, vehicle, dep->get_vehicle_list()) + for(vehicle_t* vehicle : dep->get_vehicle_list()) { if (vehicle->get_owner() == target_player) { @@ -1475,7 +1479,7 @@ void player_t::take_over(player_t* target_player) // Transfer stops // Adapted from the liquidation algorithm slist_tpl halt_list; - FOR(vector_tpl, const halt, haltestelle_t::get_alle_haltestellen()) + for(halthandle_t const halt : haltestelle_t::get_alle_haltestellen()) { if (halt->get_owner() == target_player) { @@ -1503,7 +1507,7 @@ void player_t::take_over(player_t* target_player) } } - FOR(vector_tpl, line, lines_to_transfer) + for(linehandle_t const line : lines_to_transfer) { line->set_owner(this); target_player->simlinemgmt.deregister_line(line); diff --git a/player/simplay.h b/player/simplay.h index 04332903171..7085c4e3504 100644 --- a/player/simplay.h +++ b/player/simplay.h @@ -79,7 +79,7 @@ class player_t void operator delete(void *p); }; - slist_tplmessages; + slist_tpl messages; /** * Creates new income message entry or merges with existing one if the diff --git a/script/api/Doxyfile_SQAPI b/script/api/Doxyfile_SQAPI index 8bd00c617c8..5d3262b816f 100644 --- a/script/api/Doxyfile_SQAPI +++ b/script/api/Doxyfile_SQAPI @@ -1,5 +1,5 @@ # -# This file is part of the Simutrans-Extended project under the Artistic License. +# This file is part of the Simutrans project under the Artistic License. # (see LICENSE.txt) # @@ -16,6 +16,7 @@ INPUT = api_factory.cc \ api_skeleton.h \ api_skeleton.cc \ api_city.cc \ + api_command.cc \ api_const.cc \ api_control.cc \ api_convoy.cc \ @@ -25,6 +26,7 @@ INPUT = api_factory.cc \ api_line.cc \ api_map_objects.cc \ api_obj_desc.cc \ + api_pathfinding.cc \ api_player.cc \ api_schedule.cc \ api_settings.cc \ diff --git a/script/api/api.h b/script/api/api.h index 78ff99c72bd..76193311d5a 100644 --- a/script/api/api.h +++ b/script/api/api.h @@ -10,23 +10,27 @@ /** @file api.h declarations of export functions. */ #include "../../squirrel/squirrel.h" +#include "api_command.h" void export_city(HSQUIRRELVM vm); void export_control(HSQUIRRELVM vm); void export_convoy(HSQUIRRELVM vm); void export_factory(HSQUIRRELVM vm); void export_goods_desc(HSQUIRRELVM vm); -void export_gui(HSQUIRRELVM vm); +void export_gui(HSQUIRRELVM vm, bool scenario); void export_halt(HSQUIRRELVM vm); void export_line(HSQUIRRELVM vm); void export_map_objects(HSQUIRRELVM vm); -void export_player(HSQUIRRELVM vm); +void export_player(HSQUIRRELVM vm, bool scenario); void export_scenario(HSQUIRRELVM vm); void export_settings(HSQUIRRELVM vm); void export_schedule(HSQUIRRELVM vm); void export_simple(HSQUIRRELVM vm); +void export_string_methods(HSQUIRRELVM vm); // api_scenario.cc void export_tiles(HSQUIRRELVM vm); -void export_world(HSQUIRRELVM vm); +void export_world(HSQUIRRELVM vm, bool scenario); + +void export_pathfinding(HSQUIRRELVM vm); void export_global_constants(HSQUIRRELVM vm); diff --git a/script/api/api_city.cc b/script/api/api_city.cc index e6758236b65..64bb40ee6ad 100644 --- a/script/api/api_city.cc +++ b/script/api/api_city.cc @@ -42,28 +42,42 @@ SQInteger world_get_next_city(HSQUIRRELVM vm) return generic_get_next(vm, welt->get_cities().get_count()); } +namespace script_api { + declare_fake_param(city_list_t, "city_list_x"); +} + +stadt_t* world_get_city_by_index(city_list_t, uint32 index) +{ + return index < welt->get_cities().get_count() ? welt->get_cities()[index] : NULL; +} -SQInteger world_get_city_by_index(HSQUIRRELVM vm) +call_tool_init set_citygrowth(stadt_t *city, bool allow) { - sint32 index = param::get(vm, -1); - koord pos = (0<=index && (uint32)indexget_cities().get_count()) ? welt->get_cities()[index]->get_pos() : koord::invalid; - // transform coordinates - welt->get_scenario()->koord_w2sq(pos); - return push_instance(vm, "city_x", pos.x, pos.y); + cbuffer_t buf; + buf.printf("g%hi,%hi,%hi", city->get_pos().x, city->get_pos().y, (short)allow ); + return call_tool_init(TOOL_CHANGE_CITY | SIMPLE_TOOL, (const char*)buf, 0, welt->get_public_player()); } -static script_api::void_t set_citygrowth(stadt_t *city, bool allow) +call_tool_init city_set_name(stadt_t* city, const char* name) +{ + return command_rename(welt->get_public_player(), 't', welt->get_cities().index_of(city), name); +} + + +call_tool_work city_change_size(stadt_t *city, sint32 delta) { - static char param[16]; - sprintf(param,"g%hi,%hi,%hi", city->get_pos().x, city->get_pos().y, (short)allow ); - tool_t *tool = tool_t::simple_tool[TOOL_CHANGE_CITY]; - tool->set_default_param( param ); - tool->flags |= tool_t::WFL_SCRIPT; - welt->set_tool( tool, welt->get_public_player() ); - tool->flags &= ~tool_t::WFL_SCRIPT; - return script_api::void_t(); + cbuffer_t buf; + buf.printf("%i", delta); + grund_t *gr = welt->lookup_kartenboden(city->get_pos()); + if (gr) { + return call_tool_work(TOOL_CHANGE_CITY_SIZE | GENERAL_TOOL, (const char*)buf, 0, welt->get_public_player(), gr->get_pos()); + } + else { + return "Invalid coordinate."; + } } + void export_city(HSQUIRRELVM vm) { /** @@ -85,13 +99,13 @@ void export_city(HSQUIRRELVM vm) /** * Meta-method to be used in foreach loops. Do not call them directly. */ - register_function(vm, world_get_city_by_index, "_get", 2, "xi"); + register_method(vm, &world_get_city_by_index, "_get", true); end_class(vm); /** * Class to access cities. */ - begin_class(vm, "city_x", "extend_get,coord"); + begin_class(vm, "city_x", "extend_get,coord,ingame_object"); /** * Constructor. @@ -99,114 +113,103 @@ void export_city(HSQUIRRELVM vm) * @param y y-coordinate * @typemask (integer,integer) */ - // actually defined simutrans/script/scenario_base.nut + // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Return name of city * @returns name */ register_method(vm, &stadt_t::get_name, "get_name"); /** - * Get monthly statistics of number of citizens. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_city_stat, "get_citizens", freevariable2(true, HIST_CITIZENS), true); - /** - * Get monthly statistics of number of citizens. - * @returns array, index [0] corresponds to current month + * Change city name. + * @ingroup rename_func */ - register_method_fv(vm, &get_city_stat, "get_jobs", freevariable2(true, HIST_JOBS), true); + register_method(vm, &city_set_name, "set_name", true); /** * Get monthly statistics of number of citizens. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_visitor_demand", freevariable2(true, HIST_VISITOR_DEMAND), true); + register_method_fv(vm, &get_city_stat, "get_citizens", freevariable(true, HIST_CITIZENS), true); /** * Get monthly statistics of number of city growth. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_growth", freevariable2(true, HIST_GROWTH), true ); + register_method_fv(vm, &get_city_stat, "get_growth", freevariable(true, HIST_GROWTH), true ); /** * Get monthly statistics of number of buildings. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_buildings", freevariable2(true, HIST_BUILDING), true ); + register_method_fv(vm, &get_city_stat, "get_buildings", freevariable(true, HIST_BUILDING), true ); /** * Get monthly statistics of number of citycars started. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_citycars", freevariable2(true, HIST_CITYCARS), true ); + register_method_fv(vm, &get_city_stat, "get_citycars", freevariable(true, HIST_CITYCARS), true ); /** * Get monthly statistics of number of transported passengers. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_transported_pax", freevariable2(true, HIST_PAS_TRANSPORTED), true ); + register_method_fv(vm, &get_city_stat, "get_transported_pax", freevariable(true, HIST_PAS_TRANSPORTED), true ); /** * Get monthly statistics of number of generated passengers. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_generated_pax", freevariable2(true, HIST_PAS_GENERATED), true ); + register_method_fv(vm, &get_city_stat, "get_generated_pax", freevariable(true, HIST_PAS_GENERATED), true ); /** * Get monthly statistics of number of transported mail. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_transported_mail", freevariable2(true, HIST_MAIL_TRANSPORTED), true ); + register_method_fv(vm, &get_city_stat, "get_transported_mail", freevariable(true, HIST_MAIL_TRANSPORTED), true ); /** * Get monthly statistics of number of generated mail. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_city_stat, "get_generated_mail", freevariable2(true, HIST_MAIL_GENERATED), true ); - /** - * Get per year statistics of number of citizens. - * @returns array, index [0] corresponds to current year - */ - register_method_fv(vm, &get_city_stat, "get_year_citizens", freevariable2(false, HIST_CITIZENS), true ); + register_method_fv(vm, &get_city_stat, "get_generated_mail", freevariable(true, HIST_MAIL_GENERATED), true ); /** * Get per year statistics of number of citizens. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_jobs", freevariable2(false, HIST_JOBS), true ); - /** - * Get per year statistics of number of citizens. - * @returns array, index [0] corresponds to current year - */ - register_method_fv(vm, &get_city_stat, "get_year_visitor_demand", freevariable2(false, HIST_VISITOR_DEMAND), true ); + register_method_fv(vm, &get_city_stat, "get_year_citizens", freevariable(false, HIST_CITIZENS), true ); /** * Get per year statistics of number of city growth. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_growth", freevariable2(false, HIST_GROWTH), true ); + register_method_fv(vm, &get_city_stat, "get_year_growth", freevariable(false, HIST_GROWTH), true ); /** * Get per year statistics of number of buildings. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_buildings", freevariable2(false, HIST_BUILDING), true ); + register_method_fv(vm, &get_city_stat, "get_year_buildings", freevariable(false, HIST_BUILDING), true ); /** * Get per year statistics of number of citycars started. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_citycars", freevariable2(false, HIST_CITYCARS), true ); + register_method_fv(vm, &get_city_stat, "get_year_citycars", freevariable(false, HIST_CITYCARS), true ); /** * Get per year statistics of number of transported passengers. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_transported_pax", freevariable2(false, HIST_PAS_TRANSPORTED), true ); + register_method_fv(vm, &get_city_stat, "get_year_transported_pax", freevariable(false, HIST_PAS_TRANSPORTED), true ); /** * Get per year statistics of number of generated passengers. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_generated_pax", freevariable2(false, HIST_PAS_GENERATED), true ); + register_method_fv(vm, &get_city_stat, "get_year_generated_pax", freevariable(false, HIST_PAS_GENERATED), true ); /** * Get per year statistics of number of transported mail. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_transported_mail", freevariable2(false, HIST_MAIL_TRANSPORTED), true ); + register_method_fv(vm, &get_city_stat, "get_year_transported_mail", freevariable(false, HIST_MAIL_TRANSPORTED), true ); /** * Get per year statistics of number of generated mail. * @returns array, index [0] corresponds to current year */ - register_method_fv(vm, &get_city_stat, "get_year_generated_mail", freevariable2(false, HIST_MAIL_GENERATED), true ); + register_method_fv(vm, &get_city_stat, "get_year_generated_mail", freevariable(false, HIST_MAIL_GENERATED), true ); /** * Check city growth allowance. @@ -215,8 +218,8 @@ void export_city(HSQUIRRELVM vm) register_method(vm, &stadt_t::get_citygrowth, "get_citygrowth_enabled"); /** - * Position of townhall. - * @returns townhall position + * Position of town-hall. + * @returns town-hall position */ register_method(vm, &stadt_t::get_pos, "get_pos"); @@ -243,20 +246,15 @@ void export_city(HSQUIRRELVM vm) /** * Change city size. City will immediately grow. * @param delta City size will change by this number. - * @warning cannot be used in network games. + * @ingroup game_cmd */ - register_method_fv(vm, &stadt_t::change_size, "change_size", freevariable(false)); + register_method(vm, city_change_size, "change_size", true); /** * Enable or disable city growth. + * @ingroup game_cmd */ register_method(vm, &set_citygrowth, "set_citygrowth_enabled", true); - /** - * Change city name. - * @warning cannot be used in network games. - */ - register_method(vm, &stadt_t::set_name, "set_name"); - end_class(vm); } diff --git a/script/api/api_command.cc b/script/api/api_command.cc index 4e63538c307..8699b1050f2 100644 --- a/script/api/api_command.cc +++ b/script/api/api_command.cc @@ -8,13 +8,20 @@ /** @file api_command.cc exports the command_x class, which encodes the tools to manipulate a game */ #include "api_command.h" +#include "api_obj_desc_base.h" +#include "api_simple.h" #include "../api_param.h" #include "../api_class.h" #include "../api_function.h" #include "../../simmenu.h" +#include "../../simtool.h" #include "../../simworld.h" #include "../../dataobj/environment.h" #include "../../script/script.h" +#include "../../descriptor/bridge_desc.h" +#include "../../descriptor/building_desc.h" +#include "../../descriptor/roadsign_desc.h" +#include "../../descriptor/way_obj_desc.h" #include // auto_ptr @@ -26,22 +33,32 @@ SQInteger command_constructor(HSQUIRRELVM vm) // create tool uint16 id = param::get(vm, 2); - if (tool_t *tool = create_tool(id)) { - my_tool_t* mtool = new my_tool_t(tool); - attach_instance(vm, 1, mtool); - return 0; + if (id & GENERAL_TOOL) { + // we do not want scripts to open dialogues or quitting the game etc + + if (id == (TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL)) { + // do not create & attach instance, will be done separately + return 0; + } + else { + if (tool_t *tool = create_tool(id)) { + my_tool_t* mtool = new my_tool_t(tool); + attach_instance(vm, 1, mtool); + return 0; + } + } } - return -1; + return sq_raise_error(vm, "Invalid tool called (%d / 0x%x)", id & 0xfff, id); } SQInteger param::push(HSQUIRRELVM vm, call_tool_init v) { if (v.error) { - return sq_raise_error(vm, *v.error ? v.error : "Strange error occured"); + return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred"); } // create tool, if necessary, delete on exit - std::auto_ptr our_tool; + std::unique_ptr our_tool; tool_t *tool = v.tool; if (tool == NULL) { our_tool.reset(v.create_tool()); @@ -74,10 +91,11 @@ SQInteger param::push(HSQUIRRELVM vm, call_tool_init v) tool->callback_id = callback_id; // HACK call karte_t::set_tool - welt->set_tool(tool, player); + bool suspended; + welt->set_tool_api(tool, player, suspended, true); // in networkmode, call is suspended - if (env_t::networkmode) { + if (suspended) { // register for wakeup suspended_scripts_t::register_suspended_script(callback_id, vm); // suspend vm for now, after wakeup it returns to the script @@ -135,7 +153,7 @@ SQInteger command_work(HSQUIRRELVM vm) bool twoclick = top>4; // save & set default_param - my_tool_t *mtool = get_attached_instance(vm, 1, (void*)param::get); + my_tool_t *mtool = get_attached_instance(vm, 1, param::tag()); if (mtool == NULL) { return sq_raise_error(vm, "Called from an instance different to tool_x"); } @@ -165,10 +183,10 @@ tool_t * call_tool_base_t::create_tool() SQInteger param::push(HSQUIRRELVM vm, call_tool_work v) { if (v.error) { - return sq_raise_error(vm, *v.error ? v.error : "Strange error occured"); + return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred"); } // create tool, if necessary, delete on exit - std::auto_ptr our_tool; + std::unique_ptr our_tool; tool_t *tool = v.tool; if (tool == NULL) { our_tool.reset(v.create_tool()); @@ -220,13 +238,12 @@ SQInteger param::push(HSQUIRRELVM vm, call_tool_work v) return sq_raise_error(vm, "Cannot call this tool from within `%s'.", blocker); } - bool suspended = false; const char* err = NULL; // first click of two_click_tool_t if (v.twoclick) { - err = welt->call_work(tool, player, v.start, suspended); + err = welt->call_work_api(tool, player, v.start, suspended, true); assert(!suspended); } // call the work that has effects @@ -234,7 +251,7 @@ SQInteger param::push(HSQUIRRELVM vm, call_tool_work v) // register this tool call for callback with this id uint32 callback_id = suspended_scripts_t::get_unique_key(tool); tool->callback_id = callback_id; - err = welt->call_work(tool, player, v.twoclick ? v.end : v.start, suspended); + err = welt->call_work_api(tool, player, v.twoclick ? v.end : v.start, suspended, true); if (suspended) { // register for wakeup @@ -253,11 +270,10 @@ uint8 get_flags(tool_t *tool) return tool->flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); } -// FIXME: For reasons beyond fathom, this will not compile. -void_t set_flags(tool_t *tool, uint8 flags) + +void set_flags(tool_t *tool, uint8 flags) { tool->flags = flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL); - return void_t(); } @@ -267,6 +283,162 @@ void* script_api::param::tag() } +const char* is_available(const obj_desc_timelined_t* desc) +{ + return desc->is_available(welt->get_timeline_year_month()) ? NULL : "Object not available (retired or future)."; +} + + +call_tool_work build_way(player_t* pl, koord3d start, koord3d end, const way_desc_t* way, bool straight, bool keep_city_roads) +{ + if (way == NULL) { + return call_tool_work("No way provided"); + } + if (const char* err = is_available(way)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_WAY | GENERAL_TOOL, way->get_name(), (straight ? 2 : 0) + (keep_city_roads ? 1 : 0), pl, start, end); +} + + +call_tool_work build_wayobj(player_t* pl, koord3d start, koord3d end, const way_obj_desc_t* wayobj) +{ + if (wayobj == NULL) { + return call_tool_work("No wayobj provided"); + } + if (const char* err = is_available(wayobj)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wayobj->get_name(), 0, pl, start, end); +} + + +typedef call_tool_work(*bsr_type)(player_t*, koord3d, const building_desc_t*, my_ribi_t); + +call_tool_work build_station_rotation(player_t* pl, koord3d pos, const building_desc_t* building, my_ribi_t rotation) +{ + // rotation: SENW -> 0123, see station_building_select_t + int rot = -1; + switch( (ribi_t::ribi)rotation ) + { + case ribi_t::south: rot = 0; break; + case ribi_t::east: rot = 1; break; + case ribi_t::north: rot = 2; break; + case ribi_t::west: rot = 3; break; + default: ; + } + if (building == NULL || !building->is_transport_building()) { + return call_tool_work("No building provided"); + } + if (const char* err = is_available(building)) { + return call_tool_work(err); + } + static cbuffer_t buf; + buf.clear(); + buf.printf("%s,%i", building->get_name(), rot); + return call_tool_work(TOOL_BUILD_STATION | GENERAL_TOOL, buf, 0, pl, pos); +} + +SQInteger command_build_station(HSQUIRRELVM vm) +{ + /* possible calling conventions: + * + * build_station(player, pos, desc) - top == 4 + * build_station(player, pos, desc, rotation) - top == 5 + */ + if (sq_gettop(vm) == 4) { + // rotation parameter missing, push default value + sq_pushinteger(vm, ribi_t::all); + } + return embed_call_t::call_function(vm, build_station_rotation, false); +} + + +call_tool_work build_depot(player_t* pl, koord3d pos, const building_desc_t* building) +{ + if (building == NULL || !building->is_depot()) { + return call_tool_work("No depot provided"); + } + if (const char* err = is_available(building)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_DEPOT | GENERAL_TOOL, building->get_name(), 0, pl, pos); +} + +call_tool_work build_bridge(player_t* pl, koord3d start, koord3d end, const bridge_desc_t* bridge) +{ + if (bridge == NULL) { + return call_tool_work("No bridge provided"); + } + if (const char* err = is_available(bridge)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 2, pl, start, end); +} + +call_tool_work build_bridge_at(player_t* pl, koord3d start, const bridge_desc_t* bridge) +{ + if (bridge == NULL) { + return call_tool_work("No bridge provided"); + } + if (const char* err = is_available(bridge)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 0, pl, start); +} + +call_tool_work set_slope(player_t* pl, koord3d start, my_slope_t slope) +{ + // communicate per default_param + static char buf[8]; + sprintf(buf, "%2d", (uint8)slope); + static tool_setslope_t tool; + // we do not want our slopes translated to double-height (even for single-height paksets), they are already in the double-height system + tool.old_slope_compatibility_mode = false; + tool.set_default_param(buf); + return call_tool_work(&tool, pl, start); +} + +call_tool_work restore_slope(player_t* pl, koord3d start) +{ + return call_tool_work(TOOL_RESTORESLOPE | GENERAL_TOOL, "", 0, pl, start); +} + +const char* can_set_slope(player_t* pl, koord3d pos, my_slope_t slope) +{ + return tool_setslope_t::tool_set_slope_work(pl, pos, slope); +} + +sint64 set_slope_get_price(my_slope_t slope) +{ + return slope == RESTORE_SLOPE ? -welt->get_settings().cst_alter_land : -welt->get_settings().cst_set_slope; +} + +call_tool_work build_sign_at(player_t* pl, koord3d start, const roadsign_desc_t* sign) +{ + if (sign == NULL) { + return call_tool_work("No sign provided"); + } + if (const char* err = is_available(sign)) { + return call_tool_work(err); + } + return call_tool_work(TOOL_BUILD_ROADSIGN | GENERAL_TOOL, sign->get_name(), 0, pl, start); +} + +call_tool_work change_climate_at(player_t* pl, koord3d start, int climate) +{ + if (climate < water_climate || climate >= MAX_CLIMATES) { + return call_tool_work("Invalid climate number provided"); + } + // communicate per default_param + static cbuffer_t param; + param.clear(); + param.printf("%d", climate); + return call_tool_work(TOOL_SET_CLIMATE | GENERAL_TOOL, param, 0, pl, start, start); +} + + + void export_commands(HSQUIRRELVM vm) { /** @@ -295,7 +467,7 @@ void export_commands(HSQUIRRELVM vm) * Simulates pressing shift or ctrl while clicking with mouse. * @param flags bitmap, 1 = shift pressed, 2 = ctrl pressed */ - //register_method(vm, &set_flags, "set_flags", true); + register_method(vm, &set_flags, "set_flags", true); /** * Does the dirty work. * @note In network games script will be suspended until the command is executed. @@ -334,6 +506,108 @@ void export_commands(HSQUIRRELVM vm) */ register_function(vm,, "work"); #endif + /** + * Build a way. + * @param pl player to pay for the work + * @param start coordinate, where work begins + * @param end coordinate, where work ends + * @param way type of way to be built + * @param straight force building of straight ways, similar as building way with control key pressed + */ + STATIC register_method_fv(vm, build_way, "build_way", freevariable(false), false, true); + /** + * Build a road. + * @param pl player to pay for the work + * @param start coordinate, where work begins + * @param end coordinate, where work ends + * @param way type of way to be built + * @param straight force building of straight ways, similar as building way with control key pressed + * @param keep_city_roads if true city roads will not be replaced + */ + STATIC register_method(vm, build_way, "build_road", false, true); + /** + * Build a depot. + * @param pl player to pay for the work + * @param pos position to place the depot + * @param depot type of depot to be built + */ + STATIC register_method(vm, build_depot, "build_depot", false, true); + /** + * Build a station or station extension building. + * @param pl player to pay for the work + * @param pos position to place the depot + * @param station type of station to be built + * @param rotaton (optional parameter) rotation of building (only used for flat docks, put direction from land to water here) + */ + STATIC register_function(vm, command_build_station, "build_station", -4 /* at least 4 parameters */, + func_signature_t::get_typemask(false).c_str(), false /* static */); + + log_squirrel_type(func_signature_t::get_squirrel_class(false), "build_station", func_signature_t::get_squirrel_type(false, 0)); + /** + * Build a bridge. + * Similar to drag-and-build of bridges in-game. + * @param pl player to pay for the work + * @param start coordinate, where bridge begins + * @param end coordinate, where bridge ends + * @param bridge type of bridge to be built + */ + STATIC register_method(vm, build_bridge, "build_bridge", false, true); + /** + * Build a bridge. + * Similar to one click with mouse on suitable start tile: program will figure out bridge span itself. + * @param pl player to pay for the work + * @param start coordinate, where bridge begins, the end point will be automatically determined + * @param bridge type of bridge to be built + */ + STATIC register_method(vm, build_bridge_at, "build_bridge_at", false, true); + /** + * Modify the slope of one tile. + * @param pl player to pay for the work + * @param pos position of tile + * @param slope new slope, can also be one of @ref slope::all_up_slope or @ref slope::all_down_slope. + */ + STATIC register_method(vm, set_slope, "set_slope", false, true); + /** + * Restore natural slope of one tile. + * @param pl player to pay for the work + * @param pos position of tile + */ + STATIC register_method(vm, restore_slope, "restore_slope", false, true); + /** + * Checks whether player @p pl can do this terraforming. + * @param pl player + * @param pos position + * @param slope new slope, can also be one of @ref slope::all_up_slope or @ref slope::all_down_slope + * @returns null (if allowed) or an error message otherwise + */ + STATIC register_method(vm, can_set_slope, "can_set_slope", false, true); + /** + * Costs of using @ref set_slope. + * @returns cost + */ + STATIC register_method(vm, set_slope_get_price, "slope_get_price", false, true); + /** + * Build signal / road-sign. If such a sign already exists then change its direction. + * @param pl player to pay for the work + * @param pos position of tile + * @param sign type of road-sign or signal to be built + */ + STATIC register_method(vm, build_sign_at, "build_sign_at", false, true); + /** + * Build way-object. + * @param pl player to pay for the work + * @param start coordinate, where work begins + * @param end coordinate, where work ends + * @param wayobj type of wayobj to be built + */ + STATIC register_method(vm, build_wayobj, "build_wayobj", false, true); + /** + * Change climate of tile + * @param pl player to pay for the work + * @param pos coordinate of tile + * @param climate new climate, possible values see @ref climates + */ + STATIC register_method(vm, change_climate_at, "change_climate_at", false, true); end_class(vm); } diff --git a/script/api/api_command.h b/script/api/api_command.h index a1db30bcad0..05eea2369e7 100644 --- a/script/api/api_command.h +++ b/script/api/api_command.h @@ -113,7 +113,7 @@ namespace script_api { call_tool_work(uint16 id, const char* dp, uint8 f, player_t* pl, koord3d pos1, koord3d pos2) : call_tool_base_t(id, dp, f, pl), - start(pos1), end(pos2), twoclick(false) { + start(pos1), end(pos2), twoclick(true) { } call_tool_work(const char* err) @@ -130,8 +130,8 @@ namespace script_api { * Returns error string at the end of the day. */ static SQInteger push(HSQUIRRELVM vm, call_tool_init v); - // returns strings - static const char* squirrel_type() { return param::squirrel_type(); } + // returns nothing sensible + static const char* squirrel_type() { return "void"; } }; template<> struct param { diff --git a/script/api/api_const.cc b/script/api/api_const.cc index 5db3a6187e1..6fdc0831c0d 100644 --- a/script/api/api_const.cc +++ b/script/api/api_const.cc @@ -10,6 +10,7 @@ #include "../api_param.h" #include "../../obj/simobj.h" #include "../../simmenu.h" +#include "../../simunits.h" using namespace script_api; @@ -97,6 +98,14 @@ void export_global_constants(HSQUIRRELVM vm) enum_slot(vm, "tool_change_water_height", TOOL_CHANGE_WATER_HEIGHT | GENERAL_TOOL); /// change climate of tiles enum_slot(vm, "tool_set_climate", TOOL_SET_CLIMATE | GENERAL_TOOL); + /// rotate a building + enum_slot(vm, "tool_rotate_building", TOOL_ROTATE_BUILDING | GENERAL_TOOL); +// /// merge two stops +// enum_slot(vm, "tool_merge_stop", TOOL_MERGE_STOP | GENERAL_TOOL); + /// scripted tool (one-click) + enum_slot(vm, "tool_exec_script", TOOL_EXEC_SCRIPT | GENERAL_TOOL); + /// scripted tool (two-click) + enum_slot(vm, "tool_exec_two_click_script", TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL); // simple tools /// increase industry density @@ -124,6 +133,14 @@ void export_global_constants(HSQUIRRELVM vm) end_enum(); + /** + * Flags for scripted tools. + */ + begin_enum("tool_flags"); + enum_slot(vm, "shift_pressed", tool_t::WFL_SHIFT); + enum_slot(vm, "ctrl_pressed", tool_t::WFL_CTRL); + end_enum(); + /** * Constants for different way types. */ @@ -181,7 +198,7 @@ void export_global_constants(HSQUIRRELVM vm) enum_slot(vm, "mo_tree", obj_t::baum); /// pointer (bulldozers etc) enum_slot(vm, "mo_pointer", obj_t::zeiger); - /// cloude and smoke + /// cloud and smoke enum_slot(vm, "mo_cloud", obj_t::wolke); /// building (houses, halts, factories ...) enum_slot(vm, "mo_building", obj_t::gebaeude); @@ -252,4 +269,28 @@ void export_global_constants(HSQUIRRELVM vm) end_enum(); + /** + * Internal units. + */ + begin_enum("units"); + /// The length of one side of a tile in car units. @see vehicle_desc_x::get_length + enum_slot(vm, "CARUNITS_PER_TILE", (uint32)CARUNITS_PER_TILE); + + end_enum(); + + /** + * Climate zones. Their naming may differ from the graphical representation and + * translation in some paksets. + */ + begin_enum("climates"); + enum_slot(vm, "cl_water", water_climate); + enum_slot(vm, "cl_desert", desert_climate); + enum_slot(vm, "cl_tropic", tropic_climate); + enum_slot(vm, "cl_mediterran", mediterran_climate); + enum_slot(vm, "cl_temperate", temperate_climate); + enum_slot(vm, "cl_tundra", tundra_climate); + enum_slot(vm, "cl_rocky", rocky_climate); + enum_slot(vm, "cl_arctic", arctic_climate); + end_enum(); + } diff --git a/script/api/api_control.cc b/script/api/api_control.cc index 191630b1180..b8ee3551e76 100644 --- a/script/api/api_control.cc +++ b/script/api/api_control.cc @@ -8,17 +8,49 @@ /** @file api_control.cc script control and debug functions. */ #include "../api_function.h" +#include "../api_class.h" +#include "../script.h" #include "../../squirrel/sq_extensions.h" +#include "../../simtool.h" + +namespace script_api { + + bool pause_game() + { + tool_pause_t t; + if (!t.is_selected()) { + t.init(NULL); + } + return t.is_selected(); + } + + bool is_game_paused() + { + tool_pause_t t; + return t.is_selected(); + } +}; using namespace script_api; -// SQInteger sleep(HSQUIRRELVM vm) -// { -// if (const char* blocker = sq_get_suspend_blocker(vm)) { -// return sq_raise_error(vm, "Cannot call sleep from within `%s'.", blocker); -// } -// return sq_suspendvm(vm); -// } +SQInteger sleep(HSQUIRRELVM vm) +{ + if (const char* blocker = sq_get_suspend_blocker(vm)) { + return sq_raise_error(vm, "Cannot call sleep from within `%s'.", blocker); + } + return sq_suspendvm(vm); +} + +SQInteger set_pause_on_error(HSQUIRRELVM vm) +{ + if (script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm)) { + bool poe = param::get(vm, 2); + script->pause_on_error = poe; + } + return SQ_OK; +} + + void export_control(HSQUIRRELVM vm) { @@ -30,17 +62,39 @@ void export_control(HSQUIRRELVM vm) * i.e. writing into @ref persistent. * @typemask void() */ + register_function(vm, sleep, "sleep", 1, "."); - register_function(vm, sq_suspendvm, "sleep", 1, "."); - // register_function(vm, sleep, "sleep", 1, "."); + /** + * @returns total amount of opcodes executed by vm + */ + register_function(vm, sq_get_ops_total, "get_ops_total"); - // /** - // * @returns total amount of opcodes executed by vm - // */ - // register_function(vm, sq_get_ops_total, "get_ops_total", 1, "."); + /** + * @returns amount of remaining opcodes until vm will be suspended + */ + register_function(vm, sq_get_ops_remaing, "get_ops_remaining"); + + + begin_class(vm, "debug"); + /** + * Pauses game. Does not work in network games. Use with care. + * @returns true when successful. + */ + STATIC register_method(vm, &pause_game, "pause", false, true); + /** + * Checks whether game is paused. + * Note that scripts only run for unpaused games. + * This can only be used in functions that are called by user actions (e.g., is_*_allowed). + * @returns true when successful. + */ + STATIC register_method(vm, &is_game_paused, "is_paused", false, true); + + /** + * Scripts can pause the game in case of error. Toggle this behavior by parameter @p p. + * Does not work in network games. Use with care. + * @param p true if script should make the game pause in case of error + */ + STATIC register_function(vm, set_pause_on_error, "set_pause_on_error", true); - // /** - // * @returns amount of remaining opcodes until vm will be suspended - // */ - // register_function(vm, sq_get_ops_remaing, "get_ops_remaining", 1, "."); + end_class(vm); } diff --git a/script/api/api_convoy.cc b/script/api/api_convoy.cc index 61f6aaef8e4..665ce56e7be 100644 --- a/script/api/api_convoy.cc +++ b/script/api/api_convoy.cc @@ -8,6 +8,7 @@ /** @file api_convoy.cc exports convoy related functions. */ #include "get_next.h" +#include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../simconvoi.h" @@ -16,6 +17,10 @@ #include "../../simworld.h" #include "../../vehicle/vehicle.h" +// for convoy tools +#include "../../simmenu.h" +#include "../../dataobj/schedule.h" + using namespace script_api; @@ -28,19 +33,38 @@ waytype_t get_convoy_wt(convoi_t* cnv) } -vector_tpl const& get_convoy_stat(convoi_t* cnv, int INDEX) +vector_tpl const& get_convoy_stat(convoi_t* cnv, sint32 INDEX) { static vector_tpl v; v.clear(); if (cnv && 0<=INDEX && INDEXget_stat_converted(i, (convoi_t::convoi_cost_t) INDEX)); + v.append(cnv->get_stat_converted(i, (convoi_t::convoi_cost_t)INDEX)); + } + } + return v; +} + + +vector_tpl const& convoi_get_vehicles(convoi_t* cnv) +{ + static vector_tpl v; + v.clear(); + if (cnv) { + for(uint16 i=0; iget_vehicle_count(); i++) { + v.append(cnv->get_vehicle(i)->get_desc()); } } return v; } +sint32 convoy_get_kmh(convoi_t const* cnv) +{ + return cnv ? speed_to_kmh(cnv->get_akt_speed()) : -1; +} + + vector_tpl const* generic_get_convoy_list(HSQUIRRELVM vm, SQInteger index) { uint16 id; @@ -90,6 +114,78 @@ SQInteger generic_get_convoy_by_index(HSQUIRRELVM vm) return SQ_ERROR; } +uint32 calc_max_kmh(uint32 power, uint32 weight, sint32 speed_limit) +{ + // FIXME + return 0; +// return speed_to_kmh(convoi_t::calc_max_speed(power, weight, kmh_to_speed(speed_limit)) ); +} + +uint32 kmh_to_tiles_per_month(uint32 kmh) +{ + return welt->speed_to_tiles_per_month( kmh_to_speed(kmh)); +} + +call_tool_init convoy_set_name(convoi_t *cnv, const char* name) +{ + return command_rename(cnv->get_owner(), 'c', cnv->self.get_id(), name); +} + +call_tool_init convoy_set_line(convoi_t *cnv, player_t *player, linehandle_t line) +{ + if (!line.is_bound()) { + return "Invalid line handle provided"; + } + // see depot_frame_t::apply_line() and convoi_t::call_convoi_tool() + cbuffer_t buf; + buf.printf("%c,%u,%i", 'l', cnv->self.get_id(), line->get_handle().get_id()); + + return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); +} + +call_tool_init convoy_generic_tool(convoi_t *cnv, player_t *player, uint8 cnvtool) +{ + char c = (char)cnvtool; + if (strchr("wx", c) == NULL) { + return "Invalid convoy tool called"; + } + // see convoi_t::call_convoi_tool() + cbuffer_t buf; + buf.printf("%c,%u", c, cnv->self.get_id()); + + return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); +} + +bool convoy_is_schedule_editor_open(convoi_t *cnv) +{ + return cnv->get_state() == convoi_t::EDIT_SCHEDULE; +} + +bool convoy_is_loading(convoi_t *cnv) +{ + return cnv->get_state() == convoi_t::LOADING; +} + +call_tool_init convoy_change_schedule(convoi_t *cnv, player_t *player, schedule_t *sched) +{ + if (sched) { + cbuffer_t buf; + // make a copy, and perform validation on it + schedule_t *copy = sched->copy(); +// copy->make_valid(); + if (copy->get_count() >= 2) { + // build param string (see convoi_info_t::apply_schedule and convoi_t::call_convoi_tool()) + buf.printf("g,%u,", cnv->self.get_id()); + copy->sprintf_schedule(buf); + } + else { + return "Invalid schedule provided: less than two entries remained after removing doubles"; + } + delete copy; + return call_tool_init(TOOL_CHANGE_CONVOI | SIMPLE_TOOL, buf, 0, player); + } + return "Invalid schedule provided"; +} void export_convoy(HSQUIRRELVM vm) { @@ -129,7 +225,11 @@ void export_convoy(HSQUIRRELVM vm) * Class to access a convoy. * Player vehicles are convoys, which themselves consist of individual vehicles (trucks, trailers, ...). */ - begin_class(vm, "convoy_x", "extend_get"); + begin_class(vm, "convoy_x", "extend_get,ingame_object"); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Does convoy needs electrified ways? * @returns true if this is the case @@ -140,6 +240,11 @@ void export_convoy(HSQUIRRELVM vm) * @returns name */ register_method(vm, &convoi_t::get_name, "get_name"); + /** + * Sets convoy name. + * @ingroup rename_func + */ + register_method(vm, &convoy_set_name, "set_name", true); /** * Position of convoy. * @returns pos @@ -170,11 +275,11 @@ void export_convoy(HSQUIRRELVM vm) * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_convoy_stat, "get_capacity", freevariable(convoi_t::CONVOI_CAPACITY), true); - /** - * Get monthly statistics of number of transported goods. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_convoy_stat, "get_transported_goods", freevariable(convoi_t::CONVOI_PAX_DISTANCE), true ); +// /** +// * Get monthly statistics of number of transported goods. +// * @returns array, index [0] corresponds to current month +// */ +// register_method_fv(vm, &get_convoy_stat, "get_transported_goods", freevariable(convoi_t::CONVOI_TRANSPORTED_GOODS), true ); /** * Get monthly statistics of revenue. * @returns array, index [0] corresponds to current month @@ -199,12 +304,106 @@ void export_convoy(HSQUIRRELVM vm) * Get monthly statistics of income/loss due to way tolls. * @returns array, index [0] corresponds to current month */ - // CONVOI_WAYTOLL not in Extended; unsure whether this is safe to comment out - ACarlotti register_method_fv(vm, &get_convoy_stat, "get_way_tolls", freevariable(convoi_t::CONVOI_WAYTOLL), true ); /** * @returns lifetime traveled distance of this convoy */ register_method(vm, &convoi_t::get_total_distance_traveled, "get_distance_traveled_total"); + /** + * @returns the line the convoy belongs to, null if there is no line + */ + register_method(vm, &convoi_t::get_line, "get_line"); + /** + * Assigns the convoy to the given line. + * @param player + * @param line + * @ingroup game_cmd + */ + register_method(vm, convoy_set_line, "set_line", true); + /** + * @returns returns an array containing the vehicle_desc_x 's of the vehicles of this convoy + */ + register_method(vm, convoi_get_vehicles, "get_vehicles", true); + /** + * @returns current speed of convoy + */ + register_method(vm, convoy_get_kmh, "get_speed", true); + /** + * @returns get current loading limit: waiting for full load gives 100 + */ + register_method(vm, &convoi_t::get_loading_limit, "get_loading_limit"); + /** + * @returns get current loading level + */ + register_method(vm, &convoi_t::get_loading_level, "get_loading_level"); + /** + * @returns gets location of home depot + */ + register_method(vm, &convoi_t::get_home_depot, "get_home_depot"); + /** + * @returns whether convoi has obsolete vehicles + */ + register_method(vm, &convoi_t::has_obsolete_vehicles, "has_obsolete_vehicles"); + /** + * Toggle the flag 'withdraw convoy' + * @ingroup game_cmd + */ + register_method_fv(vm, convoy_generic_tool, "toggle_withdraw", freevariable('w'), true ); + /** + * @returns the flag 'withdraw convoy' + */ + register_method(vm, &convoi_t::get_withdraw, "is_withdrawn"); + /** + * @returns true if convoy is in depot + */ + register_method(vm, &convoi_t::in_depot, "is_in_depot"); + /** + * @returns true if convoy is currently waiting (for way clearance) + */ + register_method(vm, &convoi_t::is_waiting, "is_waiting"); + /** + * @returns true if convoy is currently waiting (for way clearance) + */ + register_method(vm, convoy_is_loading, "is_loading", true); + /** + * Destroy the convoy. + * The convoy will be marked for destroying, it will be destroyed when the simulation continues. + * Call @ref sleep to be sure that the convoy is destroyed before script continues. + * @ingroup game_cmd + */ + register_method_fv(vm, convoy_generic_tool, "destroy", freevariable('x'), true); + /** + * @returns returns true if the schedule of the convoy is currently being edited. + */ + register_method(vm, convoy_is_schedule_editor_open, "is_schedule_editor_open", true); + /** + * @returns returns the number of station tiles covered by the convoy. + */ + register_method(vm, &convoi_t::get_tile_length, "get_tile_length"); + /** + * Change schedule of convoy. + * Schedule should not contain doubled entries and more than two entries. + * This might make the convoy lose its line. + * @ingroup game_cmd + */ + register_method(vm, convoy_change_schedule, "change_schedule", true); + +#define STATIC + /** + * Static method to compute the potential max speed of a convoy + * with the given parameters. + * @param power total power of convoy + * @param weight weight of convoy + * @param speed_limit speed limit induced by convoy's vehicles + * @returns max speed + */ + STATIC register_method(vm, &calc_max_kmh, "calc_max_speed"); + /** + * Static method to convert speed (from km per hour) to tiles per month. + * @param speed + * @returns tile per month + */ + STATIC register_method(vm, &kmh_to_tiles_per_month, "speed_to_tiles_per_month"); end_class(vm); } diff --git a/script/api/api_doc.h b/script/api/api_doc.h index ca4b4b22ddf..bcabce63f7e 100644 --- a/script/api/api_doc.h +++ b/script/api/api_doc.h @@ -8,63 +8,34 @@ /** * @mainpage * - * @section sec_howto How to create a scenario. + * Simutrans offers to possibility to add scripted scenarios and computer-controlled players. * - * You first need an @e idea - a vision what a scenario may look like. Then you have - * to cast this idea into a @e savegame. That is, create the world in which your scenario will live. - * You are the ruler of this toy universe, you are in charge of the rules, which go into the @e script. + * @tableofcontents * - * - * @section sec_sq Scripting language + * Scripting language + * ------------------ * * The scripts have to be written in squirrel. The manual can be found at Squirrel main page. * As squirrels like to crack nuts, understandably the script files get the extension '.nut'. * - * @section sec_ex Example script - * - * There are several commented examples. - * -# @ref page_script_mill - * -# @ref page_script_pharm - * - * - * @section sec_data_types Squirrel data types - * - * Squirrel has some built-in data types: among them integer, string, tables, and arrays. These are - * widely used throughout this documentation in the function declarations. - * - * If tables or arrays carry template parameters, this indicates the expected type of elements of the array or table. - * See factory_x::input for an example. - * - * @section sec_coord Coordinates - * - * All coordinates in the script are with respect to the initial rotation of the map. If a player rotates - * a map, then all coordinates are translated to the original rotation. - * This affects the classes @ref coord and @ref coord3d as well as any functions that expect coordinates as input, as - * for instance factory_x::factory_x or rules::forbid_way_tool_rect. + * @section s_scenarios Scripted scenarios * - * @section sec_network Network play + * See also @ref scen_skel and @ref scen_only. * - * A lot of stuff is possible for network play. There are some limitations of course. These are due to the fact, that - * the script is solely run on the server, the client does not need to have the script available at all. - * This implies for instance that all scenario texts are translated to the server's language. + * @subsection sec_howto How to create a scenario. * - * @section sec_err Logging and error handling - * If simutrans is started with '-debug 2', all errors and warnings are logged to standard output (i.e. terminal). - * If simutrans is started in addition with '-log', all the output is written to the file script.log. In particular, - * everything that is print-ed by the script, goes into this file. + * You first need an @e idea - a vision what a scenario may look like. Then you have + * to cast this idea into a @e savegame. That is, create the world in which your scenario will live. + * You are the ruler of this toy universe, you are in charge of the rules, which go into the @e script. * - * In case of error, an error window pops up showing the call stack and the values of local variables. - * You can then repair your script, and restart the scenario via New Game - Scenario. You do not need to restart - * simutrans to reload your script. * - * @section sec_rdwr Load-Save support - * Such support is available of course. If a running scenario is saved, the information about the scenario is saved within the - * savegame. Upon loading, the scenario is resumed, by calling the function ::resume_game. + * @subsection sec_ex Example scenario scripts * - * You can save data in the savegame. In order to do so, you have to keep these data in the global table ::persistent. - * This table is saved and loaded. + * There are several commented examples. + * -# @ref page_script_mill + * -# @ref page_script_pharm * - * @section sec_dir Recommended directory structure + * @subsection sec_dir Recommended directory structure * * The scenario plays in a savegame. This savegame is tied to the pak-set you are using (e.g. pak64, pak128.Britain). * Hence, the scenario files have to go into a sub-folder of the pak-set. @@ -97,6 +68,112 @@ * pak-something/scenario/myscenario/de/ <- German files go here. * * + * Scenarios can also be put into the addons folder: + * + * + * addons/pak-something/scenario/myscenario/ + * + * + * + * @section s_ai_player Scripted AI players + * + * See also @ref ai_skel. + * + * @subsection sec_dir Recommended directory structure + * + * Your script file goes into the folder + * + * + * ai/myai/ + * + * + * The main script file must be + * + * + * ai/myai/ai.nut + * + * + * The ai scripts can also be put into + * + * + * addons/ai/myai/ + * + * + * @section s_scripted_tools Scripted tools + * + * See also @ref tool_skel and @ref tool_only. + * + * @subsection sec_dir_tools Recommended directory structure + * + * The script file (tool.nut) as well as the configuration file (description.tab) go into + * + * + * pak-something/tool/mytool/ + * + * + * Related pak-files have to be placed in + * + * + * pak-something/ + * + * + * directly. + * + * @subsection sec_description_tab The file description.tab + * + * This is a plain text file with the following entries:
+ * + * title=Name of tool to be shown in tool selection dialog
+ * type=one_click or two_click
+ * tooltip=Tooltip for the icon in the toolbar
+ * restart=Set to 1 if the virtual machine has to be restarted after each call to the work functions.
+ * menu=Parameter that can be used to load tools with menuconf.tab
+ * icon=Name of cursor object (loaded from some pak-file), used images: 0 = cursor, 1 = icon, 2 = marker image
+ *
+ * + * @section s_general_advice General scripting advice + * + * Check out the sections on the Modules page. + * See also @ref pitfalls. + * + * @subsection sec_data_types Squirrel data types + * + * Squirrel has some built-in data types: among them integer, string, tables, and arrays. These are + * widely used throughout this documentation in the function declarations. + * + * If tables or arrays carry template parameters, this indicates the expected type of elements of the array or table. + * See factory_x::input for an example. + * + * @subsection sec_coord Coordinates + * + * All coordinates in the script are with respect to the initial rotation of the map. If a player rotates + * a map, then all coordinates are translated to the original rotation. + * This affects the classes @ref coord and @ref coord3d as well as any functions that expect coordinates as input, as + * for instance factory_x::factory_x or rules::forbid_way_tool_rect. + * + * @subsection sec_network Network play + * + * A lot of stuff is possible for network play. There are some limitations of course. These are due to the fact, that + * the scripts are solely run on the server, the client does not need to have the script available at all. + * This implies for instance that all scenario texts are translated to the server's language. + * + * @subsection sec_err Logging and error handling + * If simutrans is started with '-debug 2', all errors and warnings are logged to standard output (i.e. terminal). + * If simutrans is started in addition with '-log', all the output is written to the file script.log. In particular, + * everything that is print-ed by the script, goes into this file. + * + * In case of error, an error window pops up showing the call stack and the values of local variables. + * You can then repair your script, and restart the scenario via New Game - Scenario. You do not need to restart + * simutrans to reload your script. + * + * @subsection sec_rdwr Load-Save support + * Such support is available of course. If a running scenario is saved, the information about the scenario is saved within the + * savegame. Upon loading, the scenario is resumed, by calling the function ::resume_game. + * + * You can save data in the savegame. In order to do so, you have to keep these data in the global table ::persistent. + * This table is saved and loaded. + * + * */ /** @@ -110,7 +187,7 @@ * Let us inspect a sample script file. We will have a look into the pak64 Millionaire scenario. * The objective of this scenario is to get rich as fast as possible. * - * @don'tinclude millionaire/scenario.nut + * @dontinclude millionaire/scenario.nut * As first the savegame is specified: * @skipline map.file * Then we provide some meta-information about the ::scenario @@ -148,7 +225,7 @@ * * We will learn how to make the translation of the script's output possible. * - * @don'tinclude pharmacy-max/scenario.nut + * @dontinclude pharmacy-max/scenario.nut * We do not care about the meta information, and jump right into the @ref get_rule_text method. * @skip get_rule_text * @until } @@ -193,42 +270,10 @@ * That's it. The remaining parts of this script are plain routine. */ -/** - * @page deprecated_stuff Deprecated stuff - * - * @section sec_dir_112 Recommended directory structure (only simutrans nightly versions pre-r5989) - * - * The scenario plays in a savegame. This savegame is tied to the pak-set you are using (e.g. pak64, pak128.Britain). - * Hence, the scenario files have to go into a sub-folder of the pak-set. - * The pak-set is found in a directory named pak-something, which is in the same directory, where the program - * itself is located. - * - * Your scenario file goes into the folder - * - * - * pak-something/scenario/myscenario.nut - * - * - * Translation files go into a sub-directory with the same name as your file (just without extension) - * - * - * pak-something/scenario/myscenario/ - * - * - * If need more complex texts, these go into sub-directories named after the language, in which they are written. - * - * - * pak-something/scenario/myscenario/en/ <- English files go here. - * - * pak-something/scenario/myscenario/de/ <- German files go here. - * - * - */ - /** * @page pitfalls Common pitfalls * - * @section pitfall_pass_by_reference Tables, arrays, strings, classes, instances etc are passed by reference + * @subsection pitfall_pass_by_reference Tables, arrays, strings, classes, instances etc are passed by reference * * @code * local a = {} // create an empty table @@ -238,7 +283,7 @@ * @endcode * * - * @section nocalls_in_global_scope Do not call API-functions in global scope in the script + * @subsection nocalls_in_global_scope Do not call API-functions in global scope in the script * * @code * local start_time = settings.get_start_time() // this call is executed BEFORE the savegame is loaded -> undefined @@ -251,3 +296,86 @@ * @endcode * */ + +/** + * @defgroup scen_skel Scenario interface + * + * The following methods are vital for the functioning of a scripted scenario. + * They will be called from simutrans to interact with the script. You should consider + * implementing them. + * + */ + +/** + * @defgroup scen_only Scenario only functions + * + * These classes and methods are only available for scripted scenarios. + * + */ + +/** + * @defgroup ai_skel AI interface + * + * The following methods are vital for the functioning of a scripted AI. + * They will be called from simutrans to interact with the script. You should consider + * implementing them. + * + */ + +/** + * @defgroup ai_only AI only functions + * + * These classes and methods are only available for scripted AI players. + * + */ + +/** + * @defgroup tool_skel Tool interface + * + * The following methods are vital for the functioning of scripted tools. + * They will be called from simutrans to interact with the script. You should consider + * implementing them. + * + */ + +/** + * @defgroup tool_only Tool only functions + * + * These classes and methods are only available for scripted scenarios. + * + */ + +/** + * @defgroup game_cmd Functions to alter the state of the game and map + * + * The player parameter in these functions represents the player that executes the command + * and pays for it. If the call is from an AI player then the parameter is set to player_x::self, + * and it will be checked whether the + * player is permitted to execute the command. Calls from scenario always pass this check. + * + * In network games, the script is suspended until the command is executed, which is transparent to the script. + * Hence such commands cannot be called from within functions that should return immediately, see @ref quick_return_func. + */ + +/** + * @defgroup rename_func Functions to rename something in the game + * @ingroup game_cmd + * + * In AI mode, renaming works only if the object is owned by the script's player. + */ + +/** + * @defgroup quick_return_func Functions that should return quickly. + * + * These functions are intended to quickly return a result. In network games, it is not + * allowed to call any map-altering tools from within such a function, see the section on @ref game_cmd. + */ + +/** + * @page page_sqstdlib Available functions from the squirrel standard lib + * + * - Math Library, see http://squirrel-lang.org/squirreldoc/stdlib/stdmathlib.html#squirrel-api + * - String Library, see http://squirrel-lang.org/squirreldoc/stdlib/stdstringlib.html#squirrel-api + * - System Library: functions clock, time, date, see http://squirrel-lang.org/squirreldoc/stdlib/stdsystemlib.html#squirrel-api + * + */ diff --git a/script/api/api_factory.cc b/script/api/api_factory.cc index f18e645a616..be46a610f38 100644 --- a/script/api/api_factory.cc +++ b/script/api/api_factory.cc @@ -8,10 +8,12 @@ /** @file api_factory.cc exports factory related functions. */ #include "get_next.h" +#include "api_obj_desc_base.h" #include "../api_class.h" #include "../api_function.h" #include "../../dataobj/scenario.h" #include "../../simfab.h" +#include "../../descriptor/factory_desc.h" using namespace script_api; @@ -24,7 +26,7 @@ SQInteger exp_factory_constructor(HSQUIRRELVM vm) set_slot(vm, "y", y, 1); // transform coordinates koord pos(x,y); - welt->get_scenario()->koord_sq2w(pos); + coordinate_transform_t::koord_sq2w(pos); fabrik_t *fab = fabrik_t::get_fab(pos); if (!fab) { return sq_raise_error(vm, "No factory found at (%s)", pos.get_str()); @@ -45,10 +47,11 @@ SQInteger exp_factory_constructor(HSQUIRRELVM vm) // create empty table sq_newtable(vm); } - // set max value - set_slot(vm, "max_storage", prodslot[p].max >> fabrik_t::precision_bits, -1); - //production/consumption scaling - set_slot(vm, "scalling", io == 0 ? (sint64)desc->get_supplier(p)->get_consumption() : (sint64)desc->get_product(p)->get_factor(), -1); + sint64 factor = io == 0 ? desc->get_supplier(p)->get_consumption() : desc->get_product(p)->get_factor(); + // set max value - see fabrik_t::info_prod + set_slot(vm, "max_storage", convert_goods( (sint64)prodslot[p].max * factor), -1); + // production/consumption scaling + set_slot(vm, "scaling", factor, -1); // put class into table sq_newslot(vm, -3, false); } @@ -84,6 +87,18 @@ vector_tpl const& get_factory_production_stat(const ware_production_t *p } +uint32 get_production_factor(const factory_product_desc_t *desc) +{ + return desc ? ( (1<< (DEFAULT_PRODUCTION_FACTOR_BITS-1)) + (uint32)desc->get_factor() * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS : 0; +} + + +uint32 get_consumption_factor(const factory_supplier_desc_t *desc) +{ + return desc ? ( (1<< (DEFAULT_PRODUCTION_FACTOR_BITS-1)) + (uint32)desc->get_consumption() * 100) >> DEFAULT_PRODUCTION_FACTOR_BITS : 0; +} + + vector_tpl const& factory_get_tile_list(fabrik_t *fab) { static vector_tpl list; @@ -99,6 +114,24 @@ vector_tpl const& factory_get_halt_list(fabrik_t *fab) return square_get_halt_list(plan); } +leitung_t *factory_get_transformer( fabrik_t* fab ) +{ + // FIXME + return NULL; +// return fab->get_transformers().empty() ? NULL :fab->get_transformers().front(); +} + +call_tool_init factory_set_name(fabrik_t *fab, const char* name) +{ + return command_rename(welt->get_public_player(), 'f', fab->get_pos(), name); +} + + +const char* fabrik_get_raw_name(fabrik_t *fab) +{ + return fab->get_desc()->get_name(); +} + SQInteger ware_production_get_production(HSQUIRRELVM vm) { @@ -127,6 +160,11 @@ SQInteger world_get_factory_by_index(HSQUIRRELVM vm) return param::push(vm, fab); } +SQInteger world_get_factory_count(HSQUIRRELVM vm) +{ + return param::push(vm, welt->get_fab_list().get_count()); +} + void export_factory(HSQUIRRELVM vm) { @@ -150,6 +188,11 @@ void export_factory(HSQUIRRELVM vm) * Meta-method to be used in foreach loops. Do not call them directly. */ register_function(vm, world_get_factory_by_index, "_get", 2, "xi"); + /** + * Returns number of factories in the list. + * @typemask integer() + */ + register_function(vm, world_get_factory_count, "get_count", 1, "x"); end_class(vm); @@ -158,7 +201,7 @@ void export_factory(HSQUIRRELVM vm) * Class to access information about factories. * Identified by coordinate. */ - begin_class(vm, "factory_x", "extend_get,coord"); + begin_class(vm, "factory_x", "extend_get,coord,ingame_object"); /** * Constructor. @@ -197,6 +240,10 @@ void export_factory(HSQUIRRELVM vm) */ table output; #endif + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Get list of consumers of this factory. @@ -205,12 +252,11 @@ void export_factory(HSQUIRRELVM vm) //FIXTHIS? //register_method(vm, &fabrik_t::get_consumers, "get_consumers"); - /** - * Get list of consumers of this factory. - * @returns array of coordinates of suppliers - */ - //FIXTHIS - //register_method(vm, &fabrik_t::get_suppliers, "get_suppliers"); +// /** +// * Get list of consumers of this factory. +// * @returns array of coordinates of suppliers +// */ +// register_method(vm, &fabrik_t::get_suppliers, "get_suppliers"); /** * Get (translated or custom) name of factory. @@ -218,10 +264,17 @@ void export_factory(HSQUIRRELVM vm) */ register_method(vm, &fabrik_t::get_name, "get_name"); + /** + * Get untranslated name of factory. + * @returns name + */ + register_method(vm, &fabrik_get_raw_name, "get_raw_name", true); + /** * Change name. + * @ingroup rename_func */ - register_method(vm, &fabrik_t::set_name, "set_name"); + register_method(vm, &factory_set_name, "set_name", true); /** * Get monthly statistics of production. @@ -257,13 +310,13 @@ void export_factory(HSQUIRRELVM vm) * Get monthly statistics of generated passengers. * @returns array, index [0] corresponds to current month */ - //register_method_fv(vm, &get_factory_stat, "get_pax_generated", freevariable(FAB_PAX_GENERATED), true); + register_method_fv(vm, &get_factory_stat, "get_pax_generated", freevariable(FAB_PAX_GENERATED), true); /** * Get monthly statistics of departed passengers. * @returns array, index [0] corresponds to current month */ - //register_method_fv(vm, &get_factory_stat, "get_pax_departed", freevariable(FAB_PAX_DEPARTED), true); + register_method_fv(vm, &get_factory_stat, "get_pax_departed", freevariable(FAB_PAX_DEPARTED), true); /** * Get monthly statistics of arrived passengers. @@ -275,7 +328,7 @@ void export_factory(HSQUIRRELVM vm) * Get monthly statistics of generated mail. * @returns array, index [0] corresponds to current month */ - //register_method_fv(vm, &get_factory_stat, "get_mail_generated", freevariable(FAB_MAIL_GENERATED), true); + register_method_fv(vm, &get_factory_stat, "get_mail_generated", freevariable(FAB_MAIL_GENERATED), true); /** * Get monthly statistics of departed mail. @@ -295,18 +348,34 @@ void export_factory(HSQUIRRELVM vm) */ register_method(vm, &factory_get_tile_list, "get_tile_list", true); - /** - * Get monthly statistics of arrived visitors. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_factory_stat, "get_visitor_arrived", freevariable(FAB_CONSUMER_ARRIVED), true); - /** * Get list of all halts that serve this this factory. * @returns array of tile_x objects */ register_method(vm, &factory_get_halt_list, "get_halt_list", true); + /** + * Checks whether a transformer is connected. + * @returns name + */ + register_method(vm, &fabrik_t::is_transformer_connected, "is_transformer_connected"); + /** + * Get connected transformer (if any). + * @returns transformer + */ + register_method(vm, factory_get_transformer, "get_transformer", true); + /** + * @returns number of fields belonging to this factory + */ + register_method(vm, &fabrik_t::get_field_count, "get_field_count"); +// /** +// * @returns minimum number of fields required +// */ +// register_method(vm, &fabrik_t::get_min_field_count, "get_min_field_count"); + /** + * @returns factory descriptor + */ + register_method(vm, &fabrik_t::get_desc, "get_desc"); // pop class end_class(vm); @@ -358,6 +427,35 @@ void export_factory(HSQUIRRELVM vm) */ register_method_fv(vm, &get_factory_production_stat, "get_produced", freevariable(FAB_GOODS_PRODUCED), true); + /** + * Returns base maximum production of this good per month. + * Does not take any productivity boost into account. + * @typemask integer() + */ + register_function(vm, &ware_production_get_production, "get_base_production", 1, "x"); + + /** + * Returns base maximum consumption of this good per month. + * Does not take any productivity boost into account. + * @typemask integer() + */ + register_function(vm, &ware_production_get_production, "get_base_consumption", 1, "x"); + + /** + * Returns number of consumed units of this good for the production of 100 units of generic outputs unit. + * Does not take any productivity boost into account. + * @typemask integer() + */ + register_method(vm, &get_consumption_factor, "get_consumption_factor", 1, "x"); + + /** + * Returns number of produced units of this good due to the production of 100 units of generic outputs unit. + * Does not take any productivity boost into account. + * @typemask integer() + */ + register_method(vm, &get_production_factor, "get_production_factor", 1, "x"); + + // pop class end_class(vm); } diff --git a/script/api/api_gui.cc b/script/api/api_gui.cc index eb0b7833a0c..855f7bb090e 100644 --- a/script/api/api_gui.cc +++ b/script/api/api_gui.cc @@ -11,54 +11,134 @@ #include "../api_function.h" #include "../../simmesg.h" +#include "../../simmenu.h" #include "../../simworld.h" #include "../../dataobj/scenario.h" #include "../../player/simplay.h" +#include "../../dataobj/environment.h" +#include "../../network/network.h" +#include "../../network/network_cmd_scenario.h" + using namespace script_api; -script_api::void_t add_scenario_message_at(const char* text, koord pos) +call_tool_work add_scenario_message_at(const char* text, koord pos) +{ + // build param string (see tool_add_message_t::init) + cbuffer_t buf; + buf.printf("%d,%s", message_t::scenario, text); + + return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, welt->get_active_player(), koord3d(pos, 0)); +} + +call_tool_work add_ai_message_at(player_t *player, const char* text, koord pos) +{ + // build param string (see tool_add_message_t::init) + cbuffer_t buf; + buf.printf("%d,%s", message_t::ai, text); + + return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, player, koord3d(pos, 0)); +} + +call_tool_work add_scenario_message(player_t* player, const char* text) { - if (text) { - message_t *msg = welt->get_message(); - msg->add_message(text, pos, message_t::scenario, PLAYER_FLAG|welt->get_active_player()->get_player_nr()); + // build param string (see tool_add_message_t::init) + cbuffer_t buf; + buf.printf("%d,%s", message_t::scenario, text); + + return call_tool_work(TOOL_ADD_MESSAGE | GENERAL_TOOL, (const char*)buf, 0, player ? player : welt->get_active_player(), koord3d::invalid); +} + +void open_info_win_client(const char* tab, uint8 player_nr) +{ + if (env_t::server) { + // void network_send_all(network_command_t* nwc, bool exclude_us ) + nwc_scenario_t *nwc = new nwc_scenario_t(); + nwc->what = nwc_scenario_t::OPEN_SCEN_WIN; + nwc->function = tab; + network_send_all(nwc, false, player_nr); + } + else { + welt->get_scenario()->open_info_win(tab); } - return script_api::void_t(); } +void open_info_win_at(const char* tab) +{ + open_info_win_client(tab, PLAYER_UNOWNED); +} -void export_gui(HSQUIRRELVM vm) +void open_info_win() +{ + open_info_win_client("", PLAYER_UNOWNED); +} + +void export_gui(HSQUIRRELVM vm, bool scenario) { /** * Table with methods to access gui functions. */ begin_class(vm, "gui", 0); - /** - * Opens scenario info window. - */ - STATIC register_method(vm, &scenario_t::open_info_win, "open_info_win"); + if (scenario) { + /** + * Opens scenario info window and shows 'info' tab. + * In network mode, opens window on all clients and server. + * @note Only available in scenario mode. + */ + STATIC register_method(vm, open_info_win, "open_info_win", true); - /** - * Adds message to the players mailboxes. - * Will be shown in ticker or as pop-up window depending on players preferences. - * Message window has small view of world. - * - * @param text Text to be shown. Has to be a translated string or a translatable string. - * @param position Position of the view on the map. Clicking on the message will center viewport at this position. - * @warning Message only shown on server, but stored in savegame. - */ - STATIC register_method(vm, &add_scenario_message_at, "add_message_at"); + /** + * Opens scenario info window with specific tab open. + * In network mode, opens window on all clients and server. + * @param tab possible values are "info", "goal", "rules", "result", "about", "debug" + * @note Only available in scenario mode. + */ + STATIC register_method(vm, open_info_win_at, "open_info_win_at"); - /** - * Adds message to the players mailboxes. - * Will be shown in ticker or as pop-up window depending on players preferences. - * - * @param text Text to be shown. Has to be a translated string or a translatable string. - * @warning Message only shown on server, but stored in savegame. - */ - STATIC register_method_fv(vm, &add_scenario_message_at, "add_message", freevariable(koord::invalid) ); + /** + * Opens scenario info window for certain clients (and the server), + * with specific tab open. + * @param tab possible values are "info", "goal", "rules", "result", "about", "debug" + * @param player_nr opens scenario info window on all clients that have this player unlocked. + * @note Window is always opened on server. + */ + STATIC register_method(vm, open_info_win_client, "open_info_win_client"); + /** + * Adds message to the players mailboxes. + * Will be shown in ticker or as pop-up window depending on players preferences. + * Message window has small view of world. + * + * @param text Text to be shown. Has to be a translated string or a translatable string. + * @param position Position of the view on the map. Clicking on the message will center viewport at this position. + * @note Only available in scenario mode. + * @ingroup scen_only + */ + STATIC register_method(vm, &add_scenario_message_at, "add_message_at"); + /** + * Adds message to the players mailboxes. + * Will be shown in ticker or as pop-up window depending on players preferences. + * + * @param text Text to be shown. Has to be a translated string or a translatable string. + * @note Only available in scenario mode. + * @ingroup scen_only + */ + STATIC register_method(vm, &add_scenario_message, "add_message"); + } + else { + /** + * Adds message to the players mailboxes. + * Will be shown in ticker or as pop-up window depending on players preferences. + * Message window has small view of world. + * + * @param player sending this message + * @param text Text to be shown. Has to be a translated string or a translatable string. + * @param position Position of the view on the map. Clicking on the message will center viewport at this position. + * @ingroup ai_only + */ + STATIC register_method(vm, &add_ai_message_at, "add_message_at"); + } end_class(vm); } diff --git a/script/api/api_halt.cc b/script/api/api_halt.cc index f7f0d262352..35061baac60 100644 --- a/script/api/api_halt.cc +++ b/script/api/api_halt.cc @@ -14,9 +14,13 @@ #include "../api_function.h" #include "../../simhalt.h" +halthandle_t get_halt_from_koord3d(koord3d pos, const player_t *player ); // api_schedule.cc, interfaces haltestelle_t::get_halt + + namespace script_api { declare_specialized_param(haltestelle_t::tile_t, "t|x|y", "tile_x"); +// declare_specialized_param(haltestelle_t::connection_t, "t|x|y", "halt_x"); SQInteger param::push(HSQUIRRELVM vm, haltestelle_t::tile_t const& v) @@ -24,8 +28,13 @@ namespace script_api { return param::push(vm, v.grund); } +// SQInteger param::push(HSQUIRRELVM vm, haltestelle_t::connection_t const& v) +// { +// return param::push(vm, v.halt); +// } }; + using namespace script_api; vector_tpl const& get_halt_stat(const haltestelle_t *halt, sint32 INDEX) @@ -71,6 +80,14 @@ SQInteger halt_export_convoy_list(HSQUIRRELVM vm) } +call_tool_init halt_set_name(halthandle_t halt, const char* name) +{ + if (!halt.is_bound()) { + return "Invalid halt provided"; + } + return command_rename(halt->get_owner(), 'h', halt.get_id(), name); +} + SQInteger halt_export_line_list(HSQUIRRELVM vm) { @@ -87,6 +104,12 @@ SQInteger halt_export_line_list(HSQUIRRELVM vm) } +uint32 halt_get_capacity(const haltestelle_t *halt, const goods_desc_t *desc) +{ + // passenger has index 0, mail 1, everything else >=2 + return halt && desc ? halt->get_capacity(min( desc->get_catg_index(), 2 )) : 0; +} + // 0: not connected // 1: connected // -1: undecided @@ -106,6 +129,14 @@ SQInteger halt_compare(halthandle_t a, halthandle_t b) } +// vector_tpl const& halt_get_connections(const haltestelle_t *halt, const goods_desc_t* freight) +// { +// static vector_tpl dummy; +// dummy.clear(); +// return freight ? halt->get_connections(freight->get_catg_index()) : dummy; +// } + + void export_halt(HSQUIRRELVM vm) { /** @@ -134,13 +165,22 @@ void export_halt(HSQUIRRELVM vm) /** * Class to access halts / stations / bus stops. */ - begin_class(vm, "halt_x", "extend_get"); + begin_class(vm, "halt_x", "extend_get,ingame_object"); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Station name. * @returns name */ register_method(vm, &haltestelle_t::get_name, "get_name"); + /** + * Sets station name. + * @ingroup rename_func + */ + register_method(vm, &halt_set_name, "set_name", true); /** * Station owner. @@ -171,15 +211,15 @@ void export_halt(HSQUIRRELVM vm) register_method(vm, &haltestelle_t::is_enabled, "accepts_good", false); /** - * Get monthly statistics of number of passengers. + * Get monthly statistics of number of arrived goods. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_halt_stat, "get_visitors", freevariable(HALT_VISITORS), true); - /** - * Get monthly statistics of number of pax transfers. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_halt_stat, "get_commuters", freevariable(HALT_COMMUTERS), true); + register_method_fv(vm, &get_halt_stat, "get_arrived", freevariable(HALT_CONVOIS_ARRIVED), true); +// /** +// * Get monthly statistics of number of departed goods. +// * @returns array, index [0] corresponds to current month +// */ +// register_method_fv(vm, &get_halt_stat, "get_departed", freevariable(HALT_DEPARTED), true); /** * Get monthly statistics of number of waiting goods. * @returns array, index [0] corresponds to current month @@ -209,16 +249,11 @@ void export_halt(HSQUIRRELVM vm) * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_halt_stat, "get_convoys", freevariable(HALT_CONVOIS_ARRIVED), true); - /** - * Get monthly statistics of number of passengers that could walk to their destination. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_halt_stat, "get_too_slow", freevariable(HALT_TOO_SLOW), true); - /** - * Get monthly statistics of number of passengers that so long waiting at the station. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_halt_stat, "get_too_waiting", freevariable(HALT_TOO_WAITING), true); +// /** +// * Get monthly statistics of number of passengers that could walk to their destination. +// * @returns array, index [0] corresponds to current month +// */ +// register_method_fv(vm, &get_halt_stat, "get_walked", freevariable(HALT_WALKED), true); /** * Exports list of convoys that stop at this halt. * @typemask convoy_list_x() @@ -237,6 +272,35 @@ void export_halt(HSQUIRRELVM vm) * Get list of factories connected to this station. */ register_method(vm, &haltestelle_t::get_fab_list, "get_factory_list"); + /** + * Returns amount of @p freight at this halt that is going to @p target + * @param freight freight type + * @param target coordinate of target + */ + register_method(vm, &haltestelle_t::get_ware_fuer_zielpos, "get_freight_to_dest"); +// /** +// * Returns amount of @p freight at this halt that scheduled to @p stop +// * @param freight freight type +// * @param stop next transfer stop +// */ +// register_method(vm, &haltestelle_t::get_ware_fuer_zwischenziel, "get_freight_to_halt"); + /** + * Returns capacity of this halt for the given @p freight + * @param freight freight type + */ + register_method(vm, &halt_get_capacity, "get_capacity", true); +// /** +// * Returns list of connected halts for the specific @p freight type. +// * @param freight freight type +// */ +// register_method(vm, &halt_get_connections, "get_connections", true); + /** + * Returns halt at given position. + * @param pos coordinate + * @param pl player that wants to use halt here + * @return halt instance + */ + STATIC register_method(vm, &get_halt_from_koord3d, "get_halt", false, true); end_class(vm); } diff --git a/script/api/api_include.cc b/script/api/api_include.cc index 226104f985c..bcaa2b6380d 100644 --- a/script/api/api_include.cc +++ b/script/api/api_include.cc @@ -31,7 +31,7 @@ SQInteger include_aux(HSQUIRRELVM vm) } // call it sq_pushroottable(vm); - if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue))) { + if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000))) { sq_pop(vm, 1); // pop script return sq_raise_error(vm, "Call script %s failed", filename); } @@ -45,7 +45,6 @@ SQInteger include_aux(HSQUIRRELVM vm) void export_include(HSQUIRRELVM vm, const char* include_path) { - script_api::start_squirrel_type_logging(); sq_pushroottable(vm); /** @@ -59,5 +58,4 @@ void export_include(HSQUIRRELVM vm, const char* include_path) register_function_fv(vm, &include_aux, "include", 2, ".s", freevariable(include_path)); sq_pop(vm, 1); // root table - script_api::end_squirrel_type_logging(); } diff --git a/script/api/api_line.cc b/script/api/api_line.cc index 74ebc9e6ac6..3f2cc885592 100644 --- a/script/api/api_line.cc +++ b/script/api/api_line.cc @@ -17,6 +17,12 @@ #include "../../simworld.h" #include "../../player/simplay.h" +// for manipulation of lines +#include "../../simmenu.h" +#include "../../dataobj/schedule.h" + +// template<> schedule_t* script_api::param::get(HSQUIRRELVM, SQInteger); + using namespace script_api; vector_tpl const& get_line_stat(simline_t *line, sint32 INDEX) @@ -25,7 +31,7 @@ vector_tpl const& get_line_stat(simline_t *line, sint32 INDEX) v.clear(); if (line && 0<=INDEX && INDEXget_finance_history(i, (line_cost_t)INDEX) ); + v.append( line->get_stat_converted(i, INDEX) ); } } return v; @@ -50,6 +56,38 @@ waytype_t line_way_type(simline_t *line) return invalid_wt; } +call_tool_init line_change_schedule(simline_t* line, player_t *player, schedule_t *sched) +{ + if (sched) { + cbuffer_t buf; + // make a copy, and perform validation on it + schedule_t *copy = sched->copy(); +// copy->make_valid(); + if (copy->get_count() >= 2) { + // build param string (see line_management_gui_t::apply_schedule) + buf.printf( "g,%i,", line->get_handle().get_id() ); + copy->sprintf_schedule( buf ); + } + else { + return "Invalid schedule provided: less than two entries remained after removing doubles"; + } + delete copy; + return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); + } + return "Invalid schedule provided"; +} + +call_tool_init line_delete(simline_t* line, player_t *player) +{ + if (line->count_convoys() == 0) { + cbuffer_t buf; + buf.printf( "d,%i", line->get_handle().get_id() ); + + return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); + } + return "Cannot delete lines with associated convoys"; +} + SQInteger line_export_convoy_list(HSQUIRRELVM vm) { linehandle_t line = param::get(vm, 1); @@ -58,9 +96,14 @@ SQInteger line_export_convoy_list(HSQUIRRELVM vm) set_slot(vm, "line_id", line.get_id()); return 1; } + sq_raise_error(vm, "Invalid line handle provided"); return SQ_ERROR; } +call_tool_init line_set_name(simline_t* line, const char* name) +{ + return command_rename(line->get_owner(), 'l', line->get_handle().get_id(), name); +} vector_tpl const* generic_get_line_list(HSQUIRRELVM vm, SQInteger index) { @@ -99,6 +142,12 @@ SQInteger generic_get_line_by_index(HSQUIRRELVM vm) return SQ_ERROR; } +SQInteger generic_get_line_count(HSQUIRRELVM vm) +{ + vector_tpl const* list = generic_get_line_list(vm, 1); + return param::push(vm, list ? list->get_count() : 0); +} + void export_line(HSQUIRRELVM vm) { /** @@ -124,18 +173,33 @@ void export_line(HSQUIRRELVM vm) * @typemask line_x() */ register_function(vm, generic_get_line_by_index, "_get", 2, "xi"); + /** + * Returns number of lines in the list. + * @typemask integer() + */ + register_function(vm, generic_get_line_count, "get_count", 1, "x"); end_class(vm); /** * Class to access lines. */ - begin_class(vm, "line_x", "extend_get"); + begin_class(vm, "line_x", "extend_get,ingame_object"); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Line name. * @returns name */ register_method(vm, &simline_t::get_name, "get_name"); + /** + * Sets line name. + * @ingroup rename_func + * @typemask void(string) + */ + register_method(vm, &line_set_name, "set_name", true); /** * Line owner. * @returns owner @@ -155,11 +219,11 @@ void export_line(HSQUIRRELVM vm) * @returns array, index [0] corresponds to current month */ register_method_fv(vm, &get_line_stat, "get_capacity", freevariable(LINE_CAPACITY), true); - /** - * Get monthly statistics of number of transported goods. - * @returns array, index [0] corresponds to current month - */ - register_method_fv(vm, &get_line_stat, "get_transported_goods", freevariable(LINE_PAX_DISTANCE), true ); +// /** +// * Get monthly statistics of number of transported goods. +// * @returns array, index [0] corresponds to current month +// */ +// register_method_fv(vm, &get_line_stat, "get_transported_goods", freevariable(LINE_TRANSPORTED_GOODS), true ); /** * Get monthly statistics of number of convoys in this line. * @returns array, index [0] corresponds to current month @@ -201,5 +265,18 @@ void export_line(HSQUIRRELVM vm) */ register_method(vm, &line_way_type, "get_waytype", true); + /** + * Change schedule of line. + * Schedule should not contain doubled entries and more than two entries. + * @ingroup game_cmd + */ + register_method(vm, line_change_schedule, "change_schedule", true); + + /** + * Delete line + * @ingroup game_cmd + */ + register_method(vm, line_delete, "destroy", true); + end_class(vm); } diff --git a/script/api/api_map_objects.cc b/script/api/api_map_objects.cc index e14c33d5487..5ce2d9f7fc9 100644 --- a/script/api/api_map_objects.cc +++ b/script/api/api_map_objects.cc @@ -14,13 +14,26 @@ #include "../../obj/simobj.h" #include "../../simdepot.h" -#include "../../simtool.h" #include "../../simworld.h" #include "../../boden/grund.h" #include "../../dataobj/scenario.h" +#include "../../descriptor/ground_desc.h" #include "../../obj/baum.h" #include "../../obj/gebaeude.h" +#include "../../obj/field.h" #include "../../obj/label.h" +#include "../../obj/leitung2.h" +#include "../../obj/roadsign.h" +#include "../../obj/signal.h" +#include "../../obj/wayobj.h" +#include "../../player/simplay.h" + +// for depot tools +#include "../../simconvoi.h" +#include "../../simmenu.h" +#include "../../descriptor/building_desc.h" +#include "../../descriptor/vehicle_desc.h" + using namespace script_api; @@ -55,6 +68,10 @@ template struct access_objs { // object or tile disappeared: clear userpointer sq_setinstanceup(vm, index, NULL); } + sq_raise_error(vm, "Object of type %s vanished from (%s).", param::squirrel_type(), pos.get_str()); + } + else { + sq_raise_error(vm, "Object is not of type %s.", param::squirrel_type); } return NULL; } @@ -69,7 +86,7 @@ template struct access_objs { return 1; } koord pos = obj->get_pos().get_2d(); - welt->get_scenario()->koord_w2sq(pos); + coordinate_transform_t::koord_w2sq(pos); sint16 x = pos.x; sint16 y = pos.y; sint8 z = obj->get_pos().z; @@ -79,8 +96,11 @@ template struct access_objs { return SQ_ERROR; } } - else{ - assert(bind_code::objtype == obj->get_typ()); + else if (bind_code::objtype != obj->get_typ()) { + // obj is instance of derived class + return script_api::param::push(vm, obj); + } + else { // specific object with its own constructor, type already preset as default parameter if (!SQ_SUCCEEDED(push_instance(vm, script_api::param::squirrel_type(), x, y, z))) { return SQ_ERROR; @@ -102,14 +122,34 @@ SQInteger exp_obj_pos_constructor(HSQUIRRELVM vm) // parameters: sint16 x, sint1 // set coordinates set_slot(vm, "x", x, 1); set_slot(vm, "y", y, 1); - set_slot(vm, "z", z, 1); koord pos(x,y); - welt->get_scenario()->koord_sq2w(pos); + coordinate_transform_t::koord_sq2w(pos); + // find tile - some objects have larger z-coordinate (e.g., on foundations) + grund_t *gr = welt->lookup(koord3d(pos, z)); + for(uint8 i=1, end = ground_desc_t::double_grounds ? 2 : 1; gr == NULL && i<=end; i++) { + gr = welt->lookup(koord3d(pos, z-i)); + } // find object and set instance up - if (grund_t *gr = welt->lookup(koord3d(pos, z))) { + if (gr) { + // correct z-coordinate + set_slot(vm, "z", gr->get_pos().z, 1); + // search for object obj_t::typ type = (obj_t::typ)param::get(vm, 5); obj_t *obj = NULL; - if (type != obj_t::old_airdepot) { // special treatment of depots + if (type == obj_t::roadsign || type == obj_t::signal) { + // special treatment of signals/roadsigns + obj = gr->suche_obj(obj_t::roadsign); + if (obj == NULL) { + obj = gr->suche_obj(obj_t::signal); + } + } + else if (type == obj_t::pumpe || type == obj_t::senke) { + obj = gr->suche_obj(obj_t::pumpe); + if (obj == NULL) { + obj = gr->suche_obj(obj_t::senke); + } + } + else if (type != obj_t::old_airdepot) { // special treatment of depots obj = gr->suche_obj(type); } else { @@ -142,9 +182,12 @@ getpush_obj_pos(baum_t, obj_t::baum); getpush_obj_pos(gebaeude_t, obj_t::gebaeude); getpush_obj_pos(label_t, obj_t::label); getpush_obj_pos(weg_t, obj_t::way); +getpush_obj_pos(leitung_t, obj_t::leitung); +getpush_obj_pos(field_t, obj_t::field); +getpush_obj_pos(wayobj_t, obj_t::wayobj); -// each depot has its own class namespace script_api { + // each depot has its own class declare_specialized_param(depot_t*, "t|x|y", "depot_x"); declare_specialized_param(airdepot_t*, "t|x|y", "depot_x"); declare_specialized_param(narrowgaugedepot_t*, "t|x|y", "depot_x"); @@ -154,7 +197,16 @@ namespace script_api { declare_specialized_param(monoraildepot_t*, "t|x|y", "depot_x"); declare_specialized_param(tramdepot_t*, "t|x|y", "depot_x"); declare_specialized_param(maglevdepot_t*, "t|x|y", "depot_x"); + + // map roadsign_t and signal_t to the same class + declare_specialized_param(roadsign_t*, "t|x|y", "sign_x"); + declare_specialized_param(signal_t*, "t|x|y", "sign_x"); + + // map all to one transformer class + declare_specialized_param(pumpe_t*, "t|x|y", "transformer_x"); + declare_specialized_param(senke_t*, "t|x|y", "transformer_x"); }; + // base depot class, use old_airdepot as identifier here getpush_obj_pos(depot_t, obj_t::old_airdepot); // now all the derived classes @@ -166,6 +218,13 @@ getpush_obj_pos(schiffdepot_t, obj_t::schiffdepot); getpush_obj_pos(monoraildepot_t, obj_t::monoraildepot); getpush_obj_pos(tramdepot_t, obj_t::tramdepot); getpush_obj_pos(maglevdepot_t, obj_t::maglevdepot); +// roadsigns/signals +getpush_obj_pos(roadsign_t, obj_t::roadsign); +getpush_obj_pos(signal_t, obj_t::signal); +// powerlines +getpush_obj_pos(pumpe_t, obj_t::pumpe); +getpush_obj_pos(senke_t, obj_t::senke); + #define case_resolve_obj(D) \ case bind_code::objtype: \ @@ -184,6 +243,9 @@ SQInteger script_api::param::push(HSQUIRRELVM vm, obj_t* const& obj) case_resolve_obj(gebaeude_t); case_resolve_obj(label_t); case_resolve_obj(weg_t); + case_resolve_obj(roadsign_t); + case_resolve_obj(signal_t); + case_resolve_obj(field_t); case_resolve_obj(airdepot_t); case_resolve_obj(narrowgaugedepot_t); @@ -194,6 +256,11 @@ SQInteger script_api::param::push(HSQUIRRELVM vm, obj_t* const& obj) case_resolve_obj(tramdepot_t); case_resolve_obj(maglevdepot_t); + case_resolve_obj(leitung_t); + case_resolve_obj(pumpe_t); + case_resolve_obj(senke_t); + + case_resolve_obj(wayobj_t); default: return access_objs::push_with_pos(vm, obj); } @@ -212,7 +279,7 @@ static SQInteger get_way_ribi(HSQUIRRELVM vm) ribi_t::ribi ribi = w ? (masked ? w->get_ribi() : w->get_ribi_unmasked() ) : 0; - return push_ribi(vm, ribi); + return param::push(vm, ribi); } // create class @@ -221,9 +288,9 @@ void begin_obj_class(HSQUIRRELVM vm, const char* name, const char* base = NULL) { SQInteger res = create_class(vm, name, base); if( !SQ_SUCCEEDED(res) ) { - // base class not found: maybe scenario_base.nut is not up-to-date - dbg->error( "begin_obj_class()", "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/scenario_base.nut)!", name, base ); - sq_raise_error(vm, "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/scenario_base.nut)!", name, base); + // base class not found: maybe script_base.nut is not up-to-date + dbg->error( "begin_obj_class()", "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/script_base.nut)!", name, base ); + sq_raise_error(vm, "Create class failed for %s. Base class %s missing. Please update simutrans (or just script/script_base.nut)!", name, base); } uint8 objtype = bind_code::objtype; // store typetag to identify pointers @@ -233,41 +300,35 @@ void begin_obj_class(HSQUIRRELVM vm, const char* name, const char* base = NULL) // now functions can be registered } -// markers / labels -label_t* create_marker(koord pos, player_t* player, const char* text) +// mark objects +void mark_object(obj_t* obj) { - if (player == NULL || text == NULL) { - return NULL; - } - tool_marker_t w; - w.flags = 0; - const char* err = w.work(player, koord3d(pos,0)); - if (err) { - return NULL; - } - grund_t *gr = welt->lookup_kartenboden(pos); - gr->set_text(text); - return gr->find(); + obj->set_flag(obj_t::highlight); + obj->set_flag(obj_t::dirty); +} +void unmark_object(obj_t* obj) +{ + obj->clear_flag(obj_t::highlight); + obj->set_flag(obj_t::dirty); +} +bool object_is_marked(obj_t* obj) +{ + return obj->get_flag(obj_t::highlight); } -/* This fails to compile in Visual Studio citing an "ambiguous symbol" error (C2872) - * "return void_t();" also throws error C2955 - */ -/* -void_ label_set_text(label_t* l, const char* text) +// markers / labels +call_tool_work create_marker(koord pos, player_t* player, const char* text) { - if (l == NULL || text == NULL) { - return void_t(); + if (text == NULL) { + return "Cannot create label with text == null"; } - koord3d pos = l->get_pos(); - tool_rename_t w; - cbuffer_t buf; - buf.printf("m%hi,%hi,%hi,%s", pos.x, pos.y, pos.z, text); - w.set_default_param(buf); - w.init(l->get_owner()); + return call_tool_work(TOOL_MARKER | GENERAL_TOOL, text, 0, player, koord3d(pos, 0)); +} - return void_t(); -} */ +call_tool_init label_set_text(label_t *l, const char* text) +{ + return command_rename(l->get_owner(), 'm', l->get_pos(), text); +} const char* label_get_text(label_t* l) { @@ -279,6 +340,40 @@ const char* label_get_text(label_t* l) return NULL; } +// roadsign +bool roadsign_can_pass(const roadsign_t* rs, player_t* player) +{ + return player && rs->get_desc()->is_private_way() ? (rs->get_player_mask() & (1<get_player_nr()))!=0 : true; +} + +// depot +call_tool_init depot_append_vehicle(depot_t *depot, player_t *player, convoihandle_t cnv, const vehicle_desc_t *desc) +{ + if (desc == NULL) { + return "Invalid vehicle_desc_x provided"; + } + // see depot_frame_t::image_from_storage_list: tool = 'a' + // see depot_t::call_depot_tool for command string composition + cbuffer_t buf; + buf.printf( "%c,%s,%hu,%s", 'a', depot->get_pos().get_str(), cnv.get_id(), desc->get_name()); + + return call_tool_init(TOOL_CHANGE_DEPOT | SIMPLE_TOOL, buf, 0, player); +} + +call_tool_init depot_start_convoy(depot_t *depot, player_t *player, convoihandle_t cnv) +{ + // see depot_frame_t::action_triggered: tool = 'b' + // see depot_t::call_depot_tool for command string composition + cbuffer_t buf; + if (cnv.is_bound()) { + buf.printf( "%c,%s,%hu", 'b', depot->get_pos().get_str(), cnv->self.get_id()); + } + else { + buf.printf( "%c,%s,%hu", 'B', depot->get_pos().get_str(), 0); + } + + return call_tool_init(TOOL_CHANGE_DEPOT | SIMPLE_TOOL, buf, 0, player); +} vector_tpl const& depot_get_convoy_list(depot_t *depot) { @@ -296,6 +391,67 @@ vector_tpl const& depot_get_convoy_list(depot_t *depot) return list; } +vector_tpl const& get_depot_list(player_t *player, waytype_t wt) +{ + static vector_tpl list; + list.clear(); + // do the conversion of waytype_t to depot-type by linetype + simline_t::linetype line_type = simline_t::waytype_to_linetype(wt); + if (player == NULL || line_type == simline_t::MAX_LINE_TYPE) { + return list; + } + // fill list + const slist_tpl &depot_list = depot_t::get_depot_list(); + + for(depot_t* d : depot_list) { + if(d->get_line_type()==line_type && d->get_owner()==player) { + list.append(d); + } + } + return list; +} + + +const fabrik_t* transformer_get_factory(leitung_t *lt) +{ + if (pumpe_t *p = dynamic_cast(lt)) { + return p->get_factory(); + } + if (senke_t *s = dynamic_cast(lt)) { + return s->get_factory(); + } + return NULL; +} + +bool leitung_is_connected(leitung_t* lt1, leitung_t* lt2) +{ + return lt2 != NULL && lt1->get_net() == lt2->get_net(); +} + +bool field_is_deleteable(field_t* f) +{ + return f->is_deletable( welt->get_player(1) ) == NULL; +} + + +vector_tpl const& get_way_stat(weg_t* weg, sint32 INDEX) +{ + static vector_tpl v; + v.clear(); + if (weg && 0<=INDEX && INDEXget_statistics((way_stat_months)i, (way_statistics)INDEX) ); + } + } + return v; +} + +vector_tpl const& get_tile_list( gebaeude_t *gb ) +{ + static vector_tpl list; + gb->get_tile_list( list ); + return list; +} void export_map_objects(HSQUIRRELVM vm) { @@ -303,12 +459,13 @@ void export_map_objects(HSQUIRRELVM vm) * Class to access objects on the map * These classes cannot modify anything. */ - begin_class(vm, "map_object_x", "extend_get,coord3d"); + begin_class(vm, "map_object_x", "extend_get,coord3d,ingame_object"); uint8 objtype = bind_code::objtype; sq_settypetag(vm, -1, obj_t_tag + objtype); /** * Constructor. Implemented by derived classes. * Fails if no object of precisely the requested type is on the tile. + * If there is more than one object of this type on the tile then it will return the first. * @param x * @param y * @param z @@ -316,6 +473,10 @@ void export_map_objects(HSQUIRRELVM vm) * @typemask void(integer,integer,integer,map_objects) */ register_function(vm, exp_obj_pos_constructor, "constructor", 5, "xiiii"); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * @returns owner of the object. */ @@ -336,11 +497,23 @@ void export_map_objects(HSQUIRRELVM vm) * Checks whether player can remove this object. * @returns error message or null if object can be removed. */ - register_method(vm, &obj_t:: is_deletable, "is_removable"); + register_method(vm, &obj_t::is_deletable, "is_removable"); /** * @returns type of object. */ register_method(vm, &obj_t::get_typ, "get_type"); + /** + * Marks the object for highlighting. Use with care. + */ + register_method(vm, &mark_object, "mark", true); + /** + * Unmarks the object for highlighting. + */ + register_method(vm, &unmark_object, "unmark", true); + /** + * @returns whether object is highlighted. + */ + register_method(vm, &object_is_marked, "is_marked", true); end_class(vm); @@ -383,25 +556,24 @@ void export_map_objects(HSQUIRRELVM vm) * @returns whether building is a monument */ register_method(vm, &gebaeude_t::is_monument, "is_monument"); - - register_method(vm, &gebaeude_t::get_adjusted_visitor_demand, "get_adjusted_visitor_demand"); - register_method(vm, &gebaeude_t::get_adjusted_mail_demand, "get_adjusted_mail_demand"); - register_method(vm, &gebaeude_t::get_adjusted_jobs, "get_adjusted_jobs"); - -// /** -// * Passenger level controls how many passengers will be generated by this building. -// * @returns passenger level -// */ -// register_method(vm, &gebaeude_t::get_passagier_level, "get_passenger_level"); -// /** -// * Mail level controls how many mail will be generated by this building. -// * @returns mail level -// */ -// register_method(vm, &gebaeude_t::get_mail_level, "get_mail_level"); +// /** +// * Passenger level controls how many passengers will be generated by this building. +// * @returns passenger level +// */ +// register_method(vm, &gebaeude_t::get_passagier_level, "get_passenger_level"); +// /** +// * Mail level controls how many mail will be generated by this building. +// * @returns mail level +// */ +// register_method(vm, &gebaeude_t::get_mail_level, "get_mail_level"); /** * @returns object descriptor. */ register_method(vm, &gebaeude_t::get_tile, "get_desc"); + /** + * @returns coord for all tiles + */ + register_method( vm, &get_tile_list, "get_tile_list", true ); /** * @returns true if both building tiles are part of one (multi-tile) building */ @@ -411,12 +583,38 @@ void export_map_objects(HSQUIRRELVM vm) /** * Class to access depots. + * Main purpose is the manipulation of convoys in depots. */ begin_obj_class(vm, "depot_x", "building_x"); + /** + * Append a car to the convoy in this depot. If the convoy does not exist, a new one is created first. + * @param pl player owns the convoy + * @param cnv the convoy + * @param desc decriptor of the vehicle + * @ingroup game_cmd + */ + register_method(vm, depot_append_vehicle, "append_vehicle", true); + /** + * Start the convoy in this depot. + * @ingroup game_cmd + */ + register_method(vm, depot_start_convoy, "start_convoy", true); + /** + * Start all convoys in this depot. + * @ingroup game_cmd + */ + register_method_fv(vm, depot_start_convoy, "start_all_convoys", freevariable(convoihandle_t()), true); /** * @returns list of convoys sitting in this depot */ register_method(vm, &depot_get_convoy_list, "get_convoy_list", true); + /** + * List of all depots of player @p pl of way-type @p wt + * @param pl + * @param wt + * @returns depot list of player + */ + STATIC register_method(vm, &get_depot_list, "get_depot_list", false, true); end_class(vm); /** @@ -463,6 +661,22 @@ void export_map_objects(HSQUIRRELVM vm) * @returns object descriptor. */ register_method(vm, &weg_t::get_desc, "get_desc"); + /** + * Returns maximal allowed speed on this way. + * Takes limits from crossings, overhead-wires, bridges, etc, into account. + * @returns max speed in kmh. + */ + register_method(vm, &weg_t::get_max_speed, "get_max_speed"); + /** + * Get monthly statistics of goods transported on this way. + * @returns array, index [0] corresponds to current month + */ + register_method_fv(vm, &get_way_stat, "get_transported_goods", freevariable(WAY_STAT_GOODS), true); + /** + * Get monthly statistics of convoys that used this way. + * @returns array, index [0] corresponds to current month + */ + register_method_fv(vm, &get_way_stat, "get_convoys_passed", freevariable(WAY_STAT_CONVOIS), true); end_class(vm); @@ -475,19 +689,15 @@ void export_map_objects(HSQUIRRELVM vm) * @param pos position * @param pl owner * @param text text - * @returns label_x instance or null if creation failed - * @warning cannot be used in network games. + * @ingroup game_cmd */ STATIC register_method(vm, &create_marker, "create", false, true); /** * Set text of marker. * @param text text - * @warning cannot be used in network games. + * @ingroup rename_func */ - - // Must comment this out as this fails to compile in Visual Studio - // FIXME - //register_method(vm, &label_set_text, "set_text", true); + register_method(vm, &label_set_text, "set_text", true); /** * Get text of marker. * @see tile_x::get_text @@ -495,4 +705,73 @@ void export_map_objects(HSQUIRRELVM vm) register_method(vm, &label_get_text, "get_text", true); end_class(vm); + + /** + * Roadsigns and railway signals. + */ + begin_obj_class(vm, "sign_x", "map_object_x"); + /** + * @returns object descriptor. + */ + register_method(vm, &roadsign_t::get_desc, "get_desc"); + + /** + * Test whether @p player 's vehicles can pass private-way sign. + * Returns true if this is not a private-way sign. + * @param player + */ + register_method(vm, &roadsign_can_pass, "can_pass", true); + end_class(vm); + + /** + * Powerlines. + * (Including transformers. Can be differentiated by @ref obj_x::get_type. + */ + begin_obj_class(vm, "powerline_x", "map_object_x"); + + /** + * Checkes whether powerline (or transformer) is connected to @p pl are connected. + * @param pl other powerline/transformer + */ + register_method(vm, &leitung_is_connected, "is_connected", true); + end_class(vm); + + /** + * Transformers. + * Sink and source can be differentiated by @ref obj_x::get_type. + */ + begin_obj_class(vm, "transformer_x", "powerline_x"); + + /** + * Get factory connected to this transformer. + */ + register_method(vm, &transformer_get_factory, "get_factory", true); + end_class(vm); + + /** + * Fields + */ + begin_obj_class(vm, "field_x", "map_object_x"); + /** + * Fields can only be deleted if enough are present. + * @see factory_x::get_field_count, factory_x::get_min_field_count + * @return whether this one can be deleted + */ + register_method(vm, &field_is_deleteable, "is_deletable", true); +// /** +// * @returns factory this field belongs to +// * @see factory_x::get_field_count, factory_x::get_min_field_count +// */ +// register_method(vm, &field_t::get_factory, "get_factory"); + end_class(vm); + + /** + * Way-objects: overhead-wires, fences, etc. Only one such object can exist on a tile. + */ + begin_obj_class(vm, "wayobj_x", "map_object_x"); + /** + * @returns object descriptor. + */ + register_method(vm, &wayobj_t::get_desc, "get_desc"); + end_class(vm); } diff --git a/script/api/api_obj_desc.cc b/script/api/api_obj_desc.cc index 633a1fb4042..69bf2a76236 100644 --- a/script/api/api_obj_desc.cc +++ b/script/api/api_obj_desc.cc @@ -13,10 +13,24 @@ #include "get_next.h" #include "../api_class.h" #include "../api_function.h" +#include "../../descriptor/bridge_desc.h" #include "../../descriptor/building_desc.h" +#include "../../descriptor/vehicle_desc.h" #include "../../descriptor/goods_desc.h" +#include "../../descriptor/roadsign_desc.h" +#include "../../descriptor/way_obj_desc.h" +#include "../../descriptor/factory_desc.h" +#include "../../bauer/brueckenbauer.h" #include "../../bauer/hausbauer.h" +#include "../../bauer/fabrikbauer.h" +#include "../../bauer/tunnelbauer.h" +#include "../../bauer/vehikelbauer.h" #include "../../bauer/goods_manager.h" +#include "../../bauer/wegbauer.h" +#include "../../obj/roadsign.h" +#include "../../obj/wayobj.h" +#include "../../simhalt.h" +#include "../../simware.h" #include "../../simworld.h" #define begin_enum(name) @@ -33,7 +47,7 @@ SQInteger get_next_ware_desc(HSQUIRRELVM vm) } -SQInteger get_ware_desc_index(HSQUIRRELVM vm) +SQInteger get_goods_desc_index(HSQUIRRELVM vm) { uint32 index = param::get(vm, -1); @@ -51,23 +65,43 @@ bool are_equal(const obj_named_desc_t* a, const obj_named_desc_t* b) } +sint64 get_scaled_base_maintenance(const obj_desc_transport_related_t* desc) +{ + return desc ? welt->calc_adjusted_monthly_figure(desc->get_base_maintenance()) : 0; +} + sint64 get_scaled_maintenance(const obj_desc_transport_related_t* desc) { return desc ? welt->calc_adjusted_monthly_figure(desc->get_maintenance()) : 0; } +sint64 get_scaled_maintenance_vehicle(const vehicle_desc_t* desc) +{ + return desc ? welt->scale_with_month_length(desc->vehicle_desc_t::get_maintenance()) : 0; +} sint64 get_scaled_maintenance_building(const building_desc_t* desc) { - return desc ? welt->calc_adjusted_monthly_figure(desc->get_maintenance()) : 0; + return desc ? welt->scale_with_month_length(desc->get_maintenance()) : 0; } -bool building_enables(const building_desc_t* desc, uint16 which) +bool building_enables(const building_desc_t* desc, uint8 which) { return desc ? desc->get_enabled() & which : 0; } +SQInteger building_get_size(HSQUIRRELVM vm) +{ + const building_desc_t* desc = param::get(vm, 1); + uint8 rotation = param::get(vm, 2); + if (desc) { + koord size = desc->get_size(rotation); + // no automatic coordinate transform please + return push_instance(vm, "coord", size.x, size.y); + } + return -1; +} mytime_t get_intro_retire(const obj_desc_timelined_t* desc, bool intro) { @@ -99,20 +133,208 @@ const vector_tpl& get_building_list(building_desc_t::bty } +const vector_tpl& get_available_stations(building_desc_t::btype type, waytype_t wt, const goods_desc_t *freight) +{ + static vector_tpl dummy; + dummy.clear(); + + switch(type) { + case building_desc_t::depot: + case building_desc_t::generic_stop: + case building_desc_t::generic_extension: + case building_desc_t::dock: + case building_desc_t::flat_dock: + break; + default: + return dummy; + } + + const vector_tpl* p = hausbauer_t::get_list(type); + + // translate freight to enables-flags + uint8 enables = 0; + if (freight && type != building_desc_t::depot) { + switch(freight->get_catg_index()) { + case goods_manager_t::INDEX_PAS: enables = haltestelle_t::PAX; break; + case goods_manager_t::INDEX_MAIL: enables = haltestelle_t::POST; break; + default: enables = haltestelle_t::WARE; + } + } + + // use wt_all (which is equal to invalid_wt, see api_const.cc) to get buildings for all waytypes + bool accept_all_wt = wt == invalid_wt || wt == ignore_wt || wt == any_wt; + + uint16 time = welt->get_timeline_year_month(); + for(building_desc_t const* const desc : *p) { + if( desc->get_type()==type + && (accept_all_wt || desc->get_extra()==(uint32)wt) + && (enables==0 || (desc->get_enabled()&enables)!=0) + && desc->is_available(time)) + { + + dummy.append(desc); + } + } + return dummy; +} + +sint64 building_get_cost(const building_desc_t* desc) +{ + return desc->get_price() * desc->get_x() * desc->get_y(); +} + +bool building_is_terminus(const building_desc_t *desc) +{ + return desc && desc->get_type() == building_desc_t::generic_stop && desc->get_all_layouts() == 4; +} + +bool can_be_first(const vehicle_desc_t *desc) +{ + return desc->can_follow(NULL); +} + +bool can_be_last(const vehicle_desc_t *desc) +{ + return desc->can_lead(NULL); +} + +bool is_coupling_allowed(const vehicle_desc_t *desc1, const vehicle_desc_t *desc2) +{ + return desc1->can_lead(desc2) && desc2->can_follow(desc1); +} + +const vector_tpl& get_predecessors(const vehicle_desc_t *desc) +{ + static vector_tpl dummy; + dummy.clear(); + for(int i=0; iget_leader_count(); i++) { + if (desc->get_leader(i)) { + dummy.append(desc->get_leader(i)); + } + } + return dummy; +} + +const vector_tpl& get_successors(const vehicle_desc_t *desc) +{ + static vector_tpl dummy; + dummy.clear(); + for(int i=0; iget_trailer_count(); i++) { + if (desc->get_trailer(i)) { + dummy.append(desc->get_trailer(i)); + } + } + return dummy; +} + +const vector_tpl& get_available_vehicles(waytype_t wt) +{ + static vector_tpl dummy; + + bool use_obsolete = welt->get_settings().get_allow_buying_obsolete_vehicles(); + uint16 time = welt->get_timeline_year_month(); + + dummy.clear(); + slist_tpl const& list = vehicle_builder_t::get_info(wt); + + for(vehicle_desc_t * const i : list) { + if (!i->is_retired(time) || use_obsolete) { + if (!i->is_future(time)) { + dummy.append(i); + } + } + } + return dummy; +} + +uint32 get_power(const vehicle_desc_t *desc) +{ + return desc->get_power() * desc->get_gear(); +} + +bool vehicle_needs_electrification(const vehicle_desc_t *desc) +{ + return desc->get_power() && (desc->get_engine_type()==vehicle_desc_t::electric); +} + // export of building_desc_t::btype only here namespace script_api { - declare_specialized_param(building_desc_t::btype, "i", "building_desc_x::building_type"); + declare_enum_param(building_desc_t::btype, uint16, "building_desc_x::building_type"); +}; + +bool is_traffic_light(const roadsign_desc_t *d) +{ + return !d->is_signal_type() && d->is_traffic_light(); +} + + +sint64 tree_get_price() +{ + return -welt->get_settings().cst_remove_tree; +} + + +const vector_tpl& get_available_wayobjs(waytype_t wt) +{ + static vector_tpl dummy; + + uint16 time = welt->get_timeline_year_month(); + + dummy.clear(); +// for(auto i : wayobj_t::get_list()) { +// const way_obj_desc_t* desc = i.value; +// if (desc->get_waytype()==wt && desc->is_available(time)) { +// dummy.append(desc); +// } +// } + return dummy; +} + - SQInteger param::push(HSQUIRRELVM vm, const building_desc_t::btype & u) - { - return param::push(vm, u); +#ifdef DOXYGEN +/** + * Description of input/output slots + */ +struct factory_slot_information_x { // begin_class("factory_slot_information_x") + good_desc_x good; ///< type of produced/consumed good + integer capacity; ///< capacity to store the good + integer factor; ///< production/consumption factor +}; // end_class +#endif + +SQInteger get_factory_outputs(HSQUIRRELVM vm) +{ + const factory_desc_t *desc = param::get(vm, -1); + sq_newarray(vm, 0); + if (desc) { + for(uint16 i=0; iget_product_count(); i++) { + const factory_product_desc_t *fp = desc->get_product(i); + sq_newtable(vm); + create_slot(vm, "good", fp->get_output_type()); + create_slot(vm, "capacity", fp->get_capacity()); + create_slot(vm, "factor", fp->get_factor()); + sq_arrayappend(vm, -2); + } } + return 1; +} - building_desc_t::btype param::get(HSQUIRRELVM vm, SQInteger index) - { - return (building_desc_t::btype)param::get(vm, index); +SQInteger get_factory_inputs(HSQUIRRELVM vm) +{ + const factory_desc_t *desc = param::get(vm, -1); + sq_newarray(vm, 0); + if (desc) { + for(uint16 i=0; iget_supplier_count(); i++) { + const factory_supplier_desc_t *fp = desc->get_supplier(i); + sq_newtable(vm); + create_slot(vm, "good", fp->get_input_type()); + create_slot(vm, "capacity", fp->get_capacity()); + create_slot(vm, "factor", fp->get_consumption()); + sq_arrayappend(vm, -2); + } } -}; + return 1; +} void export_goods_desc(HSQUIRRELVM vm) @@ -133,6 +355,10 @@ void export_goods_desc(HSQUIRRELVM vm) * @return true if this==other */ register_method(vm, &are_equal, "is_equal", true); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") end_class(vm); /** @@ -170,11 +396,16 @@ void export_goods_desc(HSQUIRRELVM vm) */ create_class(vm, "obj_desc_transport_x", "obj_desc_time_x"); /** - * @returns monthly maintenance cost of one object of this type. + * @returns monthly maintenance cost [in 1/100 credits] of one object of this type. + */ + register_local_method(vm, &get_scaled_base_maintenance, "get_base_maintenance"); + + /** + * @returns monthly maintenance cost [in 1/100 credits] of one km object of this type. */ register_local_method(vm, &get_scaled_maintenance, "get_maintenance"); /** - * @returns cost to buy or build on piece or tile of this thing. + * @returns cost [in 1/100 credits] to buy or build one piece or tile of this thing. */ register_method(vm, &obj_desc_transport_related_t::get_value, "get_cost"); /** @@ -185,21 +416,92 @@ void export_goods_desc(HSQUIRRELVM vm) * @returns top speed: maximal possible or allowed speed, in km/h. */ register_method(vm, &obj_desc_transport_related_t::get_topspeed, "get_topspeed"); - register_method(vm, &obj_desc_transport_related_t::get_topspeed_gradient_1, "get_topspeed_gradient_1"); - register_method(vm, &obj_desc_transport_related_t::get_topspeed_gradient_2, "get_topspeed_gradient_2"); end_class(vm); + /** + * Vehicle descriptors + */ + begin_desc_class(vm, "vehicle_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); + /** + * @returns true if this vehicle can lead a convoy + */ + register_method(vm, &can_be_first, "can_be_first", true); + /** + * @returns true if this vehicle can be the last of a convoy + */ + register_method(vm, &can_be_last, "can_be_last", true); + /** + * @returns list of possible successors, if all are allowed then list is empty + */ + register_method(vm, &get_successors, "get_successors", true); + /** + * @returns list of possible predecessors, if all are allowed then list is empty + */ + register_method(vm, &get_predecessors, "get_predecessors", true); + /** + * @returns a list of all available vehicles at the current in-game-time + */ + STATIC register_method(vm, &get_available_vehicles, "get_available_vehicles", false, true); + /** + * Power of the vehicle. This value can be used in convoy_x::calc_max_speed. + * It returns (power of vehicle in kW) * (gear value) * 64, where power and gear are as shown in-game. + * @returns the total power of the vehicle (takes power and gear from pak-files into account) + */ + register_method(vm, &get_power, "get_power", true); + /** + * @returns true if this vehicle needs electrification (and is powered) + */ + register_method(vm, &vehicle_needs_electrification, "needs_electrification", true); + /** + * @returns freight that can be transported (or null) + */ + register_method(vm, &vehicle_desc_t::get_freight_type, "get_freight"); + /** + * @returns capacity + */ + register_method(vm, &vehicle_desc_t::get_capacity, "get_capacity"); +// /** +// * @returns running cost in 1/100 credits per tile +// */ +// register_method(vm, &vehicle_desc_t::get_running_cost, "get_running_cost"); + /** + * @returns fixed cost in 1/100 credits per month + */ + register_method(vm, &get_scaled_maintenance_vehicle, "get_maintenance", true); + /** + * @returns weight of the empty vehicle + */ + register_method(vm, &vehicle_desc_t::get_weight, "get_weight"); // in kg + /** + * @returns lengths in @ref units::CARUNITS_PER_TILE + */ + register_method(vm, &vehicle_desc_t::get_length, "get_length"); // in CAR_UNITS_PER_TILE + /** + * Checks if the coupling of @p first and @p second is possible in this order. + * @param first + * @param second + * @returns true if coupling is possible + */ + STATIC register_method(vm, is_coupling_allowed, "is_coupling_allowed", false, true); + end_class(vm); + /** * Object descriptors for trees. */ - begin_desc_class(vm, "tree_desc_x", "obj_desc_x", (GETBESCHFUNC)param::getfunc()); + begin_desc_class(vm, "tree_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); + + /** + * Returns price to fell one tree. + * @returns price (should be positive) + */ + STATIC register_method(vm, tree_get_price, "get_price", false, true); end_class(vm); /** * Object descriptors for buildings: houses, attractions, stations and extensions, depots, harbours. */ - begin_desc_class(vm, "building_desc_x", "obj_desc_time_x", (GETBESCHFUNC)param::getfunc()); + begin_desc_class(vm, "building_desc_x", "obj_desc_time_x", (GETDESCFUNC)param::getfunc()); /** * @returns whether building is an attraction @@ -208,16 +510,18 @@ void export_goods_desc(HSQUIRRELVM vm) /** * @param rotation * @return size of building in the given @p rotation + * @typemask coord(integer) */ - register_method(vm, &building_desc_t::get_size, "get_size"); + register_function(vm, building_get_size, "get_size", 2, "x i"); /** * @return monthly maintenance cost */ register_method(vm, &get_scaled_maintenance_building, "get_maintenance", true); /** - * @return price to build this building + * Price to build this building, takes size, level, and type into account. + * @return price [in 1/100 credits] to build this building */ - register_method_fv(vm, &building_desc_t::get_price, "get_cost", freevariable(welt)); + register_method(vm, &building_get_cost, "get_cost", true); /** * @return capacity */ @@ -255,15 +559,13 @@ void export_goods_desc(HSQUIRRELVM vm) /// townhall enum_slot(vm, "townhall", (uint8)building_desc_t::townhall, true); /// company headquarters - enum_slot(vm, "headquarters", (uint8)building_desc_t::headquarters, true); + enum_slot(vm, "headquarter", (uint8)building_desc_t::headquarters, true); /// harbour enum_slot(vm, "harbour", (uint8)building_desc_t::dock, true); /// harbour without a slope (buildable on flat ground beaches) enum_slot(vm, "flat_harbour", (uint8)building_desc_t::flat_dock, true); /// depot enum_slot(vm, "depot", (uint8)building_desc_t::depot, true); - /// signalbox - enum_slot(vm, "signalbox", (uint8)building_desc_t::signalbox, true); /// station enum_slot(vm, "station", (uint8)building_desc_t::generic_stop, true); /// station extension @@ -289,14 +591,71 @@ void export_goods_desc(HSQUIRRELVM vm) * @warning If @p type is one of building_desc_x::harbour, building_desc_x::depot, building_desc_x::station, building_desc_x::station_extension then always the same list is generated. * You have to filter out e.g. station buildings yourself. */ - STATIC register_method(vm, &get_building_list, "get_building_list"); + STATIC register_method(vm, &get_building_list, "get_building_list", false, true); + /** + * Returns an array of available station/extension/depot buildings. + * Entries are of type @ref building_desc_x. + * @param type building type from @ref building_desc_x::building_type + * @param wt waytype (can be wt_all to ignore waytype of desc) + * @param freight station should accept this freight (if null then return all buildings) + * @returns the list + */ + STATIC register_method(vm, &get_available_stations, "get_available_stations", false, true); + + /** + * @returns true if this is a station building that can be used as terminus + */ + register_method(vm, &building_is_terminus, "is_terminus", true); + + end_class(vm); + + /** + * Object descriptors for factories. + */ + begin_desc_class(vm, "factory_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); + /** + * @returns name + */ + register_method(vm, &factory_desc_t::get_name, "get_name"); + /** + * Descriptor of associated building. + */ + register_method(vm, &factory_desc_t::get_building, "get_building_desc"); + /** + * @returns true if this factory produces electricity + */ + register_method(vm, &factory_desc_t::is_electricity_producer, "is_electricity_producer"); + /** + * Initial production of a factory is get_productivity_base() + rand( get_productivity_range() ). + * @returns base production + */ + register_method(vm, &factory_desc_t::get_productivity, "get_productivity_base"); + /** + * @returns base production variable range + */ + register_method(vm, &factory_desc_t::get_range, "get_productivity_range"); + /** + * Returns array with information about input goods + * @typemask array () + */ + register_function(vm, get_factory_inputs, "get_inputs", 1, "t|x|y"); + /** + * Returns array with information about input goods + * @typemask array () + */ + register_function(vm, get_factory_outputs, "get_outputs", 1, "t|x|y"); + /** + * Returns table with all factory types. + * The factory names are used as table keys. + */ + register_method(vm, &factory_builder_t::get_factory_table, "get_list", 1); end_class(vm); /** * Object descriptors for ways. */ - begin_desc_class(vm, "way_desc_x", "obj_desc_transport_x", (GETBESCHFUNC)param::getfunc()); + begin_desc_class(vm, "way_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); /** * @returns true if this way can be build on the steeper (double) slopes. */ @@ -306,6 +665,51 @@ void export_goods_desc(HSQUIRRELVM vm) */ register_method(vm, &way_desc_t::get_styp, "get_system_type"); + /** + * Generates a list of all ways available for building. + * @param wt waytype + * @param st system type of way + * @returns the list + */ + STATIC register_method(vm, way_builder_t::get_way_list, "get_available_ways", false, true); + + end_class(vm); + + /** + * Object descriptors for tunnels. + */ + begin_desc_class(vm, "tunnel_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); +// /** +// * Returns a list with available tunnel types. +// */ +// STATIC register_method(vm, tunnel_builder_t::get_available_tunnels, "get_available_tunnels", false, true); + end_class(vm); + + /** + * Object descriptors for bridges. + */ + begin_desc_class(vm, "bridge_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); + /** + * @return true if this bridge can raise two level from flat terrain + */ + register_method(vm, &bridge_desc_t::has_double_ramp, "has_double_ramp"); + /** + * @return true if this bridge can start or end on a double slope + */ + register_method(vm, &bridge_desc_t::has_double_start, "has_double_start"); + /** + * @return maximal bridge length in tiles + */ + register_method(vm, &bridge_desc_t::get_max_length, "get_max_length"); + /** + * @return maximal bridge height + */ + register_method(vm, &bridge_desc_t::get_max_height, "get_max_height"); +// /** +// * Returns a list with available bridge types. +// */ + STATIC register_method(vm, bridge_builder_t::get_available_bridges, "get_available_bridges", false, true); + end_class(vm); /** @@ -327,14 +731,14 @@ void export_goods_desc(HSQUIRRELVM vm) /** * Meta-method to be used in foreach loops. Do not call them directly. */ - register_function(vm, get_ware_desc_index, "_get", 2, "xi"); + register_function(vm, get_goods_desc_index, "_get", 2, "xi"); end_class(vm); /** * Descriptor of goods and freight types. */ - begin_desc_class(vm, "good_desc_x", "obj_desc_x", (GETBESCHFUNC)param::getfunc()); + begin_desc_class(vm, "good_desc_x", "obj_desc_x", (GETDESCFUNC)param::getfunc()); // dummy entry to create documentation of constructor /** @@ -344,11 +748,99 @@ void export_goods_desc(HSQUIRRELVM vm) */ // register_function( .., "constructor", .. ) + create_slot(vm, "passenger", goods_manager_t::passengers, true); + create_slot(vm, "mail", goods_manager_t::mail, true); +#ifdef DOXYGEN + static good_desc_x passenger; ///< descriptor for passenger + static good_desc_x mail; ///< descriptor for mail +#endif /** * @return freight category. 0=Passengers, 1=Mail, 2=None, >=3 anything else */ register_method(vm, &goods_desc_t::get_catg_index, "get_catg_index"); + /** + * Checks if this good can be interchanged with the other, in terms of + * transportability. + */ + register_method(vm, &goods_desc_t::is_interchangeable, "is_interchangeable"); + /** + * @returns weight of one unit of this freight + */ + register_method(vm, &goods_desc_t::get_weight_per_unit, "get_weight_per_unit"); // in kg + +// /** +// * Calculates transport revenue per tile and freight unit. +// * Takes speedbonus into account. +// * Value contains an additional factor of 3000. Don't ask. +// * Divide by 3000 *after* calculating revenue for a loaded convoy. +// * @param wt waytype of vehicle +// * @param speedkmh actual achieved speed in km/h +// * @returns revenue +// */ +// register_method(vm, &ware_t::calc_revenue, "calc_revenue", true); + end_class(vm); + + /** + * Descriptor of roadsigns and signals. + */ + begin_desc_class(vm, "sign_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); + /** + * @returns true if sign is one-way sign + */ + register_method(vm, &roadsign_desc_t::is_single_way, "is_one_way"); + /** + * @returns true if sign is private-way sign + */ + register_method(vm, &roadsign_desc_t::is_private_way, "is_private_way"); + /** + * @returns true if sign is traffic light + */ + register_method(vm, &is_traffic_light, "is_traffic_light", true); + /** + * @returns true if sign is choose sign + */ + register_method(vm, &roadsign_desc_t::is_choose_sign, "is_choose_sign"); +// /** +// * @returns true if sign is signal +// */ +// register_method(vm, &roadsign_desc_t::is_simple_signal, "is_signal"); + /** + * @returns true if sign is pre signal (distant signal) + */ + register_method(vm, &roadsign_desc_t::is_pre_signal, "is_pre_signal"); +// /** +// * @returns true if sign is priority signal +// */ +// register_method(vm, &roadsign_desc_t::is_priority_signal, "is_priority_signal"); + /** + * @returns true if sign is long-block signal + */ + register_method(vm, &roadsign_desc_t::is_longblock_signal, "is_longblock_signal"); + /** + * @returns true if sign is end-of-choose sign + */ + register_method(vm, &roadsign_desc_t::is_end_choose_signal, "is_end_choose_signal"); +// /** +// * Returns a list with available sign types. +// * @param wt waytype +// */ +// STATIC register_method(vm, roadsign_t::get_available_signs, "get_available_signs", false, true); + + end_class(vm); + /** + * Descriptor of way-objects. + */ + begin_desc_class(vm, "wayobj_desc_x", "obj_desc_transport_x", (GETDESCFUNC)param::getfunc()); + /** + * @returns true for over-head lines. + */ + register_method(vm, &way_obj_desc_t::is_overhead_line, "is_overhead_line"); + /** + * Returns a list with available wayobj-types. + * @param wt waytype + */ + STATIC register_method(vm, get_available_wayobjs, "get_available_wayobjs", false, true); end_class(vm); } diff --git a/script/api/api_obj_desc_base.cc b/script/api/api_obj_desc_base.cc index 4056eb64f54..48121b3ecc3 100644 --- a/script/api/api_obj_desc_base.cc +++ b/script/api/api_obj_desc_base.cc @@ -5,11 +5,22 @@ #include "api_obj_desc_base.h" +#include "../../bauer/brueckenbauer.h" +#include "../../bauer/fabrikbauer.h" #include "../../bauer/hausbauer.h" +#include "../../bauer/tunnelbauer.h" +#include "../../bauer/vehikelbauer.h" #include "../../bauer/goods_manager.h" #include "../../bauer/wegbauer.h" +#include "../../descriptor/bridge_desc.h" +#include "../../descriptor/tunnel_desc.h" +#include "../../descriptor/vehicle_desc.h" #include "../../descriptor/goods_desc.h" +#include "../../descriptor/way_desc.h" +#include "../../descriptor/way_obj_desc.h" #include "../../obj/baum.h" +#include "../../obj/roadsign.h" +#include "../../obj/wayobj.h" #include "../../squirrel/sq_extensions.h" using namespace script_api; @@ -23,6 +34,12 @@ implement_desc_param(tree_desc_t, "tree_desc_x", &tree_builder_t::find_tree); implement_desc_param(building_desc_t, "building_desc_x", &hausbauer_t::get_desc); implement_desc_param(goods_desc_t, "good_desc_x", (const goods_desc_t* (*)(const char*))(&goods_manager_t::get_info) ); implement_desc_param(way_desc_t, "way_desc_x", &my_get_desc); +implement_desc_param(vehicle_desc_t, "vehicle_desc_x", &vehicle_builder_t::get_info); +implement_desc_param(tunnel_desc_t, "tunnel_desc_x", &tunnel_builder_t::get_desc); +implement_desc_param(bridge_desc_t, "bridge_desc_x", &bridge_builder_t::get_desc); +implement_desc_param(roadsign_desc_t, "sign_desc_x", &roadsign_t::find_desc); +implement_desc_param(way_obj_desc_t, "wayobj_desc_x", &wayobj_t::find_desc); +implement_desc_param(factory_desc_t, "factory_desc_x", &factory_builder_t::get_desc); /** * Macro to get the implementation of get method based on unique tag. diff --git a/script/api/api_obj_desc_base.h b/script/api/api_obj_desc_base.h index 10e5b3d0467..374532023a8 100644 --- a/script/api/api_obj_desc_base.h +++ b/script/api/api_obj_desc_base.h @@ -16,10 +16,16 @@ class obj_named_desc_t; class obj_desc_timelined_t; class obj_desc_transport_related_t; class tree_desc_t; +class bridge_desc_t; class building_desc_t; class building_tile_desc_t; +class factory_desc_t; class goods_desc_t; +class roadsign_desc_t; +class tunnel_desc_t; +class vehicle_desc_t; class way_desc_t; +class way_obj_desc_t; namespace script_api { @@ -59,7 +65,7 @@ namespace script_api { SQInteger param::push(HSQUIRRELVM vm, const T* b) \ { \ if (b) { \ - return push_instance(vm, sqtype, b->get_name()); \ + return push_instance_up(vm, b); \ } \ else { \ sq_pushnull(vm); return 1; \ @@ -79,6 +85,12 @@ namespace script_api { declare_desc_param(goods_desc_t, "good_desc_x"); declare_desc_param(building_desc_t, "building_desc_x"); declare_desc_param(way_desc_t, "way_desc_x"); + declare_desc_param(vehicle_desc_t, "vehicle_desc_x"); + declare_desc_param(tunnel_desc_t, "tunnel_desc_x"); + declare_desc_param(bridge_desc_t, "bridge_desc_x"); + declare_desc_param(roadsign_desc_t, "sign_desc_x"); + declare_desc_param(way_obj_desc_t, "wayobj_desc_x"); + declare_desc_param(factory_desc_t, "factory_desc_x"); // only push the building_desc_t-pointer declare_desc_param(building_tile_desc_t, "building_desc_x"); diff --git a/script/api/api_pathfinding.cc b/script/api/api_pathfinding.cc new file mode 100644 index 00000000000..5ee8bc21197 --- /dev/null +++ b/script/api/api_pathfinding.cc @@ -0,0 +1,239 @@ +/* + * This file is part of the Simutrans-Extended project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "api.h" + +/** @file api_pathfinding.cc exports heap structure and construction helpers. */ + +#include "api_obj_desc_base.h" +#include "api_simple.h" +#include "../api_class.h" +#include "../api_function.h" +#include "../../bauer/brueckenbauer.h" +#include "../../bauer/wegbauer.h" +#include "../../descriptor/bridge_desc.h" +#include "../../descriptor/way_desc.h" +#include "../../tpl/binary_heap_tpl.h" +#include "../../simworld.h" + + +using namespace script_api; + + +struct heap_node_t { + sint32 weight; + sint32 value; + + heap_node_t(sint32 w, sint32 v) : weight(w), value(v) {} + // dereferencing to be used in binary_heap_tpl + inline sint32 operator * () const { return weight; } +}; + +typedef binary_heap_tpl simple_heap_t; + +namespace script_api{ + declare_specialized_param(heap_node_t, "t|x|y", "simple_heap_x::node_x"); + declare_specialized_param(simple_heap_t*, "t|x|y", "simple_heap_x"); +}; + +SQInteger heap_constructor(HSQUIRRELVM vm) +{ + simple_heap_t* heap = new simple_heap_t(); + attach_instance(vm, 1, heap); + return SQ_OK; +} + +void* script_api::param::tag() +{ + return (void*)&heap_constructor; +} + +simple_heap_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) +{ + simple_heap_t *heap = get_attached_instance(vm, index, param::tag()); + return heap; +} + +SQInteger param::push(HSQUIRRELVM vm, heap_node_t const& v) +{ + sq_newtable(vm); + create_slot(vm, "weight", v.weight); + create_slot(vm, "value", v.value); + return 1; +} + +void simple_heap_insert(simple_heap_t *heap, sint32 weight, sint32 value) +{ + heap->insert( heap_node_t(weight, value) ); +} + +SQInteger simple_heap_pop(HSQUIRRELVM vm) // instance +{ + simple_heap_t *heap = param::get(vm, 1); + if (heap) { + if (!heap->empty()) { + return param::push(vm, heap->pop()); + } + else { + return sq_raise_error(vm, "Called pop() on empty heap"); + } + } + else { + return sq_raise_error(vm, "Not a heap instance"); // should not happen + } +} + + +SQInteger way_builder_constructor(HSQUIRRELVM vm) // instance, player +{ + // get player parameter + player_t *player = get_my_player(vm); + if (player == NULL) { + player = param::get(vm, 2); + } + if (player == NULL) { + return SQ_ERROR; + } + way_builder_t* bob = new way_builder_t(player); + + bob->set_keep_existing_faster_ways(true); + bob->set_keep_city_roads(true); + + attach_instance(vm, 1, bob); + return SQ_OK; +} + +void way_builder_set_build_types(way_builder_t *bob, const way_desc_t *way) +{ + if (way) { + bob->init_builder( (way_builder_t::bautyp_t)way->get_waytype(), way, NULL /*tunnel*/, NULL /*bridge*/); + } +} + +bool way_builder_is_allowed_step(way_builder_t *bob, grund_t *from, grund_t *to) +{ + if (from == NULL || to == NULL) { + return false; + } + sint32 costs = 0; + return bob->is_allowed_step(from, to, &costs); +} + + +koord3d bridge_builder_find_end_pos(player_t *player, koord3d pos, my_ribi_t mribi, const bridge_desc_t *bridge, uint32 min_length) +{ + const char* err; + sint8 height; + ribi_t::ribi ribi(mribi); + + if (player == NULL || bridge == NULL) { + return koord3d::invalid; + } + grund_t *from = welt->lookup(pos); + if (from == NULL || !bridge_builder_t::can_place_ramp(player, from, bridge->get_wtyp(), ribi_t::backward(ribi))) { + return koord3d::invalid; + } + return bridge_builder_t::find_end_pos(player, pos, ribi, bridge, err, height, false, min_length, false); +} + + +void export_pathfinding(HSQUIRRELVM vm) +{ + /** + * Exports a binary heap structure with + * with simple nodes. + */ + create_class(vm, "simple_heap_x", 0); + /** + * Constructor + */ + register_function(vm, heap_constructor, "constructor", 1, "x"); + sq_settypetag(vm, -1, param::tag()); + + /// Clears the heap. + register_method(vm, &simple_heap_t::clear, "clear"); + /// @returns number of nodes in heap + register_method(vm, &simple_heap_t::get_count, "len"); + /// @returns true when heap is empty + register_method(vm, &simple_heap_t::empty, "is_empty"); + /** + * Inserts a node into the heap. + * @param weight heap is sorted with respect to weight + * @param value the data to be stored + */ + register_method(vm, simple_heap_insert, "insert", true); + /** + * Pops the top node of the heap. + * Raises error if heap is empty. + * @returns top node + */ + register_function(vm, simple_heap_pop, "pop", 1, "x"); +#ifdef SQAPI_DOC + /** + * Helper node class. + */ + class node_x { + int weight; ///< heap is sorted with respect to weight + int value; ///< the data to be stored + } +#endif + end_class(vm); + + /** + * Class with helper methods for way planning. + */ + create_class(vm, "way_planner_x", 0); + /** + * Constructor + * @param pl player that wants to plan, for ai scripts is set to ai player_x::self + * @typemask way_planner_x(player_x) + */ + register_function(vm, way_builder_constructor, "constructor", 2, "xx"); + sq_settypetag(vm, -1, param::tag()); + + /** + * Sets way descriptor, which is needed for @ref is_allowed_step. + * @param way descriptor of way to be planned. + */ + register_method(vm, way_builder_set_build_types, "set_build_types", true); + /** + * Checks if player can build way from @p from to @p to. + * @param from from here + * @param to to here, @p from and @p to must be adjacent. + */ + register_method(vm, way_builder_is_allowed_step, "is_allowed_step", true); + + end_class(vm); + + /** + * Class with helper methods for bridge planning. + */ + create_class(vm, "bridge_planner_x", 0); + /** + * Find suitable end tile for bridge starting at @p pos going into direction @p dir. + * @param pl who wants to build a bridge + * @param pos start tile for bridge + * @param dir direction + * @param bridge bridge descriptor + * @param min_length bridge should have this minimal length + * @returns coordinate of end tile or an invalid coordinate + */ + STATIC register_method(vm, bridge_builder_find_end_pos, "find_end", false, true); + + end_class(vm); +} + + +void* script_api::param::tag() +{ + return (void*)&way_builder_constructor; +} + +way_builder_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) +{ + way_builder_t *bob = get_attached_instance(vm, index, param::tag()); + return bob; +} + diff --git a/script/api/api_player.cc b/script/api/api_player.cc index 1e4ec55af3e..74d78513a28 100644 --- a/script/api/api_player.cc +++ b/script/api/api_player.cc @@ -12,6 +12,11 @@ #include "../../player/simplay.h" #include "../../player/finance.h" +// for creation of lines +#include "../../simline.h" +#include "../../simmenu.h" +#include "../../simworld.h" + using namespace script_api; @@ -55,19 +60,26 @@ vector_tpl const& get_player_stat(player_t *player, sint32 INDEX, sint32 return v; } -script_api::void_t change_player_account(player_t *player, sint64 delta) -{ - if (player) { - player->get_finance()->book_account(delta); - } - return script_api::void_t(); -} +// export of finance_t only here +namespace script_api { + declare_specialized_param(finance_t*, param::typemask(), param::squirrel_type()); -bool player_active(player_t *player) -{ - return player != NULL; -} + finance_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + player_t *player = param::get(vm, index); + return player ? player->get_finance() : NULL; + } +}; +// also export transport_type - to get correct parameters +namespace script_api { + declare_specialized_param(transport_type, "i", "integer"); + + transport_type param::get(HSQUIRRELVM vm, SQInteger index) + { + return (transport_type) min(param::get(vm, index), TT_MAX-1); + } +}; SQInteger player_export_line_list(HSQUIRRELVM vm) @@ -80,27 +92,71 @@ SQInteger player_export_line_list(HSQUIRRELVM vm) return SQ_ERROR; } -void export_player(HSQUIRRELVM vm) +call_tool_init player_create_line(player_t *player, waytype_t wt) +{ + simline_t::linetype lt = simline_t::waytype_to_linetype(wt); + if (lt == simline_t::MAX_LINE_TYPE) { + return "Invalid waytype provided"; + } + // build param string (see schedule_list_gui_t::action_triggered) + cbuffer_t buf; + buf.printf( "c,0,%i,0,0|%i|", lt, lt); + return call_tool_init(TOOL_CHANGE_LINE | SIMPLE_TOOL, buf, 0, player); +} + +call_tool_init player_book_account(player_t *player, sint32 delta) +{ + // build param string (see tool_change_player_t) + cbuffer_t buf; + buf.printf( "$,%i,%i", player->get_player_nr(), delta); + return call_tool_init(TOOL_CHANGE_PLAYER | SIMPLE_TOOL, buf, 0, welt->get_public_player()); +} + +SQInteger player_get_my_player(HSQUIRRELVM vm) +{ + return script_api::param::push(vm, get_my_player(vm) ); +} + +call_tool_init player_set_name(player_t *player, const char* name) +{ + return script_api::command_rename(player, 'p', player->get_player_nr(), name); +} + +void export_player(HSQUIRRELVM vm, bool scenario) { /** * Class to access player statistics. * Here, a player refers to one transport company, not to an individual playing simutrans. */ - begin_class(vm, "player_x", "extend_get"); + begin_class(vm, "player_x", "extend_get,ingame_object"); /** * Constructor. * @param nr player number, 0 = standard player, 1 = public player * @typemask (integer) */ - // actually defined simutrans/script/scenario_base.nut + // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); /** - * Return headquarters level. - * @returns level, level is zero if no headquarters was built + * @returns if object is still valid. */ - register_method(vm, &player_t::get_headquarters_level, "get_headquarters_level"); + export_is_valid(vm); //register_function("is_valid") + + if (!scenario) { + /** + * @returns player associated with the AI. + * @ingroup ai_only + * @typemask player_x() + * @note Only available in AI mode. + */ + STATIC register_function(vm, &player_get_my_player, "self", 0, "", true); + } +// /** +// * Return headquarters level. +// * @returns level, level is zero if no headquarters was built +// */ +// register_method(vm, &player_t::get_headquarter_level, "get_headquarter_level"); /** * Return headquarters position. * @returns coordinate, (-1,-1) if no headquarters was built @@ -111,81 +167,87 @@ void export_player(HSQUIRRELVM vm) * @returns name */ register_method(vm, &player_t::get_name, "get_name"); + /** + * Sets name of company. + * @param name the new name + * @ingroup rename_func + */ + register_method(vm, &player_set_name, "set_name", true); /** * Get monthly statistics of construction costs. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_construction", freevariable3(ATV_CONSTRUCTION_COST, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_construction", freevariable(ATV_CONSTRUCTION_COST, TT_ALL, true), true); /** * Get monthly statistics of vehicle running costs. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_vehicle_maint", freevariable3(ATV_RUNNING_COST, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_vehicle_maint", freevariable(ATV_RUNNING_COST, TT_ALL, true), true); /** * Get monthly statistics of costs for vehicle purchase. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_new_vehicles", freevariable3(ATV_NEW_VEHICLE, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_new_vehicles", freevariable(ATV_NEW_VEHICLE, TT_ALL, true), true); /** * Get monthly statistics of income. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_income", freevariable3(ATV_REVENUE_TRANSPORT, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_income", freevariable(ATV_REVENUE_TRANSPORT, TT_ALL, true), true); /** * Get monthly statistics of infrastructure maintenance. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_maintenance", freevariable3(ATV_INFRASTRUCTURE_MAINTENANCE, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_maintenance", freevariable(ATV_INFRASTRUCTURE_MAINTENANCE, TT_ALL, true), true); /** * Get monthly statistics of assets. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_assets", freevariable3(ATV_NON_FINANCIAL_ASSETS, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_assets", freevariable(ATV_NON_FINANCIAL_ASSETS, TT_ALL, true), true); /** * Get monthly statistics of cash. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_cash", freevariable3(ATC_CASH, -1, true), true); + register_method_fv(vm, &get_player_stat, "get_cash", freevariable(ATC_CASH, -1, true), true); /** * Get monthly statistics of net wealth. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_net_wealth", freevariable3(ATC_NETWEALTH, -1, true), true); + register_method_fv(vm, &get_player_stat, "get_net_wealth", freevariable(ATC_NETWEALTH, -1, true), true); /** * Get monthly statistics of profit. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_profit", freevariable3(ATV_PROFIT, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_profit", freevariable(ATV_PROFIT, TT_ALL, true), true); /** * Get monthly statistics of operating profit. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_operating_profit", freevariable3(ATV_OPERATING_PROFIT, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_operating_profit", freevariable(ATV_OPERATING_PROFIT, TT_ALL, true), true); /** * Get monthly statistics of margin. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_margin", freevariable3(ATV_PROFIT_MARGIN, -1, true), true); + register_method_fv(vm, &get_player_stat, "get_margin", freevariable(ATV_PROFIT_MARGIN, -1, true), true); /** * Get monthly statistics of income from powerlines. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_powerline", freevariable3(ATV_REVENUE, TT_POWERLINE, true), true); + register_method_fv(vm, &get_player_stat, "get_powerline", freevariable(ATV_REVENUE, TT_POWERLINE, true), true); /** * Get monthly statistics of transported passengers. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_transported_pax", freevariable3(ATV_TRANSPORTED_PASSENGER, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_transported_pax", freevariable(ATV_TRANSPORTED_PASSENGER, TT_ALL, true), true); /** * Get monthly statistics of transported mail. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_transported_mail", freevariable3(ATV_TRANSPORTED_MAIL, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_transported_mail", freevariable(ATV_TRANSPORTED_MAIL, TT_ALL, true), true); /** * Get monthly statistics of transported goods. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_transported_goods", freevariable3(ATV_TRANSPORTED_GOOD, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_transported_goods", freevariable(ATV_TRANSPORTED_GOOD, TT_ALL, true), true); /** * Get monthly statistics of number of convoys. * @returns array, index [0] corresponds to current month @@ -195,15 +257,28 @@ void export_player(HSQUIRRELVM vm) * Get monthly statistics of income/loss due to way tolls. * @returns array, index [0] corresponds to current month */ - register_method_fv(vm, &get_player_stat, "get_way_tolls", freevariable3(ATV_WAY_TOLL, TT_ALL, true), true); + register_method_fv(vm, &get_player_stat, "get_way_tolls", freevariable(ATV_WAY_TOLL, TT_ALL, true), true); + if (scenario) { + /** + * Change bank account of player by given amount @p delta. + * @param delta + * @ingroup scen_only + */ + register_method(vm, player_book_account, "book_cash", true); + } /** - * Change bank account of player by given amount @p delta. - * @param delta - * @warning cannot be used in network games. + * Returns the current account balance. */ - register_method(vm, &change_player_account, "book_cash", true); - + register_method(vm, &player_t::get_account_balance_as_double, "get_current_cash"); + /** + * Returns the current net worth [in 1/100 cr]. + */ + register_method(vm, &finance_t::get_netwealth, "get_current_net_wealth"); + /** + * Returns the current maintenance [in 1/100 cr]. + */ + register_method_fv(vm, &finance_t::get_maintenance_with_bits, "get_current_maintenance", freevariable(TT_ALL)); /** * Returns whether the player (still) exists in the game. * @@ -217,5 +292,16 @@ void export_player(HSQUIRRELVM vm) */ register_function(vm, &player_export_line_list, "get_line_list", 1, param::typemask()); + /** + * Creates a new line for the player of the given way type. + * @param wt way type + * @ingroup game_cmd + */ + register_method(vm, &player_create_line, "create_line", true); + /** + * Returns player type: 1 = human, 2,3 = old c++ AI, 4 = scripted AI + */ + register_method(vm, &player_t::get_ai_id, "get_type"); + end_class(vm); } diff --git a/script/api/api_scenario.cc b/script/api/api_scenario.cc index 25eebbb9452..62f9761473a 100644 --- a/script/api/api_scenario.cc +++ b/script/api/api_scenario.cc @@ -15,6 +15,8 @@ #include "../../dataobj/translator.h" #include "../../utils/simstring.h" +#include "../../simintr.h" + using namespace script_api; static char buf[40]; @@ -35,9 +37,15 @@ static plainstring integer_to_string(sint64 f) static plainstring money_to_string_intern(sint64 m) { money_to_string(buf, m, false); + return buf; } +static plainstring difftick_to_string_intern(sint32 f ) +{ + return difftick_to_string( f, false ); +} + static plainstring koord_to_string_intern(koord k) { return k.get_str(); @@ -48,8 +56,7 @@ static plainstring koord3d_to_string_intern(koord3d k) return k.get_str(); } - -void export_scenario(HSQUIRRELVM vm) +void export_string_methods(HSQUIRRELVM vm) { /** * Helper method to translate strings. @@ -59,21 +66,6 @@ void export_scenario(HSQUIRRELVM vm) // need to identify the correct overloaded function register_method(vm, &translator::translate, "translate"); - /** - * Helper method to load scenario-related translation files. - * Tries to load files in the following order relative to pakxx/scenario: - * -# scenario-name/iso/filename - * -# scenario-name/en/filename - * -# scenario-name/filename - * - * Here, iso refers to iso-abbreviation of currently active language. - * - * The content of the files is cached. The cache is cleared upon reloading of savegame. - * @param file name of txt-file - * @return content of loaded file - */ - register_method(vm, &scenario_t::load_language_file, "load_language_file"); - /** * Pretty-print floating point numbers, use language specific separator for powers of thousands. * @param value number to print @@ -117,12 +109,40 @@ void export_scenario(HSQUIRRELVM vm) */ register_method(vm, &translator::get_month_name, "get_month_name"); + /** + * Translates difference of ticks to string. + */ + register_method(vm, &difftick_to_string_intern, "difftick_to_string"); +} + + +void export_scenario(HSQUIRRELVM vm) +{ + /** + * Helper method to load scenario-related translation files. + * Tries to load files in the following order relative to pakxx/scenario: + * -# scenario-name/iso/filename + * -# scenario-name/en/filename + * -# scenario-name/filename + * + * Here, iso refers to iso-abbreviation of currently active language. + * + * The content of the files is cached. The cache is cleared upon reloading of savegame. + * @param file name of txt-file + * @return content of loaded file + * @note Only available in scenario mode. + * @ingroup scen_only + */ + register_method(vm, &scenario_t::load_language_file, "load_language_file"); /** * Table with methods to forbid and allow tools. * * Tools that are set to forbidden using the forbid_* methods can be allowed * again by calls to the respective allow_* method with exact the same parameters. + * + * @note Only available in scenario mode. + * @ingroup scen_only */ begin_class(vm, "rules", 0); @@ -177,7 +197,7 @@ void export_scenario(HSQUIRRELVM vm) * @param wt waytype * @param pos_nw coordinate of north-western corner of rectangle * @param pos_se coordinate of south-eastern corner of rectangle - * @param err error message presented to user when trying to apply this tool + * @param err error message presented to user when trying to apply this tool, see also @ref is_work_allowed_here * @see tool_ids way_types player_all */ STATIC register_method(vm, &scenario_t::forbid_way_tool_rect, "forbid_way_tool_rect"); @@ -204,7 +224,7 @@ void export_scenario(HSQUIRRELVM vm) * @param wt waytype * @param pos_nw 3d-coordinate of north-western corner of cube * @param pos_se 3d-coordinate of south-eastern corner of cube - * @param err error message presented to user when trying to apply this tool + * @param err error message presented to user when trying to apply this tool, see also @ref is_work_allowed_here * @see tool_ids way_types player_all */ STATIC register_method(vm, &scenario_t::forbid_way_tool_cube, "forbid_way_tool_cube"); @@ -230,16 +250,23 @@ void export_scenario(HSQUIRRELVM vm) */ STATIC register_method(vm, &scenario_t::clear_rules, "clear"); + /** + * Signals that toolbars and active tools need to be checked against scenario rules again. + */ + STATIC register_method(vm, &scenario_t::gui_needs_update, "gui_needs_update"); + end_class(vm); /** * Table with methods help debugging. + * @note Only available in scenario mode. */ begin_class(vm, "debug", 0); /** * @returns text containing all active rules, can be used in @ref get_debug_text + * @note Only available in scenario mode. */ STATIC register_method(vm, &scenario_t::get_forbidden_text, "get_forbidden_text"); diff --git a/script/api/api_schedule.cc b/script/api/api_schedule.cc index 1b4d87a3951..03496407c83 100644 --- a/script/api/api_schedule.cc +++ b/script/api/api_schedule.cc @@ -11,6 +11,7 @@ #include "../api_function.h" #include "../../dataobj/schedule.h" #include "../../simhalt.h" +#include "../../simintr.h" #include "../../simworld.h" using namespace script_api; @@ -24,6 +25,95 @@ halthandle_t get_halt_from_koord3d(koord3d pos, const player_t *player ) return haltestelle_t::get_halt(pos, player); } +// SQInteger waiting_time_to_string(HSQUIRRELVM vm) +// { +// assert(false && "FIXME"); +// schedule_entry_t entry(koord3d::invalid, 0, 0, 0, 0, false); +// get_slot(vm, "wait", entry.waiting_time_shift, -1); +// plainstring str = difftick_to_string(0 /*entry.get_waiting_ticks()*/, false); +// return param::push(vm, str); +// } + +SQInteger schedule_constructor(HSQUIRRELVM vm) // instance, wt, entries +{ + waytype_t wt = param::get(vm, 2); + SQInteger res = set_slot(vm, "waytype", wt, 1); + + if (SQ_SUCCEEDED(res)) { + sq_pushstring(vm, "entries", -1); + sq_push(vm, 3); // entries + res = sq_set(vm, 1); + } + if (SQ_SUCCEEDED(res)) { + // attach a schedule instance + // the c++ schedule_t instance will be refilled everytime when transfered to the c++ side of things. + schedule_t* sched = NULL; + switch(wt) { + case road_wt: sched = new truck_schedule_t(); break; + case track_wt: sched = new train_schedule_t(); break; + case water_wt: sched = new ship_schedule_t(); break; + case air_wt: sched = new airplane_schedule_(); break; + case monorail_wt: sched = new monorail_schedule_t(); break; + case tram_wt: sched = new tram_schedule_t(); break; + case maglev_wt: sched = new maglev_schedule_t(); break; + case narrowgauge_wt: sched = new narrowgauge_schedule_t(); break; + default: + sq_raise_error(vm, "Invalid waytype %d", wt); + return SQ_ERROR; + } + attach_instance(vm, 1, sched); + } + return res; +} + +// read entry at index, append to schedule +void append_entry(HSQUIRRELVM vm, SQInteger index, schedule_t* sched) +{ + koord3d pos = param::get(vm, index); + + uint8 minimum_loading = 0; + get_slot(vm, "load", minimum_loading, index); + + uint16 waiting_time_shift = 0; + get_slot(vm, "wait", waiting_time_shift, index); + + grund_t *gr = welt->lookup(pos); + if (gr) { + sched->append(gr, minimum_loading, waiting_time_shift); + } +} + +schedule_t* script_api::param::get(HSQUIRRELVM vm, SQInteger index) +{ + // get waytype + waytype_t wt = invalid_wt; + get_slot(vm, "waytype", wt, index); + + // get instance pointer + schedule_t* sched = get_attached_instance(vm, index, param::tag()); + if (sched) { + sched->entries.clear(); + // now read the entries + sq_pushstring(vm, "entries", -1); + SQInteger new_index = index > 0 ? index : index-1; + if (SQ_SUCCEEDED(sq_get(vm, new_index))) { + // now entries array at the top of the stack + // foreach loop + sq_pushnull(vm); + while(SQ_SUCCEEDED(sq_next(vm, -2))) { + append_entry(vm, -1, sched); + sq_pop(vm, 2); + } + sq_pop(vm, 1); + } + } + return sched; +} + +void* script_api::param::tag() +{ + return (void*)&script_api::param::get; +} void export_schedule(HSQUIRRELVM vm) { @@ -50,13 +140,25 @@ void export_schedule(HSQUIRRELVM vm) * @typemask halt_x(player_x) */ register_method(vm, &get_halt_from_koord3d, "get_halt", true); + /** + * Returns waiting time formatted as string. + * @typemask string() + */ +// register_function(vm, waiting_time_to_string, "waiting_time_to_string", 1, "x" ); end_class(vm); /** * Class holding the schedule */ - begin_class(vm, "schedule_x", ""); + create_class(vm, "schedule_x"); + + /** + * Constructor + * @typemask command_x(way_types) + */ + register_function(vm, schedule_constructor, "constructor", 3, "xi."); + sq_settypetag(vm, -1, param::tag()); #ifdef SQAPI_DOC // document members /** @@ -68,6 +170,9 @@ void export_schedule(HSQUIRRELVM vm) * Waytype of schedule. */ way_types waytype; +#else + create_slot(vm, "entries", 0); + create_slot(vm, "waytype", 0); #endif end_class(vm); diff --git a/script/api/api_settings.cc b/script/api/api_settings.cc index 4a7d4f6eac1..323cf6b4cf6 100644 --- a/script/api/api_settings.cc +++ b/script/api/api_settings.cc @@ -7,6 +7,7 @@ /** @file api_settings.cc exports game settings functions. */ +#include "api_command.h" #include "api_simple.h" #include "../api_class.h" #include "../api_function.h" @@ -24,16 +25,11 @@ mytime_t get_start_time(settings_t* settings) } -script_api::void_t set_traffic_level(settings_t*, sint16 rate) +call_tool_init set_traffic_level(settings_t*, sint16 rate) { - static char level[16]; - sprintf(level, "%i", rate); - tool_t *tool = tool_t::simple_tool[TOOL_TRAFFIC_LEVEL]; - tool->set_default_param( level ); - tool->flags |= tool_t::WFL_SCRIPT; - welt->set_tool( tool, welt->get_public_player() ); - tool->flags &= ~tool_t::WFL_SCRIPT; - return script_api::void_t(); + cbuffer_t buf; + buf.printf("%i", rate); + return call_tool_init(TOOL_TRAFFIC_LEVEL | SIMPLE_TOOL, buf, 0, welt->get_public_player()); } @@ -77,21 +73,39 @@ void export_settings(HSQUIRRELVM vm) /// @returns station coverage register_method(vm, &settings_t::get_station_coverage, "get_station_coverage"); - // Many of these functions are commented out because they don't exist in Extended. - /// @returns passenger factors influences passenger generation in cities - // register_method(vm, &settings_t::get_passenger_factor, "get_passenger_factor"); - /// @returns maximum distance of city to factory for supplying workers - // register_method(vm, &settings_t::get_factory_worker_radius, "get_factory_worker_radius"); - /// @returns minimum number of cities to supply workers for a factory - // register_method(vm, &settings_t::get_factory_worker_minimum_towns, "get_factory_worker_minimum_towns"); - /// @returns maximum number of cities to supply workers for a factory - // register_method(vm, &settings_t::get_factory_worker_maximum_towns, "get_factory_worker_maximum_towns"); +// /// @returns passenger factors influences passenger generation in cities +// register_method(vm, &settings_t::get_passenger_factor, "get_passenger_factor"); +// /// @returns maximum distance of city to factory for supplying workers +// register_method(vm, &settings_t::get_factory_worker_radius, "get_factory_worker_radius"); +// /// @returns minimum number of cities to supply workers for a factory +// register_method(vm, &settings_t::get_factory_worker_minimum_towns, "get_factory_worker_minimum_towns"); +// /// @returns maximum number of cities to supply workers for a factory +// register_method(vm, &settings_t::get_factory_worker_maximum_towns, "get_factory_worker_maximum_towns"); /// @returns freight will not enter convoy if next transfer halt is overcrowded register_method(vm, &settings_t::is_avoid_overcrowding, "avoid_overcrowding"); - /// @returns freight will not start when best route goes through overcrowded halt - // register_method(vm, &settings_t::is_no_routing_over_overcrowding, "no_routing_over_overcrowding"); +// /// @returns freight will not start when best route goes through overcrowded halt +// register_method(vm, &settings_t::is_no_routing_over_overcrowding, "no_routing_over_overcrowding"); /// @returns true if halt capacity is separated between passengers, mail, freight register_method(vm, &settings_t::is_separate_halt_capacities, "separate_halt_capacities"); + /// @returns true if it is allowed to buy obsolete vehicles + register_method(vm, &settings_t::get_allow_buying_obsolete_vehicles, "obsolete_vehicles_allowed"); +// /// @returns max number of vehicles of road convois +// register_method(vm, &settings_t::get_max_road_convoi_length, "get_max_road_convoi_length"); +// /// @returns max number of vehicles of rail convois +// register_method(vm, &settings_t::get_max_rail_convoi_length, "get_max_rail_convoi_length"); +// /// @returns max number of vehicles of ship convois +// register_method(vm, &settings_t::get_max_ship_convoi_length, "get_max_ship_convoi_length"); +// /// @returns max number of vehicles of air convois +// register_method(vm, &settings_t::get_max_air_convoi_length, "get_max_air_convoi_length"); + /// @returns true, if drive-on-left is on + register_method(vm, &settings_t::is_drive_left, "get_drive_on_left"); +// /** +// * Three modes of transfer payment: +// * 0 = pay for travelled Manhattan distance +// * 1 = pay for distance difference to next transfer stop +// * 2 = pay for distance to destination +// */ +// register_method(vm, &settings_t::get_pay_for_total_distance_mode, "get_pay_for_total_distance_mode"); end_class(vm); } diff --git a/script/api/api_simple.cc b/script/api/api_simple.cc index 79a58e6f679..507837a6a02 100644 --- a/script/api/api_simple.cc +++ b/script/api/api_simple.cc @@ -79,34 +79,10 @@ mytime_t param::get(HSQUIRRELVM vm, SQInteger index) } -SQInteger script_api::push_ribi(HSQUIRRELVM vm, ribi_t::ribi ribi) -{ - welt->get_scenario()->ribi_w2sq(ribi); - return param::push(vm, ribi); -} - - -ribi_t::ribi script_api::get_ribi(HSQUIRRELVM vm, SQInteger index) -{ - ribi_t::ribi ribi = param::get(vm, index) & ribi_t::all; - welt->get_scenario()->ribi_sq2w(ribi); - return ribi; -} - -#define map_ribi_ribi(f) \ - SQInteger export_## f(HSQUIRRELVM vm) \ - { \ - ribi_t::ribi ribi = get_ribi(vm, -1); \ - ribi = ribi_t::f(ribi); \ - return push_ribi(vm, ribi); \ - } - #define map_ribi_any(f,type) \ - SQInteger export_## f(HSQUIRRELVM vm) \ + type export_## f(my_ribi_t r) \ { \ - ribi_t::ribi ribi = get_ribi(vm, -1); \ - type ret = ribi_t::f(ribi); \ - return script_api::param::push(vm, ret); \ + return ribi_t::f(r); \ } // export the ribi functions @@ -115,14 +91,51 @@ map_ribi_any(is_twoway, bool); map_ribi_any(is_threeway, bool); map_ribi_any(is_bend, bool); map_ribi_any(is_straight, bool); -map_ribi_ribi(doubles); -map_ribi_ribi(backward); +map_ribi_any(doubles, my_ribi_t); +map_ribi_any(backward, my_ribi_t); + +my_slope_t ribi_to_slope(my_ribi_t ribi) +{ + return slope_type((ribi_t::ribi)ribi); +} + +my_ribi_t slope_to_ribi(my_slope_t slope) +{ + return ribi_type((slope_t::type)slope); +} + + +template SQInteger coord_to_ribi(HSQUIRRELVM vm) +{ + // get coordinate vector without transformation + sint16 x=-1, y=-1; + get_slot(vm, "x", x, idx); + get_slot(vm, "y", y, idx); + koord k(x,y); + + // and do not transform ribi either + uint8 ribi = ribi_type(k); + return param::push(vm, ribi); +} + +SQInteger ribi_to_coord(HSQUIRRELVM vm) +{ + const uint8 ribi = param::get(vm, 2); + if ((ribi & ~(uint8)ribi_t::all) != 0) { + return sq_raise_error(vm, "Invalid dir %hhu (valid values are 0..15)", ribi); + } + + koord k( (ribi_t::ribi)ribi ); + return push_instance(vm, "coord", k.x, k.y); +} + void export_simple(HSQUIRRELVM vm) { /** * Class that holds 2d coordinates. + * All functions that use this as input parameters will accept every data structure that contains members x and y. * * Coordinates always refer to the original rotation in @ref map.file. * They will be rotated if transferred between the game engine and squirrel. @@ -133,7 +146,8 @@ void export_simple(HSQUIRRELVM vm) integer x; /// y-coordinate integer y; - // operators are defined in scenario_base.nut + // operators are defined in script_base.nut + coord(int x, int y); coord operator + (coord other); coord operator - (coord other); coord operator - (); @@ -153,10 +167,17 @@ void export_simple(HSQUIRRELVM vm) */ string href(string text); #endif + /** + * Helper function to convert direction vector to dir type. + * @typemask dir() + */ + register_function(vm, coord_to_ribi<1>, "to_dir", 1, "t|x|y"); + end_class(vm); /** * Class that holds 3d coordinates. + * All functions that use this as input parameters will accept every data structure that contains members x, y, and z. * * Coordinates always refer to the original rotation in @ref map.file. * They will be rotated if transferred between the game engine and squirrel. @@ -169,7 +190,8 @@ void export_simple(HSQUIRRELVM vm) integer y; /// z-coordinate - height integer z; - // operators are defined in scenario_base.nut + // operators are defined in script_base.nut + coord(int x, int y, int z); coord3d operator + (coord3d other); coord3d operator - (coord other); coord3d operator + (coord3d other); @@ -193,53 +215,135 @@ void export_simple(HSQUIRRELVM vm) #endif end_class(vm); + /** + * Helper function to convert direction vector to dir type. + * @param dir + * @typemask dir(coord) + */ + register_function(vm, coord_to_ribi<2>, "coord_to_dir", 2, "x t|x|y"); + /** * Class holding static methods to work with directions. * Directions are just bit-encoded integers. */ begin_class(vm, "dir"); + +#ifdef SQAPI_DOC // document members + /** @name Named directions. */ + //@{ + static const dir none; + static const dir north; + static const dir east; + static const dir northeast; + static const dir south; + static const dir northsouth; + static const dir southeast; + static const dir northsoutheast; + static const dir west; + static const dir northwest; + static const dir eastwest; + static const dir northeastwest; + static const dir southwest; + static const dir northsouthwest; + static const dir southeastwest; + static const dir all; + //@} +#endif /** * @param d direction to test * @return whether direction is single direction, i.e. just one of n/s/e/w - * @typemask bool(dir) */ - STATIC register_function(vm, &export_is_single, "is_single", 2, "yi"); + STATIC register_method(vm, &export_is_single, "is_single", false, true); /** * @param d direction to test * @return whether direction is double direction, e.g. n+e, n+s. - * @typemask bool(dir) */ - STATIC register_function(vm, &export_is_twoway, "is_twoway", 2, "yi"); + STATIC register_method(vm, &export_is_twoway, "is_twoway", false, true); /** * @param d direction to test - * @return whether direction is triple direction, e.g. n+s+e. - * @typemask bool(dir) + * @return whether direction is triple direction, e.g. n+s+e or n+s+e+w. */ - STATIC register_function(vm, &export_is_threeway, "is_threeway", 2, "yi"); + STATIC register_method(vm, &export_is_threeway, "is_threeway", false, true); /** * @param d direction to test * @return whether direction is curve, e.g. n+e, s+w. - * @typemask bool(dir) */ - STATIC register_function(vm, &export_is_bend, "is_curve", 2, "yi"); + STATIC register_method(vm, &export_is_bend, "is_curve", false, true); /** * @param d direction to test - * @return whether direction is straight and has no curves in it, e.g. n+w, w. - * @typemask bool(dir) + * @return whether direction is straight and has no curves in it, e.g. n+s, w. */ - STATIC register_function(vm, &export_is_straight, "is_straight", 2, "yi"); + STATIC register_method(vm, &export_is_straight, "is_straight", false, true); /** * @param d direction * @return complements direction to complete straight, i.e. w -> w+e, but n+w -> 0. - * @typemask dir(dir) */ - STATIC register_function(vm, &export_doubles, "double", 2, "yi"); + STATIC register_method(vm, &export_doubles, "double", false, true); /** * @param d direction to test * @return backward direction, e.g. w -> e, n+w -> s+e, n+w+s -> e. - * @typemask dir(dir) */ - STATIC register_function(vm, &export_backward, "backward", 2, "yi"); + STATIC register_method(vm, &export_backward, "backward", false, true); + + /** + * Converts direction to slope: direction goes upward on slope. + * @param d direction + */ + STATIC register_method(vm, &ribi_to_slope, "to_slope", false, true); + /** + * Helper function to convert direction vector to dir type. + * @typemask coord(dir) + */ + STATIC register_function(vm, ribi_to_coord, "to_coord", 2, ".i", true); end_class(vm); + + /** + * Class holding static methods to work with slopes. + * Slopes are just integers. + */ + begin_class(vm, "slope"); + +#ifdef SQAPI_DOC // document members + /** @name Named slopes. */ + //@{ + static const slope flat; + static const slope north; ///< North slope + static const slope west; ///< West slope + static const slope east; ///< East slope + static const slope south; ///< South slope + static const slope northwest; ///< NW corner + static const slope northeast; ///< NE corner + static const slope southeast; ///< SE corner + static const slope southwest; ///< SW corner + static const slope raised; ///< special meaning: used as slope of bridgeheads + static const slope all_up_slope = 82; ///< used for terraforming tools + static const slope all_down_slope = 83; ///< used for terraforming tools + //@} +#endif + + /** + * Converts slope to dir: direction goes upward on slope. + * If slope cannot be walked on, it returns @ref dir::none. + * @param s slope + */ + STATIC register_method(vm, &slope_to_ribi, "to_dir", false, true); + end_class(vm); + +#ifdef SQAPI_DOC + /** + * Classes to access in-game objects. + * Creating an instance of such classes will not create an object in the game. + * Instead, these classes contain data to access the in-game object. + * If the in-game object disappears, then any access to it via these classes will fail. + * Use the method @r ingame_object::is_valid to check whether the in-game object is still alive. + */ + class ingame_object + { + /** + * @returns true if in-game object can still be accessed. + */ + bool is_valid(); + } +#endif } diff --git a/script/api/api_simple.h b/script/api/api_simple.h index 72f66f8e84c..6b935a78a00 100644 --- a/script/api/api_simple.h +++ b/script/api/api_simple.h @@ -12,11 +12,24 @@ /** @file api_simple.h Helper functions to export simple datatypes: ribi & friends */ -namespace script_api { - SQInteger push_ribi(HSQUIRRELVM vm, ribi_t::ribi ribi); +/** + * Use own type for ribis and slopes + */ +struct my_ribi_t { + uint8 data; + my_ribi_t(ribi_t::ribi r) : data(r) { } + operator ribi_t::ribi() const { return data; } +}; - ribi_t::ribi get_ribi(HSQUIRRELVM vm, SQInteger index); +struct my_slope_t { + uint8 data; + my_slope_t(slope_t::type r) : data(r) { } + operator slope_t::type() const { return data; } +}; + + +namespace script_api { struct mytime_t { diff --git a/script/api/api_skeleton.cc b/script/api/api_skeleton.cc index 866aa41d4f5..fcd98a502a9 100644 --- a/script/api/api_skeleton.cc +++ b/script/api/api_skeleton.cc @@ -13,6 +13,19 @@ * This function is called when the scenario starts. Do all the initializations here, * as you cannot initialize global variables with non-built-in squirrel types. * @typemask void() + * @ingroup scen_skel + */ +register_function("start"); + +/** + * This function is called when the AI starts. Do all the initializations here, + * as you cannot initialize global variables with non-built-in squirrel types. + * + * @param pl_num the number of the AI player. Call player_x(pl_num) to obtain + * a corresponding player_x instance. Definitely store this value! + * + * @typemask void(int) + * @ingroup ai_skel */ register_function("start"); @@ -20,13 +33,36 @@ register_function("start"); * This function is called when a savegame with active scenario is loaded. * Do all the initializations and post-processing here. * @typemask void() + * @ingroup scen_skel + * @ingroup quick_return_func + */ +register_function("resume_game"); + +/** + * This function is called when a savegame with active AI player is loaded. + * Do all the initializations and post-processing here. + * + * @param pl_num the number of the AI player. Call player_x(pl_num) to obtain + * a corresponding player_x instance. Definitely store this value! + * @typemask void(int) + * @ingroup ai_skel */ register_function("resume_game"); +/** + * The heartbeat of the AI player. Here, all AI-related calculations and work can be done. + * + * @typemask void() + * @ingroup ai_skel + */ +register_function("step"); + /** * Called at the beginning of a new month. * Statistics of the last (complete) month is now in position [1] of any statistics array. * @typemask void() + * @ingroup scen_skel + * @ingroup ai_skel */ register_function("new_month") @@ -34,6 +70,8 @@ register_function("new_month") * Called at the beginning of a new year. * Statistics of the last (complete) year is now in position [1] of any statistics array. * @typemask void() + * @ingroup scen_skel + * @ingroup ai_skel */ register_function("new_year") @@ -44,6 +82,8 @@ register_function("new_year") * By default returns the string @ref map.file. * @returns filename or "" * @typemask string() + * @ingroup scen_skel + * @ingroup quick_return_func */ register_function("get_map_file") @@ -56,6 +96,7 @@ register_function("get_map_file") * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_about_text"); @@ -82,6 +123,7 @@ register_function("get_about_text"); * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_rule_text"); @@ -92,6 +134,7 @@ register_function("get_rule_text"); * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_goal_text"); @@ -102,6 +145,7 @@ register_function("get_goal_text"); * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_info_text"); @@ -112,6 +156,7 @@ register_function("get_info_text"); * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_result_text"); @@ -122,17 +167,21 @@ register_function("get_result_text"); * * @param pl player number of active player * @typemask string(integer) + * @ingroup scen_skel */ register_function("get_debug_text"); /** * Returns string containing the version of the api * that the scenario supports. + * By default returns the string @ref scenario.api. * * If it returns "*" then this indicates that the scenario works in the most current api version. * Currently "112.3" and "120.1" are supported. * * @typemask string() + * @ingroup scen_skel + * @ingroup quick_return_func */ register_function("get_api_version"); @@ -148,6 +197,7 @@ register_function("get_api_version"); * * @param pl player number of active player * @typemask integer(integer) + * @ingroup scen_skel */ register_function("is_scenario_completed"); @@ -164,6 +214,8 @@ register_function("is_scenario_completed"); * @param wt waytype of tool * @returns true if tool is allowed. * @typemask bool(integer,integer,way_types) + * @ingroup scen_skel + * @ingroup quick_return_func */ register_function("is_tool_allowed"); @@ -172,6 +224,14 @@ register_function("is_tool_allowed"); * * This function is network-aware: * Error messages are sent back over network to clients. + * + * The error message can contain a coordinate, which is used to show a location on the map. + * In order to show the right place, use @ref coord_to_string. The must be enclosed in parentheses + * or prefixed with @ . + * @code + return "You cannot do this. The guy living at (" + coord_to_string({x=47, y=11}) + ") does not like you!" + @endcode + * * @attention Does not work with waybuilding and all tools that need path-finding, use the functions provided in #rules in this case. * * @param pl player number @@ -180,6 +240,8 @@ register_function("is_tool_allowed"); * * @return null if allowed, an error message otherwise * @typemask string(integer,integer,coord3d) + * @ingroup scen_skel + * @ingroup quick_return_func */ register_function("is_work_allowed_here"); @@ -193,7 +255,123 @@ register_function("is_work_allowed_here"); * * @return null if allowed, an error message otherwise * @typemask string(integer,schedule_x) + * @ingroup scen_skel + * @ingroup quick_return_func */ register_function("is_schedule_allowed"); +/** + * Called when user wants to start convoy. + * + * @warning Function will NOT be called in network games. + * + * @param pl player number + * @param convoy convoy to start + * @param depot convoy is in this depot + * + * @return null if allowed, an error message otherwise + * @typemask string(integer,convoy_x,depot_x) + * @ingroup scen_skel + * @ingroup quick_return_func + */ +register_function("is_convoy_allowed"); + +/** + * Called when player click link in scenario windows. + * + * @param pos coordinate go to in link + * + * @return null if allowed, an error message otherwise + * @typemask string(coord3d) + * @ingroup scen_skel + * @ingroup quick_return_func + */ +register_function("jump_to_link_executed"); + +/** + * Initializes the tool. + * @returns true upon success. + * + * @param pl player instance to use this tool. + * @ingroup tool_skel + * @typemask bool(player_x) + */ +register_function("init"); + +/** + * Exits the tool. Do cleanup here. + * @returns true upon success. + * + * @param pl player instance to use this tool. + * @ingroup tool_skel + * @typemask bool(player_x) + */ +register_function("exit"); + +/** + * Does the work (for tools of one-click type). + * @returns null upon success, an error message otherwise. + * + * @param pl player instance to use this tool. + * @param pos tile clicked by user, here the work should be done. + * @param keys state of ctrl/shift keys. + * @ingroup tool_skel + * @typemask string(player_x, coord3d, int) + */ +register_function("work"); + +/** + * Does the work (for tools of two-click type). + * @returns null upon success, an error message otherwise. + * + * @param pl player instance to use this tool. + * @param start first tile clicked by user. + * @param end second tile clicked by user. + * @param keys state of ctrl/shift keys. + * @ingroup tool_skel + * @typemask string(player_x, coord3d, coord3d, int) + */ +register_function("do_work"); + +/** + * Mark tiles for working (for tools of two-click type). + * Call @ref mark_tile from here. + * + * @param pl player instance to use this tool. + * @param start first tile clicked by user. + * @param end second tile clicked by user. + * @param keys state of ctrl/shift keys. + * @ingroup tool_skel + * @typemask void(player_x, coord3d, coord3d, int) + */ +register_function("mark_tiles"); + +/** + * Place marker image of scripted tool at @p pos. + * Marker images will be deleted automatically. + * @returns true if succesfull + * + * @param pos position to be marked + * @ingroup tool_only + * @typemask bool(coord3d) + */ +// see tool/simtool.scripted.cc +register_function("mark_tile"); + +/** + * Can the tool start/end on @p pos? If it is the second click, @p start is the position of the first click. + * Possible return values: + * 0 = no + * 1 = This tool can work on this tile (with single click) + * 2 = On this tile can dragging start/end + * 3 = Both (1 and 2) + * + * @param pl player instance to use this tool. + * @param pos position to test + * @param start first tile clicked by user + * @ingroup tool_skel + * @typemask void(player_x, coord3d, coord3d) + */ +register_function("is_valid_pos"); + #endif diff --git a/script/api/api_skeleton.h b/script/api/api_skeleton.h index 79be0ef1f09..c0842c5b43b 100644 --- a/script/api/api_skeleton.h +++ b/script/api/api_skeleton.h @@ -19,6 +19,8 @@ struct { * Corresponds to release numbers (e.g. 112.3). * Set it to "*" to support nightly versions. * If this string is not set, the default "112.3" will be used. + * + * @see get_api_version */ /// Required version of api. string api; @@ -33,6 +35,8 @@ struct { * Name of savegame. The scenario starts with the world saved there. * If file == "" then do not load a saved, attach * the scenario to the running world instead. + * + * @see get_map_file */ string file; } diff --git a/script/api/api_tiles.cc b/script/api/api_tiles.cc index 06e0fccfa48..2f5b6842dc1 100644 --- a/script/api/api_tiles.cc +++ b/script/api/api_tiles.cc @@ -11,11 +11,21 @@ #include "get_next.h" #include "../api_class.h" #include "../api_function.h" -#include "../../simtool.h" +#include "../../simmenu.h" #include "../../simworld.h" +#include "../../boden/wasser.h" + +#include "../../simconvoi.h" +#include "../../vehicle/vehicle.h" + +namespace script_api { + declare_enum_param(grund_t::flag_values, uint8, "flags"); + declare_specialized_param(depot_t*, "t|x|y", "depot_x"); +}; using namespace script_api; + SQInteger get_next_object(HSQUIRRELVM vm) { grund_t *gr = param::get(vm, 1); @@ -35,19 +45,17 @@ SQInteger get_object_index(HSQUIRRELVM vm) return param::push(vm, obj); } - -const char* tile_remove_object(grund_t* gr, player_t* player, obj_t::typ type) +SQInteger get_object_count(HSQUIRRELVM vm) { - if (gr == NULL || player == NULL) { - return ""; - } - tool_remover_t w; - // default param is object type - char buf[5]; - sprintf(buf, "%d", (int)type); - w.set_default_param(buf); + grund_t *gr = param::get(vm, 1); + return param::push(vm, gr ? gr->get_top() : 0); +} - return w.work(player, gr->get_pos()); +call_tool_work tile_remove_object(grund_t* gr, player_t* player, obj_t::typ type) +{ + cbuffer_t buf; + buf.printf("%d", (int)type); + return call_tool_work(TOOL_REMOVER | GENERAL_TOOL, (const char*)buf, 0, player, gr->get_pos()); } // return way ribis, have to implement a wrapper, to correctly rotate ribi @@ -59,24 +67,34 @@ static SQInteger get_way_ribi(HSQUIRRELVM vm) ribi_t::ribi ribi = gr ? (masked ? gr->get_weg_ribi(wt) : gr->get_weg_ribi_unmasked(wt) ) : 0; - return push_ribi(vm, ribi); + return param::push(vm, ribi); +} + +static SQInteger get_canal_ribi(HSQUIRRELVM vm) +{ + grund_t *gr = param::get(vm, 1); + ribi_t::ribi ribi = gr && gr->is_water() ? ((wasser_t*)gr)->get_canal_ribi() : (ribi_t::ribi)0; + + return param::push(vm, ribi); } // we have to implement a wrapper, to correctly rotate ribi -SQInteger get_neighbour(HSQUIRRELVM vm) +grund_t* get_neighbour(grund_t *gr, waytype_t wt, my_ribi_t ribi) { - grund_t *gr = param::get(vm, 1); - waytype_t wt = param::get(vm, 2); - ribi_t::ribi ribi = get_ribi(vm, 3); - grund_t *to = NULL; if (gr && ribi_t::is_single(ribi)) { gr->get_neighbour(to, wt, ribi); } - return param::push(vm, to); + return to; +} + +my_slope_t get_slope(grund_t *gr) +{ + return gr->get_grund_hang(); } + halthandle_t get_first_halt_on_square(planquadrat_t* plan) { return plan->get_halt(NULL); @@ -87,7 +105,7 @@ vector_tpl const& square_get_halt_list(planquadrat_t *plan) static vector_tpl list; list.clear(); if (plan) { - const nearby_halt_t* haltlist = plan->get_haltlist(); + const nearby_halt_t *haltlist = plan->get_haltlist(); for(uint8 i=0, end = plan->get_haltlist_count(); i < end; i++) { list.append(haltlist[i].halt); } @@ -95,6 +113,22 @@ vector_tpl const& square_get_halt_list(planquadrat_t *plan) return list; } +vector_tpl const get_convoy_list(grund_t* gr) +{ + static vector_tpl list; + list.clear(); + + for( uint8 n = 0; nget_top(); n++) { + obj_t* obj = gr->obj_bei( n ); + if( vehicle_t* veh = dynamic_cast(obj) ) { + convoihandle_t cnv; + if( veh->get_convoi() ) { + list.append_unique( veh->get_convoi()->self ); + } + } + } + return list; +} void export_tiles(HSQUIRRELVM vm) { @@ -109,7 +143,7 @@ void export_tiles(HSQUIRRELVM vm) * } * @endcode */ - begin_class(vm, "tile_x", "extend_get,coord3d"); + begin_class(vm, "tile_x", "extend_get,coord3d,ingame_object"); /** * Constructor. Returns tile at particular 3d coordinate. @@ -120,10 +154,13 @@ void export_tiles(HSQUIRRELVM vm) * @param z z-coordinate * @typemask (integer,integer,integer) */ - // actually defined simutrans/script/scenario_base.nut + // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); - + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Search for a given object type on the tile. * @return some instance or null if not found @@ -131,10 +168,14 @@ void export_tiles(HSQUIRRELVM vm) register_method(vm, &grund_t::suche_obj, "find_object"); /** * Remove object of given type from the tile. + * Type @p type must be one of the following: ::mo_label, ::mo_pedestrian, ::mo_city_car, ::mo_powerline, ::mo_transformer_s, + * ::mo_transformer_c, ::mo_signal, ::mo_roadsign, ::mo_wayobj, ::mo_tunnel, ::mo_bridge, ::mo_field, ::mo_building, ::mo_depot_rail. + * Setting @p type to ::mo_depot_rail deletes depots of every type. * @param pl player that pays for removal * @param type object type * @returns null upon success, an error message otherwise - * @warning Cannot be used in network games. Does not work with all object types. + * @warning Does not work with all object types. + * @ingroup game_cmd */ register_method(vm, &tile_remove_object, "remove_object", true); /** @@ -177,7 +218,7 @@ void export_tiles(HSQUIRRELVM vm) * Returns encoded slope of tile, zero means flat tile. * @returns slope */ - register_method(vm, &grund_t::get_grund_hang, "get_slope"); + register_method(vm, &get_slope, "get_slope", true); /** * Returns text of a sign on this tile (station sign, city name, label). @@ -203,7 +244,12 @@ void export_tiles(HSQUIRRELVM vm) * @returns true if there is are two ways on the tile */ register_method(vm, &grund_t::has_two_ways, "has_two_ways"); - + /** + * Returns way_x object on this tile of way type @p wt if present + * @param wt waytype + * @returns way object or null + */ + register_method(vm, &grund_t::get_weg, "get_way"); /** * Return directions in which ways on this tile go. One-way signs are ignored here. * @param wt waytype @@ -218,15 +264,50 @@ void export_tiles(HSQUIRRELVM vm) * @typemask dir(waytypes) */ register_function_fv(vm, &get_way_ribi, "get_way_dirs_masked", 2, "xi", freevariable(true) ); - + /** + * Return directions in which canals branch off from water tiles. + * Used for jps pathfinding on water tiles. + * @returns direction + * @typemask dir() + */ + register_function(vm, &get_canal_ribi, "get_canal_ribi", 1, "x"); /** * Returns neighbour if one follows way in the given direction. * @param wt waytype, if equal to @ref wt_all then ways are ignored. * @param d direction * @return neighbour tile or null - * @typemask tile_x(waytypes,dir) */ - register_function(vm, &get_neighbour, "get_neighbour", 3, "xii"); + register_method(vm, &get_neighbour, "get_neighbour", true); + /** + * Returns depot_x object on this tile if any depot is present. + * @returns depot object or null + */ + register_method(vm, &grund_t::get_depot, "get_depot"); + /** + * Checks whether player can delete all objects on the tile. + * @param pl player + * @return error message or null if player can delete everything + */ + register_method(vm, &grund_t::kann_alle_obj_entfernen, "can_remove_all_objects"); + + /** @name Functions to mark tiles. + * Methods to mark, unmark, and check mark-status of tiles. + * Mark flag can be reset by cursor movement. + * @warning In network games, they only work on server. + */ + //@{ + /// Check if tile is marked. + register_method_fv(vm, &grund_t::get_flag, "is_marked", freevariable(grund_t::marked)); + /// Unmark tile. + register_method_fv(vm, &grund_t::clear_flag, "unmark", freevariable(grund_t::marked)); + /// Mark tile. + register_method_fv(vm, &grund_t::set_flag, "mark", freevariable(grund_t::marked)); + //@} + + /** + * @return convoy list by tile + */ + register_method(vm, &get_convoy_list, "get_convoys", true); #ifdef SQAPI_DOC // document members /** @@ -257,13 +338,18 @@ void export_tiles(HSQUIRRELVM vm) * Meta-method to be used in foreach loops to loop over all objects on the tile. Do not call it directly. */ register_function(vm, get_object_index, "_get", 2, "x i|s"); + /** + * Returns number of objects on the tile. + * @typemask integer() + */ + register_function(vm, get_object_count, "get_count", 1, "x"); end_class(vm); /** * Class to map squares, which holds all the tiles on one particular coordinate. */ - begin_class(vm, "square_x", "extend_get,coord"); + begin_class(vm, "square_x", "extend_get,coord,ingame_object"); /** * Constructor. Returns map square at particular 2d coordinate. @@ -272,9 +358,13 @@ void export_tiles(HSQUIRRELVM vm) * @param y z-coordinate * @typemask (integer,integer) */ - // actually defined simutrans/script/scenario_base.nut + // actually defined in simutrans/script/script_base.nut // register_function(..., "constructor", ...); + /** + * @returns if object is still valid. + */ + export_is_valid(vm); //register_function("is_valid") /** * Access some halt at this square. * @deprecated Use square_x::get_player_halt or tile_x::get_halt instead! @@ -308,5 +398,10 @@ void export_tiles(HSQUIRRELVM vm) */ register_method(vm, &square_get_halt_list, "get_halt_list", true); + /** + * Returns climate of ground tile. + */ + register_method(vm, &planquadrat_t::get_climate, "get_climate"); + end_class(vm); } diff --git a/script/api/api_world.cc b/script/api/api_world.cc index a1b4449b732..4a3703f1f08 100644 --- a/script/api/api_world.cc +++ b/script/api/api_world.cc @@ -12,15 +12,17 @@ #include "../api_class.h" #include "../api_function.h" #include "../../simworld.h" +#include "../../simversion.h" #include "../../player/simplay.h" #include "../../obj/gebaeude.h" +#include "../../descriptor/ground_desc.h" using namespace script_api; mytime_ticks_t world_get_time(karte_t*) { - return mytime_ticks_t(welt->get_current_month(), welt->get_ticks(), welt->calc_adjusted_monthly_figure(1<<18), welt->get_next_month_ticks()); + return mytime_ticks_t(welt->get_current_month(), welt->get_ticks(), welt->scale_with_month_length(1<<18), welt->get_next_month_ticks()); } @@ -94,6 +96,10 @@ SQInteger world_get_attraction_list(HSQUIRRELVM vm) return push_instance(vm, "attraction_list_x"); } +SQInteger world_get_attraction_count(HSQUIRRELVM vm) +{ + return param::push(vm, welt->get_attractions().get_count()); +} SQInteger world_get_convoy_list(HSQUIRRELVM vm) { @@ -102,8 +108,28 @@ SQInteger world_get_convoy_list(HSQUIRRELVM vm) return 1; } +SQInteger world_get_size(HSQUIRRELVM vm) +{ + koord k = welt->get_size(); + if (coordinate_transform_t::get_rotation() & 1) { + return push_instance(vm, "coord", k.y, k.x); + } + else { + return push_instance(vm, "coord", k.x, k.y); + } +} + +const char* get_pakset_name() +{ + return ground_desc_t::outside->get_copyright(); +} + +const char* get_version_number() +{ + return VERSION_NUMBER; +} -void export_world(HSQUIRRELVM vm) +void export_world(HSQUIRRELVM vm, bool scenario) { /** * Table with methods to access the world, the universe, and everything. @@ -130,18 +156,21 @@ void export_world(HSQUIRRELVM vm) */ STATIC register_method(vm, &karte_t::get_season, "get_season"); - /** - * Removes player company: removes all assets. Use with care. - * - * If pl is the first player (nr == 0) it is restarted immediately. - * Public player (nr == 1) cannot be removed. - * - * In network games, there will be a delay between the call to this function and the removal of the player. - * - * @param pl player to be removed - * @returns whether operation was successfull - */ - STATIC register_method(vm, &world_remove_player, "remove_player", true); + if (scenario) { + /** + * Removes player company: removes all assets. Use with care. + * + * If pl is the first player (nr == 0) it is restarted immediately. + * Public player (nr == 1) cannot be removed. + * + * In network games, there will be a delay between the call to this function and the removal of the player. + * + * @param pl player to be removed + * @ingroup scen_only + * @returns whether operation was successful + */ + STATIC register_method(vm, &world_remove_player, "remove_player", true); + } /** * Returns player number @p pl. If player does not exist, returns null. * @param pl player number @@ -156,145 +185,136 @@ void export_world(HSQUIRRELVM vm) * Get monthly statistics of total number of citizens. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_citizens", freevariable2(true, karte_t::WORLD_CITIZENS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_citizens", freevariable(true, karte_t::WORLD_CITIZENS), true ); /** * Get monthly statistics of total city growth. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_growth", freevariable2(true, karte_t::WORLD_GROWTH), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_growth", freevariable(true, karte_t::WORLD_GROWTH), true ); /** * Get monthly statistics of total number of towns. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_towns", freevariable2(true, karte_t::WORLD_TOWNS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_towns", freevariable(true, karte_t::WORLD_TOWNS), true ); /** * Get monthly statistics of total number of factories. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_factories", freevariable2(true, karte_t::WORLD_FACTORIES), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_factories", freevariable(true, karte_t::WORLD_FACTORIES), true ); /** * Get monthly statistics of total number of convoys. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_convoys", freevariable2(true, karte_t::WORLD_CONVOIS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_convoys", freevariable(true, karte_t::WORLD_CONVOIS), true ); /** * Get monthly statistics of total number of citycars. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_citycars", freevariable2(true, karte_t::WORLD_CITYCARS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_citycars", freevariable(true, karte_t::WORLD_CITYCARS), true ); /** * Get monthly statistics of ratio transported to generated passengers. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_ratio_pax", freevariable2(true, karte_t::WORLD_PAS_RATIO), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_ratio_pax", freevariable(true, karte_t::WORLD_PAS_RATIO), true ); /** * Get monthly statistics of total number of generated passengers. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_pax city_x::get_transported_pax */ - STATIC register_method_fv(vm, &get_world_stat, "get_generated_pax", freevariable2(true, karte_t::WORLD_PAS_GENERATED), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_generated_pax", freevariable(true, karte_t::WORLD_PAS_GENERATED), true ); /** * Get monthly statistics of ratio transported to generated mail. * @returns array, index [0] corresponds to current month */ - STATIC register_method_fv(vm, &get_world_stat, "get_ratio_mail", freevariable2(true, karte_t::WORLD_MAIL_RATIO), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_ratio_mail", freevariable(true, karte_t::WORLD_MAIL_RATIO), true ); /** * Get monthly statistics of total number of generated mail. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_generated_mail", freevariable2(true, karte_t::WORLD_MAIL_GENERATED), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_generated_mail", freevariable(true, karte_t::WORLD_MAIL_GENERATED), true ); /** * Get monthly statistics of ratio of factories that got supplied. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_ratio_goods", freevariable2(true, karte_t::WORLD_GOODS_RATIO), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_ratio_goods", freevariable(true, karte_t::WORLD_GOODS_RATIO), true ); /** * Get monthly statistics of total number of transported goods. * @returns array, index [0] corresponds to current month * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_transported_goods", freevariable2(true, karte_t::WORLD_TRANSPORTED_GOODS), true ); - - /** - * Get monthly statistics of proportion of people with access to a private car. - * @returns array, index [0] corresponds to current month - * @see city_x::get_generated_mail city_x::get_transported_mail - */ - STATIC register_method_fv(vm, &get_world_stat, "get_car_ownership", freevariable2(true, karte_t::WORLD_CAR_OWNERSHIP), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_transported_goods", freevariable(true, karte_t::WORLD_TRANSPORTED_GOODS), true ); /** * Get per year statistics of total number of citizens. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_citizens", freevariable2(false, karte_t::WORLD_CITIZENS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_citizens", freevariable(false, karte_t::WORLD_CITIZENS), true ); /** * Get per year statistics of total city growth. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_growth", freevariable2(false, karte_t::WORLD_GROWTH), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_growth", freevariable(false, karte_t::WORLD_GROWTH), true ); /** * Get per year statistics of total number of towns. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_towns", freevariable2(false, karte_t::WORLD_TOWNS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_towns", freevariable(false, karte_t::WORLD_TOWNS), true ); /** * Get per year statistics of total number of factories. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_factories", freevariable2(false, karte_t::WORLD_FACTORIES), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_factories", freevariable(false, karte_t::WORLD_FACTORIES), true ); /** * Get per year statistics of total number of convoys. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_convoys", freevariable2(false, karte_t::WORLD_CONVOIS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_convoys", freevariable(false, karte_t::WORLD_CONVOIS), true ); /** * Get per year statistics of total number of citycars. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_citycars", freevariable2(false, karte_t::WORLD_CITYCARS), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_citycars", freevariable(false, karte_t::WORLD_CITYCARS), true ); /** * Get per year statistics of ratio transported to generated passengers. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_pax", freevariable2(false, karte_t::WORLD_PAS_RATIO), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_pax", freevariable(false, karte_t::WORLD_PAS_RATIO), true ); /** * Get per year statistics of total number of generated passengers. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_pax city_x::get_transported_pax */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_pax", freevariable2(false, karte_t::WORLD_PAS_GENERATED), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_pax", freevariable(false, karte_t::WORLD_PAS_GENERATED), true ); /** * Get per year statistics of ratio transported to generated mail. * @returns array, index [0] corresponds to current year */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_mail", freevariable2(false, karte_t::WORLD_MAIL_RATIO), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_mail", freevariable(false, karte_t::WORLD_MAIL_RATIO), true ); /** * Get per year statistics of total number of generated mail. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_mail", freevariable2(false, karte_t::WORLD_MAIL_GENERATED), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_generated_mail", freevariable(false, karte_t::WORLD_MAIL_GENERATED), true ); /** * Get per year statistics of ratio of factories that got supplied. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_goods", freevariable2(false, karte_t::WORLD_GOODS_RATIO), true ); - + STATIC register_method_fv(vm, &get_world_stat, "get_year_ratio_goods", freevariable(false, karte_t::WORLD_GOODS_RATIO), true ); /** - * Get per year statistics of the proportion of people with access to a private car. + * Get per year statistics of total number of transported goods. * @returns array, index [0] corresponds to current year * @see city_x::get_generated_mail city_x::get_transported_mail */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_car_ownership", freevariable2(false, karte_t::WORLD_CAR_OWNERSHIP), true ); + STATIC register_method_fv(vm, &get_world_stat, "get_year_transported_goods", freevariable(false, karte_t::WORLD_TRANSPORTED_GOODS), true ); + /** - * Get per year statistics of total number of transported goods. - * @returns array, index [0] corresponds to current year - * @see city_x::get_generated_mail city_x::get_transported_mail + * @returns true if timeline play is active */ - STATIC register_method_fv(vm, &get_world_stat, "get_year_transported_goods", freevariable2(false, karte_t::WORLD_TRANSPORTED_GOODS), true ); + STATIC register_method(vm, &karte_t::use_timeline, "use_timeline"); /** * Returns iterator through the list of attractions on the map. @@ -308,6 +328,11 @@ void export_world(HSQUIRRELVM vm) * @typemask convoy_list_x() */ STATIC register_function(vm, world_get_convoy_list, "get_convoy_list", 1, "."); + /** + * Returns size of the map. + * @typemask coord() + */ + STATIC register_function(vm, world_get_size, "get_size", 1, "."); end_class(vm); @@ -332,6 +357,21 @@ void export_world(HSQUIRRELVM vm) * Meta-method to be used in foreach loops to loop over all attractions on the map. Do not call it directly. */ register_method(vm, world_attraction_list_get, "_get", true); + /** + * Returns number of attractions in the list. + * @typemask integer() + */ + register_function(vm, world_get_attraction_count, "get_count", 1, "x"); end_class(vm); + + /** + * Returns pakset name as set in ground.outside.pak + */ + register_method(vm, get_pakset_name, "get_pakset_name"); + + /** + * Returns simutrans version number + */ + register_method(vm, get_version_number, "get_version_number"); } diff --git a/script/api/changelog.h b/script/api/changelog.h index d76c3fe759e..46fabd09dd2 100644 --- a/script/api/changelog.h +++ b/script/api/changelog.h @@ -9,6 +9,79 @@ * * @section api-trunk Current trunk * + * - Added @ref change_climate_at + * - Added @ref convoy_x::change_schedule + * - Changed building_desc_x::get_available_stations to accept wt_all + * + * @section api-123 Release 123.0 + * + * - Changed scripted tools: work, do_work, mark_tiles have additional parameter to send state of ctrl/shift keys + * - Added @ref factory_desc_x + * - Added @ref way_x::get_max_speed + * - Added @ref wayobj_desc_x, @ref wayobj_x, @ref command_x::build_wayobj to work with way-objects + * - Added @ref factory_x::get_raw_name + * - Added @ref tree_desc_x::get_price, @ref command_x::slope_get_price + * - Added @ref way_x::get_transported_goods, @ref way_x::get_convoys_passed + * - Added @ref tile_x::get_way, @ref tile_x::get_depot + * - Added @ref get_pakset_name, @ref player_x::get_type + * + * @section api-122 Release 122.0 + * + * - Feature: scripted tools + * - Added @ref debug::pause, @ref debug::set_pause_on_error + * - Added more tool ids + * - Added @ref convoy_x::is_waiting, @ref convoy_x::is_loading + * - Added @ref rules::gui_needs_update + * - Added @ref convoy_x::get_tile_length + * - Added @ref settings::get_pay_for_total_distance_mode, @ref settings::get_drive_on_left + * - Added @ref halt_x::get_connections + * - Added @ref command_x::can_set_slope + * - Added @ref halt_x::get_halt + * - Changed possible direction parameter of @ref command_x::build_station (useful for flat docks) + * - Added @ref depot_x::get_depot_list + * - Added @ref vehicle_desc_x::needs_electrification + * - Added @ref command_x::build_road + * - Added fields: @ref field_x + * - Added powerlines: @ref powerline_x, @ref transformer_x, @ref factory_x::get_transformer, @ref factory_x::is_transformer_connected + * + * @section api-121 Release 121.0 + * + * - Added functions to mark tiles, see @ref tile_x::mark + * - Added @ref convoy_x::is_schedule_editor_open + * + * @section api-120-3 Release 120.3 + * + * - Changed enable scenario scripts to open info window also on clients, see @ref gui::open_info_win_client + * - Added sign_x::can_pass + * - Added methods is_valid to check whether in-game object is still present, see @ref ingame_object + * - Added tile_x::can_remove_all_objects + * - Added @ref coord_to_dir, @ref coord.to_dir, @ref dir.to_coord + * - Added @ref simple_heap_x, @ref way_planner_x, @ref bridge_planner_x + * - Added map_object_x::is_marked + * - Added gui::open_info_win_at + * - Added ::is_convoy_allowed + * - Added convoy_x::is_in_depot + * - Added scriptable hyperlinks in scenario info window + * - Added @ref slope + * - Added functions to build stuff: command_x::build_way, command_x::build_depot, command_x::build_station, command_x::build_bridge, command_x::build_bridge_at, command_x::set_slope, command_x::restore_slope + * - Added sign_x, sign_desc_x, command_x::build_sign_at + * - Added line_x::destroy + * + * @section api-120-2-2 Release 120.2.2 + * + * - Added map_object_x::mark and unmark + * + * @section api-120-2 Release 120.2 + * + * - Feature: scripted AI players + * - Feature: script calls can be suspended and waken up, less strict checks of 'script take too long' + * - Feature: add commands to modify game state, see @ref game_cmd + * - Added @ref climates, square_x::get_climate, world::get_size + * - Added new classes command_x, vehicle_desc_x, tunnel_desc_x, bridge_desc_x + * - Added tool_ids::tool_set_climate, tool_ids::tool_change_water_height + * - Added string::toalnum (converts strings to strings that can be used as table keys) + * - Changed gui::add_message to take additional player_x parameter + * * @section api-120-1-2 Release 120.1.2 * * - Added label_x::get_text, tile_x::get_text diff --git a/script/api/export.awk b/script/api/export.awk index 5009b51f48c..35856bbf3c1 100644 --- a/script/api/export.awk +++ b/script/api/export.awk @@ -14,6 +14,7 @@ BEGIN { indent = "" within_apidoc = 0 mask = "" + ai_only = 0 } # match beginning of SQAPI_DOC block @@ -22,9 +23,14 @@ BEGIN { } # match end of SQAPI_DOC block -/^#endif/ { +/^#(endif|else)/ { if (within_sqapi_doc == 1) { within_sqapi_doc = 0 + param_count = 0 + delete params + delete ptypes + mask = "" + returns = "void" } } @@ -33,6 +39,10 @@ BEGIN { next } +/ingroup.*ai_only/ { + ai_only = 1 +} + function split_params(string) { count = split(string, types, / *, */) @@ -57,10 +67,10 @@ function split_params(string) # and everything in a SQAPI_DOC block { if (within_doxygen_comment==1) { - print gensub( /^[[:space:]]*([ \\/])\\*/, indent "\\1", $0) + print gensub( /^[[:space:]]*([ \\/])\\*/, indent "\\1", 1) } else if (within_sqapi_doc == 1) { - print gensub( /^[[:space:]]*(.*)/, indent "\\1", $0) + print gensub( /^[[:space:]]*(.*)/, indent "\\1", 1) } } @@ -69,10 +79,10 @@ function split_params(string) within_doxygen_comment = 0 } -# print doxygen brief commands -/\/\/\// { +# print doxygen brief commands, also //@ +/\/\/[\/@]/ { if (within_doxygen_comment!=1 && within_sqapi_doc != 1) { - print gensub( /^[[:space:]]*(\\.*)/, indent "\\1", $0) + print gensub( /^[[:space:]]*(\\.*)/, indent "\\1", 1) } } @@ -154,12 +164,18 @@ function split_params(string) /register_function/ || /register_method/ || /register_local_method/{ match($0, /"([^"]*)"/, data) method = data[1] + if (ai_only == 1) { + suffix = "ai" + } + else { + suffix = "scenario" + } # check for param types - if ( (within_class "::" method) in export_types) { - mask = export_types[(within_class "::" method)] + if (ai_only && ((within_class "::" method) in export_types_ai)) { + mask = export_types_ai[(within_class "::" method)] } - else if ( ("::" method) in export_types) { - mask = export_types[("::" method)] + else if ((within_class "::" method) in export_types_scenario) { + mask = export_types_scenario[(within_class "::" method)] } if (mask != "") { match(mask, " *(.*)\\((.*)\\)", data) @@ -168,6 +184,7 @@ function split_params(string) for (t in ptypes) { if (!(t in params)) { params[t]="" + param_count++ } } } @@ -194,18 +211,15 @@ function split_params(string) fname = fname returns " " method "(" } } - for (param = 1; param <= 100; param++) { - if (!(param in params) && !(param in ptypes) ) { - break - } + for (param = 1; param <= param_count; param++) { if (mode != "sq") { if (!(param in ptypes)) ptypes[param] = "any_x" - fname = fname ptypes[param] " " + fname = fname ptypes[param] } - - fname = fname params[param] - param_count-- - if (param_count > 0) fname = fname ", " + if (params[param] != "") { + fname = fname " " params[param] + } + if (param < param_count) fname = fname ", " } fname = fname ");" print indent fname @@ -219,6 +233,7 @@ function split_params(string) delete ptypes mask = "" returns = "void" + ai_only = 0 } # enum constants diff --git a/script/api/export_desc.cc b/script/api/export_desc.cc index f49e86c3523..476910fa309 100644 --- a/script/api/export_desc.cc +++ b/script/api/export_desc.cc @@ -14,7 +14,7 @@ using namespace script_api; -static vector_tpl registered_desc_functions; +static vector_tpl registered_desc_functions; SQInteger get_desc_pointer(HSQUIRRELVM vm) @@ -23,9 +23,9 @@ SQInteger get_desc_pointer(HSQUIRRELVM vm) // get type tag of class sq_gettypetag(vm, 1, &tag); plainstring name = param::get(vm, 2); - if (tag && registered_desc_functions.is_contained((GETBESCHFUNC)tag) ) { + if (tag && registered_desc_functions.is_contained((GETDESCFUNC)tag) ) { // call method - const void *desc = ((GETBESCHFUNC)tag)(name.c_str()); + const void *desc = ((GETDESCFUNC)tag)(name.c_str()); // set userpointer of class instance sq_setinstanceup(vm, 1, const_cast(desc) ); return 0; @@ -34,12 +34,10 @@ SQInteger get_desc_pointer(HSQUIRRELVM vm) } -void begin_desc_class(HSQUIRRELVM vm, const char* name, const char* base, GETBESCHFUNC func) +void begin_desc_class(HSQUIRRELVM vm, const char* name, const char* base, GETDESCFUNC func) { SQInteger res = create_class(vm, name, base); - assert( SQ_SUCCEEDED(res) ); - (void)res; - + assert( SQ_SUCCEEDED(res) ); (void)res; // store method to retrieve desc in typetag pointer sq_settypetag(vm, -1, (void*)func); registered_desc_functions.append_unique( func ); diff --git a/script/api/export_desc.h b/script/api/export_desc.h index a883e7fe040..91879293e52 100644 --- a/script/api/export_desc.h +++ b/script/api/export_desc.h @@ -22,6 +22,6 @@ void begin_desc_class(HSQUIRRELVM vm, const char* name, const char* parent, const void* (*func)(const char*)); /// Function signature to retrieve desc-pointer from name -typedef const void* (*GETBESCHFUNC)(const char*); +typedef const void* (*GETDESCFUNC)(const char*); #endif diff --git a/script/api/filter_cpp.sh b/script/api/filter_cpp.sh index c15c06994ba..43dc6289e7b 100755 --- a/script/api/filter_cpp.sh +++ b/script/api/filter_cpp.sh @@ -6,4 +6,4 @@ # # Filter for Doxygen input files -gawk -f squirrel_types.awk -f export.awk -v mode=cpp $1 +gawk -f squirrel_types_scenario.awk -f squirrel_types_ai.awk -f export.awk -v mode=cpp $1 diff --git a/script/api/filter_sq.sh b/script/api/filter_sq.sh index e1c1afdbb5d..cf22d34bc85 100755 --- a/script/api/filter_sq.sh +++ b/script/api/filter_sq.sh @@ -6,4 +6,4 @@ # # Filter for Doxygen source code browsing -gawk -f squirrel_types.awk -f export.awk -v mode=sq $1 +gawk -f squirrel_types_scenario.awk -f squirrel_types_ai.awk -f export.awk -v mode=sq $1 diff --git a/script/api/squirrel_types.awk b/script/api/squirrel_types.awk deleted file mode 100644 index 27eec045194..00000000000 --- a/script/api/squirrel_types.awk +++ /dev/null @@ -1,254 +0,0 @@ -# file used to generate doxygen documentation of squirrel API -# needs to be copied to trunk/script/api -BEGIN { - export_types["city_x::get_name"] = "string()" - export_types["city_x::get_citizens"] = "array()" - export_types["city_x::get_growth"] = "array()" - export_types["city_x::get_buildings"] = "array()" - export_types["city_x::get_citycars"] = "array()" - export_types["city_x::get_transported_pax"] = "array()" - export_types["city_x::get_generated_pax"] = "array()" - export_types["city_x::get_transported_mail"] = "array()" - export_types["city_x::get_generated_mail"] = "array()" - export_types["city_x::get_year_citizens"] = "array()" - export_types["city_x::get_year_growth"] = "array()" - export_types["city_x::get_year_buildings"] = "array()" - export_types["city_x::get_year_citycars"] = "array()" - export_types["city_x::get_year_transported_pax"] = "array()" - export_types["city_x::get_year_generated_pax"] = "array()" - export_types["city_x::get_year_transported_mail"] = "array()" - export_types["city_x::get_year_generated_mail"] = "array()" - export_types["city_x::get_citygrowth_enabled"] = "bool()" - export_types["city_x::get_pos"] = "coord()" - export_types["city_x::get_pos_nw"] = "coord()" - export_types["city_x::get_pos_se"] = "coord()" - export_types["city_x::change_size"] = "void(integer)" - export_types["city_x::set_citygrowth_enabled"] = "void(bool)" - export_types["city_x::set_name"] = "void(string)" - export_types["convoy_x::needs_electrification"] = "bool()" - export_types["convoy_x::get_name"] = "string()" - export_types["convoy_x::get_pos"] = "coord3d()" - export_types["convoy_x::get_owner"] = "player_x()" - export_types["convoy_x::get_goods_catg_index"] = "array()" - export_types["convoy_x::get_waytype"] = "way_types()" - export_types["convoy_x::get_schedule"] = "schedule_x()" - export_types["convoy_x::get_capacity"] = "array()" - export_types["convoy_x::get_transported_goods"] = "array()" - export_types["convoy_x::get_revenue"] = "array()" - export_types["convoy_x::get_cost"] = "array()" - export_types["convoy_x::get_profit"] = "array()" - export_types["convoy_x::get_traveled_distance"] = "array()" - export_types["convoy_x::get_way_tolls"] = "array()" - export_types["convoy_x::get_distance_traveled_total"] = "integer()" - export_types["factory_x::get_consumers"] = "array()" - export_types["factory_x::get_suppliers"] = "array()" - export_types["factory_x::get_name"] = "string()" - export_types["factory_x::set_name"] = "void(string)" - export_types["factory_x::get_production"] = "array()" - export_types["factory_x::get_power"] = "array()" - export_types["factory_x::get_boost_electric"] = "array()" - export_types["factory_x::get_boost_pax"] = "array()" - export_types["factory_x::get_boost_mail"] = "array()" - export_types["factory_x::get_pax_generated"] = "array()" - export_types["factory_x::get_pax_departed"] = "array()" - export_types["factory_x::get_pax_arrived"] = "array()" - export_types["factory_x::get_mail_generated"] = "array()" - export_types["factory_x::get_mail_departed"] = "array()" - export_types["factory_x::get_mail_arrived"] = "array()" - export_types["factory_x::get_tile_list"] = "array()" - export_types["factory_x::get_halt_list"] = "array()" - export_types["factory_production_x::get_storage"] = "array()" - export_types["factory_production_x::get_received"] = "array()" - export_types["factory_production_x::get_consumed"] = "array()" - export_types["factory_production_x::get_in_transit"] = "array()" - export_types["factory_production_x::get_delivered"] = "array()" - export_types["factory_production_x::get_produced"] = "array()" - export_types["obj_desc_x::get_name"] = "string()" - export_types["obj_desc_x::is_equal"] = "bool(obj_desc_x)" - export_types["obj_desc_time_x::get_intro_date"] = "time_x()" - export_types["obj_desc_time_x::get_retire_date"] = "time_x()" - export_types["obj_desc_time_x::is_future"] = "bool(time_x)" - export_types["obj_desc_time_x::is_retired"] = "bool(time_x)" - export_types["obj_desc_time_x::is_available"] = "bool(time_x)" - export_types["obj_desc_transport_x::get_maintenance"] = "integer()" - export_types["obj_desc_transport_x::get_cost"] = "integer()" - export_types["obj_desc_transport_x::get_waytype"] = "way_types()" - export_types["obj_desc_transport_x::get_topspeed"] = "integer()" - export_types["building_desc_x::is_attraction"] = "bool()" - export_types["building_desc_x::get_size"] = "coord(integer)" - export_types["building_desc_x::get_maintenance"] = "integer()" - export_types["building_desc_x::get_cost"] = "integer()" - export_types["building_desc_x::get_capacity"] = "integer()" - export_types["building_desc_x::can_be_built_underground"] = "bool()" - export_types["building_desc_x::can_be_built_aboveground"] = "bool()" - export_types["building_desc_x::enables_pax"] = "bool()" - export_types["building_desc_x::enables_mail"] = "bool()" - export_types["building_desc_x::enables_freight"] = "bool()" - export_types["building_desc_x::get_type"] = "building_desc_x::building_type()" - export_types["building_desc_x::get_waytype"] = "way_types()" - export_types["building_desc_x::get_headquarter_level"] = "integer()" - export_types["::get_building_list"] = "array(building_desc_x::building_type)" - export_types["way_desc_x::has_double_slopes"] = "bool()" - export_types["way_desc_x::get_system_type"] = "integer()" - export_types["good_desc_x::get_catg_index"] = "integer()" - export_types["::open_info_win"] = "bool()" - export_types["::add_message_at"] = "void(string, coord)" - export_types["::add_message"] = "void(string)" - export_types["halt_x::get_name"] = "string()" - export_types["halt_x::get_owner"] = "player_x()" - export_types["halt_x::_cmp"] = "integer(halt_x)" - export_types["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" - export_types["halt_x::accepts_good"] = "bool(good_desc_x)" - export_types["halt_x::get_arrived"] = "array()" - export_types["halt_x::get_departed"] = "array()" - export_types["halt_x::get_waiting"] = "array()" - export_types["halt_x::get_happy"] = "array()" - export_types["halt_x::get_unhappy"] = "array()" - export_types["halt_x::get_noroute"] = "array()" - export_types["halt_x::get_convoys"] = "array()" - export_types["halt_x::get_walked"] = "array()" - export_types["halt_x::get_tile_list"] = "array()" - export_types["halt_x::get_factory_list"] = "array()" - export_types["line_x::get_name"] = "string()" - export_types["line_x::get_owner"] = "player_x()" - export_types["line_x::get_schedule"] = "schedule_x()" - export_types["line_x::get_goods_catg_index"] = "array()" - export_types["line_x::get_capacity"] = "array()" - export_types["line_x::get_transported_goods"] = "array()" - export_types["line_x::get_convoy_count"] = "array()" - export_types["line_x::get_revenue"] = "array()" - export_types["line_x::get_cost"] = "array()" - export_types["line_x::get_profit"] = "array()" - export_types["line_x::get_traveled_distance"] = "array()" - export_types["line_x::get_way_tolls"] = "array()" - export_types["map_object_x::get_owner"] = "player_x()" - export_types["map_object_x::get_name"] = "string()" - export_types["map_object_x::get_waytype"] = "way_types()" - export_types["map_object_x::get_pos"] = "coord3d()" - export_types["map_object_x::is_removable"] = "string(player_x)" - export_types["map_object_x::get_type"] = "map_objects()" - export_types["tree_x::get_age"] = "integer()" - export_types["tree_x::get_desc"] = "tree_desc_x()" - export_types["building_x::get_factory"] = "factory_x()" - export_types["building_x::get_city"] = "city_x()" - export_types["building_x::is_townhall"] = "bool()" - export_types["building_x::is_headquarter"] = "bool()" - export_types["building_x::is_monument"] = "bool()" - export_types["building_x::get_passenger_level"] = "integer()" - export_types["building_x::get_mail_level"] = "integer()" - export_types["building_x::get_desc"] = "building_desc_x()" - export_types["building_x::is_same_building"] = "bool(building_x)" - export_types["way_x::has_sidewalk"] = "bool()" - export_types["way_x::is_electrified"] = "bool()" - export_types["way_x::has_sign"] = "bool()" - export_types["way_x::has_signal"] = "bool()" - export_types["way_x::has_wayobj"] = "bool()" - export_types["way_x::is_crossing"] = "bool()" - export_types["way_x::get_desc"] = "way_desc_x()" - export_types["::create"] = "label_x(coord, player_x, string)" - export_types["label_x::set_text"] = "void(string)" - export_types["label_x::get_text"] = "string()" - export_types["player_x::get_headquarter_level"] = "integer()" - export_types["player_x::get_headquarter_pos"] = "coord()" - export_types["player_x::get_name"] = "string()" - export_types["player_x::get_construction"] = "array()" - export_types["player_x::get_vehicle_maint"] = "array()" - export_types["player_x::get_new_vehicles"] = "array()" - export_types["player_x::get_income"] = "array()" - export_types["player_x::get_maintenance"] = "array()" - export_types["player_x::get_assets"] = "array()" - export_types["player_x::get_cash"] = "array()" - export_types["player_x::get_net_wealth"] = "array()" - export_types["player_x::get_profit"] = "array()" - export_types["player_x::get_operating_profit"] = "array()" - export_types["player_x::get_margin"] = "array()" - export_types["player_x::get_transported"] = "array()" - export_types["player_x::get_powerline"] = "array()" - export_types["player_x::get_transported_pax"] = "array()" - export_types["player_x::get_transported_mail"] = "array()" - export_types["player_x::get_transported_goods"] = "array()" - export_types["player_x::get_convoys"] = "array()" - export_types["player_x::get_way_tolls"] = "array()" - export_types["player_x::book_cash"] = "void(integer)" - export_types["::translate"] = "string(string)" - export_types["::load_language_file"] = "string(string)" - export_types["::double_to_string"] = "string(float, integer)" - export_types["::integer_to_string"] = "string(integer)" - export_types["::money_to_string"] = "string(integer)" - export_types["::coord_to_string"] = "string(coord)" - export_types["::coord3d_to_string"] = "string(coord3d)" - export_types["::get_month_name"] = "string(integer)" - export_types["::forbid_tool"] = "void(integer, integer)" - export_types["::allow_tool"] = "void(integer, integer)" - export_types["::forbid_way_tool"] = "void(integer, integer, way_types)" - export_types["::allow_way_tool"] = "void(integer, integer, way_types)" - export_types["::forbid_way_tool_rect"] = "void(integer, integer, way_types, coord, coord, string)" - export_types["::allow_way_tool_rect"] = "void(integer, integer, way_types, coord, coord)" - export_types["::forbid_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d, string)" - export_types["::allow_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d)" - export_types["::clear"] = "void()" - export_types["::get_forbidden_text"] = "string()" - export_types["coord3d::get_halt"] = "halt_x(player_x)" - export_types["settings::get_industry_increase_every"] = "integer()" - export_types["settings::set_industry_increase_every"] = "void(integer)" - export_types["settings::get_traffic_level"] = "integer()" - export_types["settings::set_traffic_level"] = "void(integer)" - export_types["settings::get_start_time"] = "time_x()" - export_types["settings::get_station_coverage"] = "integer()" -# export_types["settings::get_passenger_factor"] = "integer()" -# export_types["settings::get_factory_worker_radius"] = "integer()" -# export_types["settings::get_factory_worker_minimum_towns"] = "integer()" -# export_types["settings::get_factory_worker_maximum_towns"] = "integer()" - export_types["settings::avoid_overcrowding"] = "bool()" -# export_types["settings::no_routing_over_overcrowding"] = "bool()" - export_types["settings::separate_halt_capacities"] = "bool()" - export_types["tile_x::find_object"] = "map_object_x(map_objects)" - export_types["tile_x::remove_object"] = "string(player_x, map_objects)" - export_types["tile_x::get_halt"] = "halt_x()" - export_types["tile_x::is_water"] = "bool()" - export_types["tile_x::is_bridge"] = "bool()" - export_types["tile_x::is_tunnel"] = "bool()" - export_types["tile_x::is_empty"] = "bool()" - export_types["tile_x::is_ground"] = "bool()" - export_types["tile_x::get_slope"] = "integer()" - export_types["tile_x::get_text"] = "string()" - export_types["tile_x::has_way"] = "bool(way_types)" - export_types["tile_x::has_ways"] = "bool()" - export_types["tile_x::has_two_ways"] = "bool()" - export_types["square_x::get_halt"] = "halt_x()" - export_types["square_x::get_player_halt"] = "halt_x(player_x)" - export_types["square_x::get_tile_at_height"] = "tile_x(integer)" - export_types["square_x::get_ground_tile"] = "tile_x()" - export_types["square_x::get_halt_list"] = "array()" - export_types["world::is_coord_valid"] = "bool(coord)" - export_types["world::find_nearest_city"] = "city_x(coord)" - export_types["world::get_season"] = "integer()" - export_types["world::remove_player"] = "bool(player_x)" - export_types["integer::get_player"] = "player_x()" - export_types["world::get_time"] = "time_ticks_x()" - export_types["world::get_citizens"] = "array()" - export_types["world::get_growth"] = "array()" - export_types["world::get_towns"] = "array()" - export_types["world::get_factories"] = "array()" - export_types["world::get_convoys"] = "array()" - export_types["world::get_citycars"] = "array()" - export_types["world::get_ratio_pax"] = "array()" - export_types["world::get_generated_pax"] = "array()" - export_types["world::get_ratio_mail"] = "array()" - export_types["world::get_generated_mail"] = "array()" - export_types["world::get_ratio_goods"] = "array()" - export_types["world::get_transported_goods"] = "array()" - export_types["world::get_year_citizens"] = "array()" - export_types["world::get_year_growth"] = "array()" - export_types["world::get_year_towns"] = "array()" - export_types["world::get_year_factories"] = "array()" - export_types["world::get_year_convoys"] = "array()" - export_types["world::get_year_citycars"] = "array()" - export_types["world::get_year_ratio_pax"] = "array()" - export_types["world::get_year_generated_pax"] = "array()" - export_types["world::get_year_ratio_mail"] = "array()" - export_types["world::get_year_generated_mail"] = "array()" - export_types["world::get_year_ratio_goods"] = "array()" - export_types["world::get_year_transported_goods"] = "array()" - export_types["attraction_list_x::_get"] = "building_x(integer)" -} diff --git a/script/api/squirrel_types_ai.awk b/script/api/squirrel_types_ai.awk index 858562f9d89..083ce4c7809 100644 --- a/script/api/squirrel_types_ai.awk +++ b/script/api/squirrel_types_ai.awk @@ -2,307 +2,416 @@ # This file is part of the Simutrans-Extended project under the Artistic License. # (see LICENSE.txt) # - # file used to generate doxygen documentation of squirrel API # needs to be copied to trunk/script/api BEGIN { - export_types["city_list_x::_get"] = "city_x(integer)" - export_types["city_x::get_name"] = "string()" - export_types["city_x::set_name"] = "string(string)" - export_types["city_x::get_citizens"] = "array()" - export_types["city_x::get_growth"] = "array()" - export_types["city_x::get_buildings"] = "array()" - export_types["city_x::get_citycars"] = "array()" - export_types["city_x::get_transported_pax"] = "array()" - export_types["city_x::get_generated_pax"] = "array()" - export_types["city_x::get_transported_mail"] = "array()" - export_types["city_x::get_generated_mail"] = "array()" - export_types["city_x::get_year_citizens"] = "array()" - export_types["city_x::get_year_growth"] = "array()" - export_types["city_x::get_year_buildings"] = "array()" - export_types["city_x::get_year_citycars"] = "array()" - export_types["city_x::get_year_transported_pax"] = "array()" - export_types["city_x::get_year_generated_pax"] = "array()" - export_types["city_x::get_year_transported_mail"] = "array()" - export_types["city_x::get_year_generated_mail"] = "array()" - export_types["city_x::get_citygrowth_enabled"] = "bool()" - export_types["city_x::get_pos"] = "coord()" - export_types["city_x::get_pos_nw"] = "coord()" - export_types["city_x::get_pos_se"] = "coord()" - export_types["city_x::change_size"] = "string(integer)" - export_types["city_x::set_citygrowth_enabled"] = "string(bool)" - export_types["convoy_x::needs_electrification"] = "bool()" - export_types["convoy_x::get_name"] = "string()" - export_types["convoy_x::set_name"] = "string(string)" - export_types["convoy_x::get_pos"] = "coord3d()" - export_types["convoy_x::get_owner"] = "player_x()" - export_types["convoy_x::get_goods_catg_index"] = "array()" - export_types["convoy_x::get_waytype"] = "way_types()" - export_types["convoy_x::get_schedule"] = "schedule_x()" - export_types["convoy_x::get_capacity"] = "array()" - export_types["convoy_x::get_transported_goods"] = "array()" - export_types["convoy_x::get_revenue"] = "array()" - export_types["convoy_x::get_cost"] = "array()" - export_types["convoy_x::get_profit"] = "array()" - export_types["convoy_x::get_traveled_distance"] = "array()" - export_types["convoy_x::get_way_tolls"] = "array()" - export_types["convoy_x::get_distance_traveled_total"] = "integer()" - export_types["convoy_x::get_line"] = "line_x()" - export_types["convoy_x::set_line"] = "string(player_x, line_x)" - export_types["convoy_x::get_vehicles"] = "array()" - export_types["convoy_x::get_speed"] = "integer()" - export_types["convoy_x::get_loading_limit"] = "integer()" - export_types["convoy_x::get_loading_level"] = "integer()" - export_types["convoy_x::get_home_depot"] = "coord3d()" - export_types["convoy_x::has_obsolete_vehicles"] = "bool()" - export_types["convoy_x::toggle_withdraw"] = "string(player_x)" - export_types["convoy_x::is_withdrawn"] = "bool()" - export_types["convoy_x::destroy"] = "string(player_x)" - export_types["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" - export_types["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" - export_types["factory_x::get_consumers"] = "array()" - export_types["factory_x::get_suppliers"] = "array()" - export_types["factory_x::get_name"] = "string()" - export_types["factory_x::set_name"] = "string(string)" - export_types["factory_x::get_production"] = "array()" - export_types["factory_x::get_power"] = "array()" - export_types["factory_x::get_boost_electric"] = "array()" - export_types["factory_x::get_boost_pax"] = "array()" - export_types["factory_x::get_boost_mail"] = "array()" - export_types["factory_x::get_pax_generated"] = "array()" - export_types["factory_x::get_pax_departed"] = "array()" - export_types["factory_x::get_pax_arrived"] = "array()" - export_types["factory_x::get_mail_generated"] = "array()" - export_types["factory_x::get_mail_departed"] = "array()" - export_types["factory_x::get_mail_arrived"] = "array()" - export_types["factory_x::get_tile_list"] = "array()" - export_types["factory_x::get_halt_list"] = "array()" - export_types["factory_production_x::get_storage"] = "array()" - export_types["factory_production_x::get_received"] = "array()" - export_types["factory_production_x::get_consumed"] = "array()" - export_types["factory_production_x::get_in_transit"] = "array()" - export_types["factory_production_x::get_delivered"] = "array()" - export_types["factory_production_x::get_produced"] = "array()" - export_types["factory_production_x::get_consumption_factor"] = "integer()" - export_types["factory_production_x::get_production_factor"] = "integer()" - export_types["obj_desc_x::get_name"] = "string()" - export_types["obj_desc_x::is_equal"] = "bool(obj_desc_x)" - export_types["obj_desc_time_x::get_intro_date"] = "time_x()" - export_types["obj_desc_time_x::get_retire_date"] = "time_x()" - export_types["obj_desc_time_x::is_future"] = "bool(time_x)" - export_types["obj_desc_time_x::is_retired"] = "bool(time_x)" - export_types["obj_desc_time_x::is_available"] = "bool(time_x)" - export_types["obj_desc_transport_x::get_maintenance"] = "integer()" - export_types["obj_desc_transport_x::get_cost"] = "integer()" - export_types["obj_desc_transport_x::get_waytype"] = "way_types()" - export_types["obj_desc_transport_x::get_topspeed"] = "integer()" - export_types["vehicle_desc_x::can_be_first"] = "bool()" - export_types["vehicle_desc_x::can_be_last"] = "bool()" - export_types["vehicle_desc_x::get_successors"] = "array()" - export_types["vehicle_desc_x::get_predecessors"] = "array()" - export_types["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" - export_types["vehicle_desc_x::get_power"] = "integer()" - export_types["vehicle_desc_x::get_freight"] = "good_desc_x()" - export_types["vehicle_desc_x::get_capacity"] = "integer()" - export_types["vehicle_desc_x::get_running_cost"] = "integer()" - export_types["vehicle_desc_x::get_maintenance"] = "integer()" - export_types["vehicle_desc_x::get_weight"] = "integer()" - export_types["vehicle_desc_x::get_length"] = "integer()" - export_types["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" - export_types["building_desc_x::is_attraction"] = "bool()" - export_types["building_desc_x::get_size"] = "coord(integer)" - export_types["building_desc_x::get_maintenance"] = "integer()" - export_types["building_desc_x::get_cost"] = "integer()" - export_types["building_desc_x::get_capacity"] = "integer()" - export_types["building_desc_x::can_be_built_underground"] = "bool()" - export_types["building_desc_x::can_be_built_aboveground"] = "bool()" - export_types["building_desc_x::enables_pax"] = "bool()" - export_types["building_desc_x::enables_mail"] = "bool()" - export_types["building_desc_x::enables_freight"] = "bool()" - export_types["building_desc_x::get_type"] = "building_desc_x::building_type()" - export_types["building_desc_x::get_waytype"] = "way_types()" - export_types["building_desc_x::get_headquarter_level"] = "integer()" - export_types["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" - export_types["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" - export_types["building_desc_x::is_terminus"] = "bool()" - export_types["way_desc_x::has_double_slopes"] = "bool()" - export_types["way_desc_x::get_system_type"] = "integer()" - export_types["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" - export_types["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" - export_types["bridge_desc_x::has_double_ramp"] = "bool()" - export_types["bridge_desc_x::has_double_start"] = "bool()" - export_types["bridge_desc_x::get_max_length"] = "integer()" - export_types["bridge_desc_x::get_max_height"] = "integer()" - export_types["bridge_desc_x::get_available_bridges"] = "array(way_types)" - export_types["good_desc_x::get_catg_index"] = "integer()" - export_types["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" - export_types["good_desc_x::get_weight_per_unit"] = "integer()" - export_types["good_desc_x::calc_revenue"] = "integer(way_types, integer)" - export_types["halt_x::get_name"] = "string()" - export_types["halt_x::set_name"] = "string(string)" - export_types["halt_x::get_owner"] = "player_x()" - export_types["halt_x::_cmp"] = "integer(halt_x)" - export_types["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" - export_types["halt_x::accepts_good"] = "bool(good_desc_x)" - export_types["halt_x::get_arrived"] = "array()" - export_types["halt_x::get_departed"] = "array()" - export_types["halt_x::get_waiting"] = "array()" - export_types["halt_x::get_happy"] = "array()" - export_types["halt_x::get_unhappy"] = "array()" - export_types["halt_x::get_noroute"] = "array()" - export_types["halt_x::get_convoys"] = "array()" - export_types["halt_x::get_walked"] = "array()" - export_types["halt_x::get_tile_list"] = "array()" - export_types["halt_x::get_factory_list"] = "array()" - export_types["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" - export_types["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" - export_types["halt_x::get_capacity"] = "integer(good_desc_x)" - export_types["line_x::is_valid"] = "bool()" - export_types["line_x::get_name"] = "string()" - export_types["line_x::set_name"] = "string(string)" - export_types["line_x::get_owner"] = "player_x()" - export_types["line_x::get_schedule"] = "schedule_x()" - export_types["line_x::get_goods_catg_index"] = "array()" - export_types["line_x::get_capacity"] = "array()" - export_types["line_x::get_transported_goods"] = "array()" - export_types["line_x::get_convoy_count"] = "array()" - export_types["line_x::get_revenue"] = "array()" - export_types["line_x::get_cost"] = "array()" - export_types["line_x::get_profit"] = "array()" - export_types["line_x::get_traveled_distance"] = "array()" - export_types["line_x::get_way_tolls"] = "array()" - export_types["line_x::get_waytype"] = "way_types()" - export_types["line_x::change_schedule"] = "string(player_x, schedule_x)" - export_types["map_object_x::get_owner"] = "player_x()" - export_types["map_object_x::get_name"] = "string()" - export_types["map_object_x::get_waytype"] = "way_types()" - export_types["map_object_x::get_pos"] = "coord3d()" - export_types["map_object_x::is_removable"] = "string(player_x)" - export_types["map_object_x::get_type"] = "map_objects()" - export_types["tree_x::get_age"] = "integer()" - export_types["tree_x::get_desc"] = "tree_desc_x()" - export_types["building_x::get_factory"] = "factory_x()" - export_types["building_x::get_city"] = "city_x()" - export_types["building_x::is_townhall"] = "bool()" - export_types["building_x::is_headquarter"] = "bool()" - export_types["building_x::is_monument"] = "bool()" - export_types["building_x::get_passenger_level"] = "integer()" - export_types["building_x::get_mail_level"] = "integer()" - export_types["building_x::get_desc"] = "building_desc_x()" - export_types["building_x::is_same_building"] = "bool(building_x)" - export_types["depot_x::append_vehicle"] = "string(player_x, convoy_x, vehicle_desc_x)" - export_types["depot_x::start_convoy"] = "string(player_x, convoy_x)" - export_types["depot_x::start_all_convoys"] = "string(player_x)" - export_types["depot_x::get_convoy_list"] = "array()" - export_types["way_x::has_sidewalk"] = "bool()" - export_types["way_x::is_electrified"] = "bool()" - export_types["way_x::has_sign"] = "bool()" - export_types["way_x::has_signal"] = "bool()" - export_types["way_x::has_wayobj"] = "bool()" - export_types["way_x::is_crossing"] = "bool()" - export_types["way_x::get_desc"] = "way_desc_x()" - export_types["label_x::create"] = "string(coord, player_x, string)" - export_types["label_x::set_text"] = "string(string)" - export_types["label_x::get_text"] = "string()" - export_types["player_x::get_headquarter_level"] = "integer()" - export_types["player_x::get_headquarter_pos"] = "coord()" - export_types["player_x::get_name"] = "string()" - export_types["player_x::set_name"] = "string(string)" - export_types["player_x::get_construction"] = "array()" - export_types["player_x::get_vehicle_maint"] = "array()" - export_types["player_x::get_new_vehicles"] = "array()" - export_types["player_x::get_income"] = "array()" - export_types["player_x::get_maintenance"] = "array()" - export_types["player_x::get_assets"] = "array()" - export_types["player_x::get_cash"] = "array()" - export_types["player_x::get_net_wealth"] = "array()" - export_types["player_x::get_profit"] = "array()" - export_types["player_x::get_operating_profit"] = "array()" - export_types["player_x::get_margin"] = "array()" - export_types["player_x::get_transported"] = "array()" - export_types["player_x::get_powerline"] = "array()" - export_types["player_x::get_transported_pax"] = "array()" - export_types["player_x::get_transported_mail"] = "array()" - export_types["player_x::get_transported_goods"] = "array()" - export_types["player_x::get_convoys"] = "array()" - export_types["player_x::get_way_tolls"] = "array()" - export_types["player_x::get_current_cash"] = "float()" - export_types["player_x::get_current_net_wealth"] = "integer()" - export_types["player_x::get_current_maintenance"] = "integer()" - export_types["player_x::create_line"] = "string(way_types)" - export_types["coord3d::get_halt"] = "halt_x(player_x)" - export_types["settings::get_industry_increase_every"] = "integer()" - export_types["settings::set_industry_increase_every"] = "void(integer)" - export_types["settings::get_traffic_level"] = "integer()" - export_types["settings::set_traffic_level"] = "string(integer)" - export_types["settings::get_start_time"] = "time_x()" - export_types["settings::get_station_coverage"] = "integer()" - export_types["settings::get_passenger_factor"] = "integer()" - export_types["settings::get_factory_worker_radius"] = "integer()" - export_types["settings::get_factory_worker_minimum_towns"] = "integer()" - export_types["settings::get_factory_worker_maximum_towns"] = "integer()" - export_types["settings::avoid_overcrowding"] = "bool()" - export_types["settings::no_routing_over_overcrowding"] = "bool()" - export_types["settings::separate_halt_capacities"] = "bool()" - export_types["settings::obsolete_vehicles_allowed"] = "bool()" - export_types["::translate"] = "string(string)" - export_types["::double_to_string"] = "string(float, integer)" - export_types["::integer_to_string"] = "string(integer)" - export_types["::money_to_string"] = "string(integer)" - export_types["::coord_to_string"] = "string(coord)" - export_types["::coord3d_to_string"] = "string(coord3d)" - export_types["::get_month_name"] = "string(integer)" - export_types["tile_x::find_object"] = "map_object_x(map_objects)" - export_types["tile_x::remove_object"] = "string(player_x, map_objects)" - export_types["tile_x::get_halt"] = "halt_x()" - export_types["tile_x::is_water"] = "bool()" - export_types["tile_x::is_bridge"] = "bool()" - export_types["tile_x::is_tunnel"] = "bool()" - export_types["tile_x::is_empty"] = "bool()" - export_types["tile_x::is_ground"] = "bool()" - export_types["tile_x::get_slope"] = "integer()" - export_types["tile_x::get_text"] = "string()" - export_types["tile_x::has_way"] = "bool(way_types)" - export_types["tile_x::has_ways"] = "bool()" - export_types["tile_x::has_two_ways"] = "bool()" - export_types["square_x::get_halt"] = "halt_x()" - export_types["square_x::get_player_halt"] = "halt_x(player_x)" - export_types["square_x::get_tile_at_height"] = "tile_x(integer)" - export_types["square_x::get_ground_tile"] = "tile_x()" - export_types["square_x::get_halt_list"] = "array()" - export_types["square_x::get_climate"] = "climates()" - export_types["world::is_coord_valid"] = "bool(coord)" - export_types["world::find_nearest_city"] = "city_x(coord)" - export_types["world::get_season"] = "integer()" - export_types["world::remove_player"] = "bool(player_x)" - export_types["integer::get_player"] = "player_x()" - export_types["world::get_time"] = "time_ticks_x()" - export_types["world::get_citizens"] = "array()" - export_types["world::get_growth"] = "array()" - export_types["world::get_towns"] = "array()" - export_types["world::get_factories"] = "array()" - export_types["world::get_convoys"] = "array()" - export_types["world::get_citycars"] = "array()" - export_types["world::get_ratio_pax"] = "array()" - export_types["world::get_generated_pax"] = "array()" - export_types["world::get_ratio_mail"] = "array()" - export_types["world::get_generated_mail"] = "array()" - export_types["world::get_ratio_goods"] = "array()" - export_types["world::get_transported_goods"] = "array()" - export_types["world::get_year_citizens"] = "array()" - export_types["world::get_year_growth"] = "array()" - export_types["world::get_year_towns"] = "array()" - export_types["world::get_year_factories"] = "array()" - export_types["world::get_year_convoys"] = "array()" - export_types["world::get_year_citycars"] = "array()" - export_types["world::get_year_ratio_pax"] = "array()" - export_types["world::get_year_generated_pax"] = "array()" - export_types["world::get_year_ratio_mail"] = "array()" - export_types["world::get_year_generated_mail"] = "array()" - export_types["world::get_year_ratio_goods"] = "array()" - export_types["world::get_year_transported_goods"] = "array()" - export_types["world::use_timeline"] = "bool()" - export_types["attraction_list_x::_get"] = "building_x(integer)" - export_types["command_x::get_flags"] = "integer()" - export_types["command_x::set_flags"] = "void(integer)" + export_types_ai["city_list_x::_get"] = "city_x(integer)" + export_types_ai["city_x::is_valid"] = "bool()" + export_types_ai["city_x::get_name"] = "string()" + export_types_ai["city_x::set_name"] = "void(string)" + export_types_ai["city_x::get_citizens"] = "array()" + export_types_ai["city_x::get_growth"] = "array()" + export_types_ai["city_x::get_buildings"] = "array()" + export_types_ai["city_x::get_citycars"] = "array()" + export_types_ai["city_x::get_transported_pax"] = "array()" + export_types_ai["city_x::get_generated_pax"] = "array()" + export_types_ai["city_x::get_transported_mail"] = "array()" + export_types_ai["city_x::get_generated_mail"] = "array()" + export_types_ai["city_x::get_year_citizens"] = "array()" + export_types_ai["city_x::get_year_growth"] = "array()" + export_types_ai["city_x::get_year_buildings"] = "array()" + export_types_ai["city_x::get_year_citycars"] = "array()" + export_types_ai["city_x::get_year_transported_pax"] = "array()" + export_types_ai["city_x::get_year_generated_pax"] = "array()" + export_types_ai["city_x::get_year_transported_mail"] = "array()" + export_types_ai["city_x::get_year_generated_mail"] = "array()" + export_types_ai["city_x::get_citygrowth_enabled"] = "bool()" + export_types_ai["city_x::get_pos"] = "coord()" + export_types_ai["city_x::get_pos_nw"] = "coord()" + export_types_ai["city_x::get_pos_se"] = "coord()" + export_types_ai["city_x::change_size"] = "string(integer)" + export_types_ai["city_x::set_citygrowth_enabled"] = "void(bool)" + export_types_ai["::get_ops_total"] = "integer()" + export_types_ai["::get_ops_remaining"] = "integer()" + export_types_ai["debug::pause"] = "bool()" + export_types_ai["debug::is_paused"] = "bool()" + export_types_ai["debug::set_pause_on_error"] = "void(bool)" + export_types_ai["convoy_x::is_valid"] = "bool()" + export_types_ai["convoy_x::needs_electrification"] = "bool()" + export_types_ai["convoy_x::get_name"] = "string()" + export_types_ai["convoy_x::set_name"] = "void(string)" + export_types_ai["convoy_x::get_pos"] = "coord3d()" + export_types_ai["convoy_x::get_owner"] = "player_x()" + export_types_ai["convoy_x::get_goods_catg_index"] = "array()" + export_types_ai["convoy_x::get_waytype"] = "way_types()" + export_types_ai["convoy_x::get_schedule"] = "schedule_x()" + export_types_ai["convoy_x::get_capacity"] = "array()" + export_types_ai["convoy_x::get_transported_goods"] = "array()" + export_types_ai["convoy_x::get_revenue"] = "array()" + export_types_ai["convoy_x::get_cost"] = "array()" + export_types_ai["convoy_x::get_profit"] = "array()" + export_types_ai["convoy_x::get_traveled_distance"] = "array()" + export_types_ai["convoy_x::get_way_tolls"] = "array()" + export_types_ai["convoy_x::get_distance_traveled_total"] = "integer()" + export_types_ai["convoy_x::get_line"] = "line_x()" + export_types_ai["convoy_x::set_line"] = "void(player_x, line_x)" + export_types_ai["convoy_x::get_vehicles"] = "array()" + export_types_ai["convoy_x::get_speed"] = "integer()" + export_types_ai["convoy_x::get_loading_limit"] = "integer()" + export_types_ai["convoy_x::get_loading_level"] = "integer()" + export_types_ai["convoy_x::get_home_depot"] = "coord3d()" + export_types_ai["convoy_x::has_obsolete_vehicles"] = "bool()" + export_types_ai["convoy_x::toggle_withdraw"] = "void(player_x)" + export_types_ai["convoy_x::is_withdrawn"] = "bool()" + export_types_ai["convoy_x::is_in_depot"] = "bool()" + export_types_ai["convoy_x::is_waiting"] = "bool()" + export_types_ai["convoy_x::is_loading"] = "bool()" + export_types_ai["convoy_x::destroy"] = "void(player_x)" + export_types_ai["convoy_x::is_schedule_editor_open"] = "bool()" + export_types_ai["convoy_x::get_tile_length"] = "integer()" + export_types_ai["convoy_x::change_schedule"] = "void(player_x, schedule_x)" + export_types_ai["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" + export_types_ai["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" + export_types_ai["factory_x::is_valid"] = "bool()" + export_types_ai["factory_x::get_consumers"] = "array()" + export_types_ai["factory_x::get_suppliers"] = "array()" + export_types_ai["factory_x::get_name"] = "string()" + export_types_ai["factory_x::get_raw_name"] = "string()" + export_types_ai["factory_x::set_name"] = "void(string)" + export_types_ai["factory_x::get_production"] = "array()" + export_types_ai["factory_x::get_power"] = "array()" + export_types_ai["factory_x::get_boost_electric"] = "array()" + export_types_ai["factory_x::get_boost_pax"] = "array()" + export_types_ai["factory_x::get_boost_mail"] = "array()" + export_types_ai["factory_x::get_pax_generated"] = "array()" + export_types_ai["factory_x::get_pax_departed"] = "array()" + export_types_ai["factory_x::get_pax_arrived"] = "array()" + export_types_ai["factory_x::get_mail_generated"] = "array()" + export_types_ai["factory_x::get_mail_departed"] = "array()" + export_types_ai["factory_x::get_mail_arrived"] = "array()" + export_types_ai["factory_x::get_tile_list"] = "array()" + export_types_ai["factory_x::get_halt_list"] = "array()" + export_types_ai["factory_x::is_transformer_connected"] = "bool()" + export_types_ai["factory_x::get_transformer"] = "powerline_x()" + export_types_ai["factory_x::get_field_count"] = "integer()" + export_types_ai["factory_x::get_min_field_count"] = "integer()" + export_types_ai["factory_x::get_desc"] = "factory_desc_x()" + export_types_ai["factory_production_x::get_storage"] = "array()" + export_types_ai["factory_production_x::get_received"] = "array()" + export_types_ai["factory_production_x::get_consumed"] = "array()" + export_types_ai["factory_production_x::get_in_transit"] = "array()" + export_types_ai["factory_production_x::get_delivered"] = "array()" + export_types_ai["factory_production_x::get_produced"] = "array()" + export_types_ai["factory_production_x::get_consumption_factor"] = "integer()" + export_types_ai["factory_production_x::get_production_factor"] = "integer()" + export_types_ai["obj_desc_x::get_name"] = "string()" + export_types_ai["obj_desc_x::is_equal"] = "bool(obj_desc_x)" + export_types_ai["obj_desc_x::is_valid"] = "bool()" + export_types_ai["obj_desc_time_x::get_intro_date"] = "time_x()" + export_types_ai["obj_desc_time_x::get_retire_date"] = "time_x()" + export_types_ai["obj_desc_time_x::is_future"] = "bool(time_x)" + export_types_ai["obj_desc_time_x::is_retired"] = "bool(time_x)" + export_types_ai["obj_desc_time_x::is_available"] = "bool(time_x)" + export_types_ai["obj_desc_transport_x::get_maintenance"] = "integer()" + export_types_ai["obj_desc_transport_x::get_cost"] = "integer()" + export_types_ai["obj_desc_transport_x::get_waytype"] = "way_types()" + export_types_ai["obj_desc_transport_x::get_topspeed"] = "integer()" + export_types_ai["vehicle_desc_x::can_be_first"] = "bool()" + export_types_ai["vehicle_desc_x::can_be_last"] = "bool()" + export_types_ai["vehicle_desc_x::get_successors"] = "array()" + export_types_ai["vehicle_desc_x::get_predecessors"] = "array()" + export_types_ai["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" + export_types_ai["vehicle_desc_x::get_power"] = "integer()" + export_types_ai["vehicle_desc_x::needs_electrification"] = "bool()" + export_types_ai["vehicle_desc_x::get_freight"] = "good_desc_x()" + export_types_ai["vehicle_desc_x::get_capacity"] = "integer()" + export_types_ai["vehicle_desc_x::get_running_cost"] = "integer()" + export_types_ai["vehicle_desc_x::get_maintenance"] = "integer()" + export_types_ai["vehicle_desc_x::get_weight"] = "integer()" + export_types_ai["vehicle_desc_x::get_length"] = "integer()" + export_types_ai["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" + export_types_ai["tree_desc_x::get_price"] = "integer()" + export_types_ai["building_desc_x::is_attraction"] = "bool()" + export_types_ai["building_desc_x::get_maintenance"] = "integer()" + export_types_ai["building_desc_x::get_cost"] = "integer()" + export_types_ai["building_desc_x::get_capacity"] = "integer()" + export_types_ai["building_desc_x::can_be_built_underground"] = "bool()" + export_types_ai["building_desc_x::can_be_built_aboveground"] = "bool()" + export_types_ai["building_desc_x::enables_pax"] = "bool()" + export_types_ai["building_desc_x::enables_mail"] = "bool()" + export_types_ai["building_desc_x::enables_freight"] = "bool()" + export_types_ai["building_desc_x::get_type"] = "building_desc_x::building_type()" + export_types_ai["building_desc_x::get_waytype"] = "way_types()" + export_types_ai["building_desc_x::get_headquarter_level"] = "integer()" + export_types_ai["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" + export_types_ai["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" + export_types_ai["building_desc_x::is_terminus"] = "bool()" + export_types_ai["factory_desc_x::get_name"] = "string()" + export_types_ai["factory_desc_x::get_building_desc"] = "building_desc_x()" + export_types_ai["factory_desc_x::is_electricity_producer"] = "bool()" + export_types_ai["factory_desc_x::get_productivity_base"] = "integer()" + export_types_ai["factory_desc_x::get_productivity_range"] = "integer()" + export_types_ai["factory_desc_x::get_list"] = "table()" + export_types_ai["way_desc_x::has_double_slopes"] = "bool()" + export_types_ai["way_desc_x::get_system_type"] = "way_system_types()" + export_types_ai["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" + export_types_ai["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" + export_types_ai["bridge_desc_x::has_double_ramp"] = "bool()" + export_types_ai["bridge_desc_x::has_double_start"] = "bool()" + export_types_ai["bridge_desc_x::get_max_length"] = "integer()" + export_types_ai["bridge_desc_x::get_max_height"] = "integer()" + export_types_ai["bridge_desc_x::get_available_bridges"] = "array(way_types)" + export_types_ai["good_desc_x::get_catg_index"] = "integer()" + export_types_ai["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" + export_types_ai["good_desc_x::get_weight_per_unit"] = "integer()" + export_types_ai["good_desc_x::calc_revenue"] = "integer(way_types, integer)" + export_types_ai["sign_desc_x::is_one_way"] = "bool()" + export_types_ai["sign_desc_x::is_private_way"] = "bool()" + export_types_ai["sign_desc_x::is_traffic_light"] = "bool()" + export_types_ai["sign_desc_x::is_choose_sign"] = "bool()" + export_types_ai["sign_desc_x::is_signal"] = "bool()" + export_types_ai["sign_desc_x::is_pre_signal"] = "bool()" + export_types_ai["sign_desc_x::is_priority_signal"] = "bool()" + export_types_ai["sign_desc_x::is_longblock_signal"] = "bool()" + export_types_ai["sign_desc_x::is_end_choose_signal"] = "bool()" + export_types_ai["sign_desc_x::get_available_signs"] = "array(way_types)" + export_types_ai["wayobj_desc_x::is_overhead_line"] = "bool()" + export_types_ai["wayobj_desc_x::get_available_wayobjs"] = "array(way_types)" + export_types_ai["gui::add_message_at"] = "string(player_x, string, coord)" + export_types_ai["halt_x::is_valid"] = "bool()" + export_types_ai["halt_x::get_name"] = "string()" + export_types_ai["halt_x::set_name"] = "void(string)" + export_types_ai["halt_x::get_owner"] = "player_x()" + export_types_ai["halt_x::_cmp"] = "integer(halt_x)" + export_types_ai["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" + export_types_ai["halt_x::accepts_good"] = "bool(good_desc_x)" + export_types_ai["halt_x::get_arrived"] = "array()" + export_types_ai["halt_x::get_departed"] = "array()" + export_types_ai["halt_x::get_waiting"] = "array()" + export_types_ai["halt_x::get_happy"] = "array()" + export_types_ai["halt_x::get_unhappy"] = "array()" + export_types_ai["halt_x::get_noroute"] = "array()" + export_types_ai["halt_x::get_convoys"] = "array()" + export_types_ai["halt_x::get_walked"] = "array()" + export_types_ai["halt_x::get_tile_list"] = "array()" + export_types_ai["halt_x::get_factory_list"] = "array()" + export_types_ai["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" + export_types_ai["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" + export_types_ai["halt_x::get_capacity"] = "integer(good_desc_x)" + export_types_ai["halt_x::get_connections"] = "array(good_desc_x)" + export_types_ai["halt_x::get_halt"] = "halt_x(coord3d, player_x)" + export_types_ai["line_x::is_valid"] = "bool()" + export_types_ai["line_x::get_name"] = "string()" + export_types_ai["line_x::set_name"] = "void(string)" + export_types_ai["line_x::get_owner"] = "player_x()" + export_types_ai["line_x::get_schedule"] = "schedule_x()" + export_types_ai["line_x::get_goods_catg_index"] = "array()" + export_types_ai["line_x::get_capacity"] = "array()" + export_types_ai["line_x::get_transported_goods"] = "array()" + export_types_ai["line_x::get_convoy_count"] = "array()" + export_types_ai["line_x::get_revenue"] = "array()" + export_types_ai["line_x::get_cost"] = "array()" + export_types_ai["line_x::get_profit"] = "array()" + export_types_ai["line_x::get_traveled_distance"] = "array()" + export_types_ai["line_x::get_way_tolls"] = "array()" + export_types_ai["line_x::get_waytype"] = "way_types()" + export_types_ai["line_x::change_schedule"] = "void(player_x, schedule_x)" + export_types_ai["line_x::destroy"] = "void(player_x)" + export_types_ai["map_object_x::is_valid"] = "bool()" + export_types_ai["map_object_x::get_owner"] = "player_x()" + export_types_ai["map_object_x::get_name"] = "string()" + export_types_ai["map_object_x::get_waytype"] = "way_types()" + export_types_ai["map_object_x::get_pos"] = "coord3d()" + export_types_ai["map_object_x::is_removable"] = "string(player_x)" + export_types_ai["map_object_x::get_type"] = "map_objects()" + export_types_ai["map_object_x::mark"] = "void()" + export_types_ai["map_object_x::unmark"] = "void()" + export_types_ai["map_object_x::is_marked"] = "bool()" + export_types_ai["tree_x::get_age"] = "integer()" + export_types_ai["tree_x::get_desc"] = "tree_desc_x()" + export_types_ai["building_x::get_factory"] = "factory_x()" + export_types_ai["building_x::get_city"] = "city_x()" + export_types_ai["building_x::is_townhall"] = "bool()" + export_types_ai["building_x::is_headquarter"] = "bool()" + export_types_ai["building_x::is_monument"] = "bool()" + export_types_ai["building_x::get_passenger_level"] = "integer()" + export_types_ai["building_x::get_mail_level"] = "integer()" + export_types_ai["building_x::get_desc"] = "building_desc_x()" + export_types_ai["building_x::get_tile_list"] = "array()" + export_types_ai["building_x::is_same_building"] = "bool(building_x)" + export_types_ai["depot_x::append_vehicle"] = "void(player_x, convoy_x, vehicle_desc_x)" + export_types_ai["depot_x::start_convoy"] = "void(player_x, convoy_x)" + export_types_ai["depot_x::start_all_convoys"] = "void(player_x)" + export_types_ai["depot_x::get_convoy_list"] = "array()" + export_types_ai["depot_x::get_depot_list"] = "array(player_x, way_types)" + export_types_ai["way_x::has_sidewalk"] = "bool()" + export_types_ai["way_x::is_electrified"] = "bool()" + export_types_ai["way_x::has_sign"] = "bool()" + export_types_ai["way_x::has_signal"] = "bool()" + export_types_ai["way_x::has_wayobj"] = "bool()" + export_types_ai["way_x::is_crossing"] = "bool()" + export_types_ai["way_x::get_desc"] = "way_desc_x()" + export_types_ai["way_x::get_max_speed"] = "integer()" + export_types_ai["way_x::get_transported_goods"] = "array()" + export_types_ai["way_x::get_convoys_passed"] = "array()" + export_types_ai["label_x::create"] = "string(coord, player_x, string)" + export_types_ai["label_x::set_text"] = "void(string)" + export_types_ai["label_x::get_text"] = "string()" + export_types_ai["sign_x::get_desc"] = "sign_desc_x()" + export_types_ai["sign_x::can_pass"] = "bool(player_x)" + export_types_ai["powerline_x::is_connected"] = "bool(powerline_x)" + export_types_ai["powerline_x::get_factory"] = "factory_x()" + export_types_ai["field_x::is_deletable"] = "bool()" + export_types_ai["field_x::get_factory"] = "factory_x()" + export_types_ai["wayobj_x::get_desc"] = "wayobj_desc_x()" + export_types_ai["player_x::is_valid"] = "bool()" + export_types_ai["player_x::get_headquarter_level"] = "integer()" + export_types_ai["player_x::get_headquarter_pos"] = "coord()" + export_types_ai["player_x::get_name"] = "string()" + export_types_ai["player_x::set_name"] = "void(string)" + export_types_ai["player_x::get_construction"] = "array()" + export_types_ai["player_x::get_vehicle_maint"] = "array()" + export_types_ai["player_x::get_new_vehicles"] = "array()" + export_types_ai["player_x::get_income"] = "array()" + export_types_ai["player_x::get_maintenance"] = "array()" + export_types_ai["player_x::get_assets"] = "array()" + export_types_ai["player_x::get_cash"] = "array()" + export_types_ai["player_x::get_net_wealth"] = "array()" + export_types_ai["player_x::get_profit"] = "array()" + export_types_ai["player_x::get_operating_profit"] = "array()" + export_types_ai["player_x::get_margin"] = "array()" + export_types_ai["player_x::get_transported"] = "array()" + export_types_ai["player_x::get_powerline"] = "array()" + export_types_ai["player_x::get_transported_pax"] = "array()" + export_types_ai["player_x::get_transported_mail"] = "array()" + export_types_ai["player_x::get_transported_goods"] = "array()" + export_types_ai["player_x::get_convoys"] = "array()" + export_types_ai["player_x::get_way_tolls"] = "array()" + export_types_ai["player_x::get_current_cash"] = "float()" + export_types_ai["player_x::get_current_net_wealth"] = "integer()" + export_types_ai["player_x::get_current_maintenance"] = "integer()" + export_types_ai["player_x::create_line"] = "void(way_types)" + export_types_ai["player_x::get_type"] = "integer()" + export_types_ai["coord3d::get_halt"] = "halt_x(player_x)" + export_types_ai["settings::get_industry_increase_every"] = "integer()" + export_types_ai["settings::set_industry_increase_every"] = "void(integer)" + export_types_ai["settings::get_traffic_level"] = "integer()" + export_types_ai["settings::set_traffic_level"] = "void(integer)" + export_types_ai["settings::get_start_time"] = "time_x()" + export_types_ai["settings::get_station_coverage"] = "integer()" + export_types_ai["settings::get_passenger_factor"] = "integer()" + export_types_ai["settings::get_factory_worker_radius"] = "integer()" + export_types_ai["settings::get_factory_worker_minimum_towns"] = "integer()" + export_types_ai["settings::get_factory_worker_maximum_towns"] = "integer()" + export_types_ai["settings::avoid_overcrowding"] = "bool()" + export_types_ai["settings::no_routing_over_overcrowding"] = "bool()" + export_types_ai["settings::separate_halt_capacities"] = "bool()" + export_types_ai["settings::obsolete_vehicles_allowed"] = "bool()" + export_types_ai["settings::get_max_road_convoi_length"] = "integer()" + export_types_ai["settings::get_max_rail_convoi_length"] = "integer()" + export_types_ai["settings::get_max_ship_convoi_length"] = "integer()" + export_types_ai["settings::get_max_air_convoi_length"] = "integer()" + export_types_ai["settings::get_drive_on_left"] = "bool()" + export_types_ai["settings::get_pay_for_total_distance_mode"] = "integer()" + export_types_ai["dir::is_single"] = "bool(dir)" + export_types_ai["dir::is_twoway"] = "bool(dir)" + export_types_ai["dir::is_threeway"] = "bool(dir)" + export_types_ai["dir::is_curve"] = "bool(dir)" + export_types_ai["dir::is_straight"] = "bool(dir)" + export_types_ai["dir::double"] = "dir(dir)" + export_types_ai["dir::backward"] = "dir(dir)" + export_types_ai["dir::to_slope"] = "slope(dir)" + export_types_ai["slope::to_dir"] = "dir(slope)" + export_types_ai["::translate"] = "string(string)" + export_types_ai["::double_to_string"] = "string(float, integer)" + export_types_ai["::integer_to_string"] = "string(integer)" + export_types_ai["::money_to_string"] = "string(integer)" + export_types_ai["::coord_to_string"] = "string(coord)" + export_types_ai["::coord3d_to_string"] = "string(coord3d)" + export_types_ai["::get_month_name"] = "string(integer)" + export_types_ai["::difftick_to_string"] = "string(integer)" + export_types_ai["tile_x::is_valid"] = "bool()" + export_types_ai["tile_x::find_object"] = "map_object_x(map_objects)" + export_types_ai["tile_x::remove_object"] = "string(player_x, map_objects)" + export_types_ai["tile_x::get_halt"] = "halt_x()" + export_types_ai["tile_x::is_water"] = "bool()" + export_types_ai["tile_x::is_bridge"] = "bool()" + export_types_ai["tile_x::is_tunnel"] = "bool()" + export_types_ai["tile_x::is_empty"] = "bool()" + export_types_ai["tile_x::is_ground"] = "bool()" + export_types_ai["tile_x::get_slope"] = "slope()" + export_types_ai["tile_x::get_text"] = "string()" + export_types_ai["tile_x::has_way"] = "bool(way_types)" + export_types_ai["tile_x::has_ways"] = "bool()" + export_types_ai["tile_x::has_two_ways"] = "bool()" + export_types_ai["tile_x::get_way"] = "way_x(way_types)" + export_types_ai["tile_x::get_neighbour"] = "tile_x(way_types, dir)" + export_types_ai["tile_x::get_depot"] = "depot_x()" + export_types_ai["tile_x::can_remove_all_objects"] = "string(player_x)" + export_types_ai["tile_x::is_marked"] = "bool()" + export_types_ai["tile_x::unmark"] = "void()" + export_types_ai["tile_x::mark"] = "void()" + export_types_ai["tile_x::get_convoys"] = "array()" + export_types_ai["square_x::is_valid"] = "bool()" + export_types_ai["square_x::get_halt"] = "halt_x()" + export_types_ai["square_x::get_player_halt"] = "halt_x(player_x)" + export_types_ai["square_x::get_tile_at_height"] = "tile_x(integer)" + export_types_ai["square_x::get_ground_tile"] = "tile_x()" + export_types_ai["square_x::get_halt_list"] = "array()" + export_types_ai["square_x::get_climate"] = "climates()" + export_types_ai["world::is_coord_valid"] = "bool(coord)" + export_types_ai["world::find_nearest_city"] = "city_x(coord)" + export_types_ai["world::get_season"] = "integer()" + export_types_ai["world::get_player"] = "player_x(integer)" + export_types_ai["world::get_time"] = "time_ticks_x()" + export_types_ai["world::get_citizens"] = "array()" + export_types_ai["world::get_growth"] = "array()" + export_types_ai["world::get_towns"] = "array()" + export_types_ai["world::get_factories"] = "array()" + export_types_ai["world::get_convoys"] = "array()" + export_types_ai["world::get_citycars"] = "array()" + export_types_ai["world::get_ratio_pax"] = "array()" + export_types_ai["world::get_generated_pax"] = "array()" + export_types_ai["world::get_ratio_mail"] = "array()" + export_types_ai["world::get_generated_mail"] = "array()" + export_types_ai["world::get_ratio_goods"] = "array()" + export_types_ai["world::get_transported_goods"] = "array()" + export_types_ai["world::get_year_citizens"] = "array()" + export_types_ai["world::get_year_growth"] = "array()" + export_types_ai["world::get_year_towns"] = "array()" + export_types_ai["world::get_year_factories"] = "array()" + export_types_ai["world::get_year_convoys"] = "array()" + export_types_ai["world::get_year_citycars"] = "array()" + export_types_ai["world::get_year_ratio_pax"] = "array()" + export_types_ai["world::get_year_generated_pax"] = "array()" + export_types_ai["world::get_year_ratio_mail"] = "array()" + export_types_ai["world::get_year_generated_mail"] = "array()" + export_types_ai["world::get_year_ratio_goods"] = "array()" + export_types_ai["world::get_year_transported_goods"] = "array()" + export_types_ai["world::use_timeline"] = "bool()" + export_types_ai["attraction_list_x::_get"] = "building_x(integer)" + export_types_ai["::get_pakset_name"] = "string()" + export_types_ai["::get_version_number"] = "string()" + export_types_ai["simple_heap_x::clear"] = "void()" + export_types_ai["simple_heap_x::len"] = "integer()" + export_types_ai["simple_heap_x::is_empty"] = "bool()" + export_types_ai["simple_heap_x::insert"] = "void(integer, integer)" + export_types_ai["way_planner_x::set_build_types"] = "void(way_desc_x)" + export_types_ai["way_planner_x::is_allowed_step"] = "bool(tile_x, tile_x)" + export_types_ai["bridge_planner_x::find_end"] = "coord3d(player_x, coord3d, dir, bridge_desc_x, integer)" + export_types_ai["command_x::get_flags"] = "integer()" + export_types_ai["command_x::set_flags"] = "void(integer)" + export_types_ai["command_x::build_way"] = "string(player_x, coord3d, coord3d, way_desc_x, bool)" + export_types_ai["command_x::build_road"] = "string(player_x, coord3d, coord3d, way_desc_x, bool, bool)" + export_types_ai["command_x::build_depot"] = "string(player_x, coord3d, building_desc_x)" + export_types_ai["command_x::build_station"] = "string(player_x, coord3d, building_desc_x, dir)" + export_types_ai["command_x::build_bridge"] = "string(player_x, coord3d, coord3d, bridge_desc_x)" + export_types_ai["command_x::build_bridge_at"] = "string(player_x, coord3d, bridge_desc_x)" + export_types_ai["command_x::set_slope"] = "string(player_x, coord3d, slope)" + export_types_ai["command_x::restore_slope"] = "string(player_x, coord3d)" + export_types_ai["command_x::can_set_slope"] = "string(player_x, coord3d, slope)" + export_types_ai["command_x::slope_get_price"] = "integer(slope)" + export_types_ai["command_x::build_sign_at"] = "string(player_x, coord3d, sign_desc_x)" + export_types_ai["command_x::build_wayobj"] = "string(player_x, coord3d, coord3d, wayobj_desc_x)" + export_types_ai["command_x::change_climate_at"] = "string(player_x, coord3d, integer)" } diff --git a/script/api/squirrel_types_scenario.awk b/script/api/squirrel_types_scenario.awk index 133887a212a..d86589edd12 100644 --- a/script/api/squirrel_types_scenario.awk +++ b/script/api/squirrel_types_scenario.awk @@ -2,322 +2,434 @@ # This file is part of the Simutrans-Extended project under the Artistic License. # (see LICENSE.txt) # - # file used to generate doxygen documentation of squirrel API # needs to be copied to trunk/script/api BEGIN { - export_types["city_list_x::_get"] = "city_x(integer)" - export_types["city_x::get_name"] = "string()" - export_types["city_x::set_name"] = "string(string)" - export_types["city_x::get_citizens"] = "array()" - export_types["city_x::get_growth"] = "array()" - export_types["city_x::get_buildings"] = "array()" - export_types["city_x::get_citycars"] = "array()" - export_types["city_x::get_transported_pax"] = "array()" - export_types["city_x::get_generated_pax"] = "array()" - export_types["city_x::get_transported_mail"] = "array()" - export_types["city_x::get_generated_mail"] = "array()" - export_types["city_x::get_year_citizens"] = "array()" - export_types["city_x::get_year_growth"] = "array()" - export_types["city_x::get_year_buildings"] = "array()" - export_types["city_x::get_year_citycars"] = "array()" - export_types["city_x::get_year_transported_pax"] = "array()" - export_types["city_x::get_year_generated_pax"] = "array()" - export_types["city_x::get_year_transported_mail"] = "array()" - export_types["city_x::get_year_generated_mail"] = "array()" - export_types["city_x::get_citygrowth_enabled"] = "bool()" - export_types["city_x::get_pos"] = "coord()" - export_types["city_x::get_pos_nw"] = "coord()" - export_types["city_x::get_pos_se"] = "coord()" - export_types["city_x::change_size"] = "string(integer)" - export_types["city_x::set_citygrowth_enabled"] = "string(bool)" - export_types["convoy_x::needs_electrification"] = "bool()" - export_types["convoy_x::get_name"] = "string()" - export_types["convoy_x::set_name"] = "string(string)" - export_types["convoy_x::get_pos"] = "coord3d()" - export_types["convoy_x::get_owner"] = "player_x()" - export_types["convoy_x::get_goods_catg_index"] = "array()" - export_types["convoy_x::get_waytype"] = "way_types()" - export_types["convoy_x::get_schedule"] = "schedule_x()" - export_types["convoy_x::get_capacity"] = "array()" - export_types["convoy_x::get_transported_goods"] = "array()" - export_types["convoy_x::get_revenue"] = "array()" - export_types["convoy_x::get_cost"] = "array()" - export_types["convoy_x::get_profit"] = "array()" - export_types["convoy_x::get_traveled_distance"] = "array()" - export_types["convoy_x::get_way_tolls"] = "array()" - export_types["convoy_x::get_distance_traveled_total"] = "integer()" - export_types["convoy_x::get_line"] = "line_x()" - export_types["convoy_x::set_line"] = "string(player_x, line_x)" - export_types["convoy_x::get_vehicles"] = "array()" - export_types["convoy_x::get_speed"] = "integer()" - export_types["convoy_x::get_loading_limit"] = "integer()" - export_types["convoy_x::get_loading_level"] = "integer()" - export_types["convoy_x::get_home_depot"] = "coord3d()" - export_types["convoy_x::has_obsolete_vehicles"] = "bool()" - export_types["convoy_x::toggle_withdraw"] = "string(player_x)" - export_types["convoy_x::is_withdrawn"] = "bool()" - export_types["convoy_x::destroy"] = "string(player_x)" - export_types["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" - export_types["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" - export_types["factory_x::get_consumers"] = "array()" - export_types["factory_x::get_suppliers"] = "array()" - export_types["factory_x::get_name"] = "string()" - export_types["factory_x::set_name"] = "string(string)" - export_types["factory_x::get_production"] = "array()" - export_types["factory_x::get_power"] = "array()" - export_types["factory_x::get_boost_electric"] = "array()" - export_types["factory_x::get_boost_pax"] = "array()" - export_types["factory_x::get_boost_mail"] = "array()" - export_types["factory_x::get_pax_generated"] = "array()" - export_types["factory_x::get_pax_departed"] = "array()" - export_types["factory_x::get_pax_arrived"] = "array()" - export_types["factory_x::get_mail_generated"] = "array()" - export_types["factory_x::get_mail_departed"] = "array()" - export_types["factory_x::get_mail_arrived"] = "array()" - export_types["factory_x::get_tile_list"] = "array()" - export_types["factory_x::get_halt_list"] = "array()" - export_types["factory_production_x::get_storage"] = "array()" - export_types["factory_production_x::get_received"] = "array()" - export_types["factory_production_x::get_consumed"] = "array()" - export_types["factory_production_x::get_in_transit"] = "array()" - export_types["factory_production_x::get_delivered"] = "array()" - export_types["factory_production_x::get_produced"] = "array()" - export_types["factory_production_x::get_consumption_factor"] = "integer()" - export_types["factory_production_x::get_production_factor"] = "integer()" - export_types["obj_desc_x::get_name"] = "string()" - export_types["obj_desc_x::is_equal"] = "bool(obj_desc_x)" - export_types["obj_desc_time_x::get_intro_date"] = "time_x()" - export_types["obj_desc_time_x::get_retire_date"] = "time_x()" - export_types["obj_desc_time_x::is_future"] = "bool(time_x)" - export_types["obj_desc_time_x::is_retired"] = "bool(time_x)" - export_types["obj_desc_time_x::is_available"] = "bool(time_x)" - export_types["obj_desc_transport_x::get_maintenance"] = "integer()" - export_types["obj_desc_transport_x::get_cost"] = "integer()" - export_types["obj_desc_transport_x::get_waytype"] = "way_types()" - export_types["obj_desc_transport_x::get_topspeed"] = "integer()" - export_types["vehicle_desc_x::can_be_first"] = "bool()" - export_types["vehicle_desc_x::can_be_last"] = "bool()" - export_types["vehicle_desc_x::get_successors"] = "array()" - export_types["vehicle_desc_x::get_predecessors"] = "array()" - export_types["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" - export_types["vehicle_desc_x::get_power"] = "integer()" - export_types["vehicle_desc_x::get_freight"] = "good_desc_x()" - export_types["vehicle_desc_x::get_capacity"] = "integer()" - export_types["vehicle_desc_x::get_running_cost"] = "integer()" - export_types["vehicle_desc_x::get_maintenance"] = "integer()" - export_types["vehicle_desc_x::get_weight"] = "integer()" - export_types["vehicle_desc_x::get_length"] = "integer()" - export_types["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" - export_types["building_desc_x::is_attraction"] = "bool()" - export_types["building_desc_x::get_size"] = "coord(integer)" - export_types["building_desc_x::get_maintenance"] = "integer()" - export_types["building_desc_x::get_cost"] = "integer()" - export_types["building_desc_x::get_capacity"] = "integer()" - export_types["building_desc_x::can_be_built_underground"] = "bool()" - export_types["building_desc_x::can_be_built_aboveground"] = "bool()" - export_types["building_desc_x::enables_pax"] = "bool()" - export_types["building_desc_x::enables_mail"] = "bool()" - export_types["building_desc_x::enables_freight"] = "bool()" - export_types["building_desc_x::get_type"] = "building_desc_x::building_type()" - export_types["building_desc_x::get_waytype"] = "way_types()" - export_types["building_desc_x::get_headquarter_level"] = "integer()" - export_types["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" - export_types["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" - export_types["building_desc_x::is_terminus"] = "bool()" - export_types["way_desc_x::has_double_slopes"] = "bool()" - export_types["way_desc_x::get_system_type"] = "integer()" - export_types["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" - export_types["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" - export_types["bridge_desc_x::has_double_ramp"] = "bool()" - export_types["bridge_desc_x::has_double_start"] = "bool()" - export_types["bridge_desc_x::get_max_length"] = "integer()" - export_types["bridge_desc_x::get_max_height"] = "integer()" - export_types["bridge_desc_x::get_available_bridges"] = "array(way_types)" - export_types["good_desc_x::get_catg_index"] = "integer()" - export_types["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" - export_types["good_desc_x::get_weight_per_unit"] = "integer()" - export_types["good_desc_x::calc_revenue"] = "integer(way_types, integer)" - export_types["::open_info_win"] = "bool()" - export_types["gui::add_message_at"] = "void(string, coord)" - export_types["gui::add_message"] = "string(player_x, string)" - export_types["halt_x::get_name"] = "string()" - export_types["halt_x::set_name"] = "string(string)" - export_types["halt_x::get_owner"] = "player_x()" - export_types["halt_x::_cmp"] = "integer(halt_x)" - export_types["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" - export_types["halt_x::accepts_good"] = "bool(good_desc_x)" - export_types["halt_x::get_arrived"] = "array()" - export_types["halt_x::get_departed"] = "array()" - export_types["halt_x::get_waiting"] = "array()" - export_types["halt_x::get_happy"] = "array()" - export_types["halt_x::get_unhappy"] = "array()" - export_types["halt_x::get_noroute"] = "array()" - export_types["halt_x::get_convoys"] = "array()" - export_types["halt_x::get_walked"] = "array()" - export_types["halt_x::get_tile_list"] = "array()" - export_types["halt_x::get_factory_list"] = "array()" - export_types["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" - export_types["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" - export_types["halt_x::get_capacity"] = "integer(good_desc_x)" - export_types["line_x::is_valid"] = "bool()" - export_types["line_x::get_name"] = "string()" - export_types["line_x::set_name"] = "string(string)" - export_types["line_x::get_owner"] = "player_x()" - export_types["line_x::get_schedule"] = "schedule_x()" - export_types["line_x::get_goods_catg_index"] = "array()" - export_types["line_x::get_capacity"] = "array()" - export_types["line_x::get_transported_goods"] = "array()" - export_types["line_x::get_convoy_count"] = "array()" - export_types["line_x::get_revenue"] = "array()" - export_types["line_x::get_cost"] = "array()" - export_types["line_x::get_profit"] = "array()" - export_types["line_x::get_traveled_distance"] = "array()" - export_types["line_x::get_way_tolls"] = "array()" - export_types["line_x::get_waytype"] = "way_types()" - export_types["line_x::change_schedule"] = "string(player_x, schedule_x)" - export_types["map_object_x::get_owner"] = "player_x()" - export_types["map_object_x::get_name"] = "string()" - export_types["map_object_x::get_waytype"] = "way_types()" - export_types["map_object_x::get_pos"] = "coord3d()" - export_types["map_object_x::is_removable"] = "string(player_x)" - export_types["map_object_x::get_type"] = "map_objects()" - export_types["tree_x::get_age"] = "integer()" - export_types["tree_x::get_desc"] = "tree_desc_x()" - export_types["building_x::get_factory"] = "factory_x()" - export_types["building_x::get_city"] = "city_x()" - export_types["building_x::is_townhall"] = "bool()" - export_types["building_x::is_headquarter"] = "bool()" - export_types["building_x::is_monument"] = "bool()" - export_types["building_x::get_passenger_level"] = "integer()" - export_types["building_x::get_mail_level"] = "integer()" - export_types["building_x::get_desc"] = "building_desc_x()" - export_types["building_x::is_same_building"] = "bool(building_x)" - export_types["depot_x::append_vehicle"] = "string(player_x, convoy_x, vehicle_desc_x)" - export_types["depot_x::start_convoy"] = "string(player_x, convoy_x)" - export_types["depot_x::start_all_convoys"] = "string(player_x)" - export_types["depot_x::get_convoy_list"] = "array()" - export_types["way_x::has_sidewalk"] = "bool()" - export_types["way_x::is_electrified"] = "bool()" - export_types["way_x::has_sign"] = "bool()" - export_types["way_x::has_signal"] = "bool()" - export_types["way_x::has_wayobj"] = "bool()" - export_types["way_x::is_crossing"] = "bool()" - export_types["way_x::get_desc"] = "way_desc_x()" - export_types["label_x::create"] = "string(coord, player_x, string)" - export_types["label_x::set_text"] = "string(string)" - export_types["label_x::get_text"] = "string()" - export_types["player_x::get_headquarter_level"] = "integer()" - export_types["player_x::get_headquarter_pos"] = "coord()" - export_types["player_x::get_name"] = "string()" - export_types["player_x::set_name"] = "string(string)" - export_types["player_x::get_construction"] = "array()" - export_types["player_x::get_vehicle_maint"] = "array()" - export_types["player_x::get_new_vehicles"] = "array()" - export_types["player_x::get_income"] = "array()" - export_types["player_x::get_maintenance"] = "array()" - export_types["player_x::get_assets"] = "array()" - export_types["player_x::get_cash"] = "array()" - export_types["player_x::get_net_wealth"] = "array()" - export_types["player_x::get_profit"] = "array()" - export_types["player_x::get_operating_profit"] = "array()" - export_types["player_x::get_margin"] = "array()" - export_types["player_x::get_transported"] = "array()" - export_types["player_x::get_powerline"] = "array()" - export_types["player_x::get_transported_pax"] = "array()" - export_types["player_x::get_transported_mail"] = "array()" - export_types["player_x::get_transported_goods"] = "array()" - export_types["player_x::get_convoys"] = "array()" - export_types["player_x::get_way_tolls"] = "array()" - export_types["player_x::book_cash"] = "string(integer)" - export_types["player_x::get_current_cash"] = "float()" - export_types["player_x::get_current_net_wealth"] = "integer()" - export_types["player_x::get_current_maintenance"] = "integer()" - export_types["player_x::create_line"] = "string(way_types)" - export_types["::load_language_file"] = "string(string)" - export_types["::forbid_tool"] = "void(integer, integer)" - export_types["::allow_tool"] = "void(integer, integer)" - export_types["::forbid_way_tool"] = "void(integer, integer, way_types)" - export_types["::allow_way_tool"] = "void(integer, integer, way_types)" - export_types["::forbid_way_tool_rect"] = "void(integer, integer, way_types, coord, coord, string)" - export_types["::allow_way_tool_rect"] = "void(integer, integer, way_types, coord, coord)" - export_types["::forbid_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d, string)" - export_types["::allow_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d)" - export_types["::clear"] = "void()" - export_types["::get_forbidden_text"] = "string()" - export_types["coord3d::get_halt"] = "halt_x(player_x)" - export_types["settings::get_industry_increase_every"] = "integer()" - export_types["settings::set_industry_increase_every"] = "void(integer)" - export_types["settings::get_traffic_level"] = "integer()" - export_types["settings::set_traffic_level"] = "string(integer)" - export_types["settings::get_start_time"] = "time_x()" - export_types["settings::get_station_coverage"] = "integer()" - export_types["settings::get_passenger_factor"] = "integer()" - export_types["settings::get_factory_worker_radius"] = "integer()" - export_types["settings::get_factory_worker_minimum_towns"] = "integer()" - export_types["settings::get_factory_worker_maximum_towns"] = "integer()" - export_types["settings::avoid_overcrowding"] = "bool()" - export_types["settings::no_routing_over_overcrowding"] = "bool()" - export_types["settings::separate_halt_capacities"] = "bool()" - export_types["settings::obsolete_vehicles_allowed"] = "bool()" - export_types["::translate"] = "string(string)" - export_types["::double_to_string"] = "string(float, integer)" - export_types["::integer_to_string"] = "string(integer)" - export_types["::money_to_string"] = "string(integer)" - export_types["::coord_to_string"] = "string(coord)" - export_types["::coord3d_to_string"] = "string(coord3d)" - export_types["::get_month_name"] = "string(integer)" - export_types["tile_x::find_object"] = "map_object_x(map_objects)" - export_types["tile_x::remove_object"] = "string(player_x, map_objects)" - export_types["tile_x::get_halt"] = "halt_x()" - export_types["tile_x::is_water"] = "bool()" - export_types["tile_x::is_bridge"] = "bool()" - export_types["tile_x::is_tunnel"] = "bool()" - export_types["tile_x::is_empty"] = "bool()" - export_types["tile_x::is_ground"] = "bool()" - export_types["tile_x::get_slope"] = "integer()" - export_types["tile_x::get_text"] = "string()" - export_types["tile_x::has_way"] = "bool(way_types)" - export_types["tile_x::has_ways"] = "bool()" - export_types["tile_x::has_two_ways"] = "bool()" - export_types["square_x::get_halt"] = "halt_x()" - export_types["square_x::get_player_halt"] = "halt_x(player_x)" - export_types["square_x::get_tile_at_height"] = "tile_x(integer)" - export_types["square_x::get_ground_tile"] = "tile_x()" - export_types["square_x::get_halt_list"] = "array()" - export_types["square_x::get_climate"] = "climates()" - export_types["world::is_coord_valid"] = "bool(coord)" - export_types["world::find_nearest_city"] = "city_x(coord)" - export_types["world::get_season"] = "integer()" - export_types["world::remove_player"] = "bool(player_x)" - export_types["integer::get_player"] = "player_x()" - export_types["world::get_time"] = "time_ticks_x()" - export_types["world::get_citizens"] = "array()" - export_types["world::get_growth"] = "array()" - export_types["world::get_towns"] = "array()" - export_types["world::get_factories"] = "array()" - export_types["world::get_convoys"] = "array()" - export_types["world::get_citycars"] = "array()" - export_types["world::get_ratio_pax"] = "array()" - export_types["world::get_generated_pax"] = "array()" - export_types["world::get_ratio_mail"] = "array()" - export_types["world::get_generated_mail"] = "array()" - export_types["world::get_ratio_goods"] = "array()" - export_types["world::get_transported_goods"] = "array()" - export_types["world::get_year_citizens"] = "array()" - export_types["world::get_year_growth"] = "array()" - export_types["world::get_year_towns"] = "array()" - export_types["world::get_year_factories"] = "array()" - export_types["world::get_year_convoys"] = "array()" - export_types["world::get_year_citycars"] = "array()" - export_types["world::get_year_ratio_pax"] = "array()" - export_types["world::get_year_generated_pax"] = "array()" - export_types["world::get_year_ratio_mail"] = "array()" - export_types["world::get_year_generated_mail"] = "array()" - export_types["world::get_year_ratio_goods"] = "array()" - export_types["world::get_year_transported_goods"] = "array()" - export_types["world::use_timeline"] = "bool()" - export_types["attraction_list_x::_get"] = "building_x(integer)" - export_types["command_x::get_flags"] = "integer()" - export_types["command_x::set_flags"] = "void(integer)" + export_types_scenario["city_list_x::_get"] = "city_x(integer)" + export_types_scenario["city_x::is_valid"] = "bool()" + export_types_scenario["city_x::get_name"] = "string()" + export_types_scenario["city_x::set_name"] = "void(string)" + export_types_scenario["city_x::get_citizens"] = "array()" + export_types_scenario["city_x::get_growth"] = "array()" + export_types_scenario["city_x::get_buildings"] = "array()" + export_types_scenario["city_x::get_citycars"] = "array()" + export_types_scenario["city_x::get_transported_pax"] = "array()" + export_types_scenario["city_x::get_generated_pax"] = "array()" + export_types_scenario["city_x::get_transported_mail"] = "array()" + export_types_scenario["city_x::get_generated_mail"] = "array()" + export_types_scenario["city_x::get_year_citizens"] = "array()" + export_types_scenario["city_x::get_year_growth"] = "array()" + export_types_scenario["city_x::get_year_buildings"] = "array()" + export_types_scenario["city_x::get_year_citycars"] = "array()" + export_types_scenario["city_x::get_year_transported_pax"] = "array()" + export_types_scenario["city_x::get_year_generated_pax"] = "array()" + export_types_scenario["city_x::get_year_transported_mail"] = "array()" + export_types_scenario["city_x::get_year_generated_mail"] = "array()" + export_types_scenario["city_x::get_citygrowth_enabled"] = "bool()" + export_types_scenario["city_x::get_pos"] = "coord()" + export_types_scenario["city_x::get_pos_nw"] = "coord()" + export_types_scenario["city_x::get_pos_se"] = "coord()" + export_types_scenario["city_x::change_size"] = "string(integer)" + export_types_scenario["city_x::set_citygrowth_enabled"] = "void(bool)" + export_types_scenario["::get_ops_total"] = "integer()" + export_types_scenario["::get_ops_remaining"] = "integer()" + export_types_scenario["debug::pause"] = "bool()" + export_types_scenario["debug::is_paused"] = "bool()" + export_types_scenario["debug::set_pause_on_error"] = "void(bool)" + export_types_scenario["convoy_x::is_valid"] = "bool()" + export_types_scenario["convoy_x::needs_electrification"] = "bool()" + export_types_scenario["convoy_x::get_name"] = "string()" + export_types_scenario["convoy_x::set_name"] = "void(string)" + export_types_scenario["convoy_x::get_pos"] = "coord3d()" + export_types_scenario["convoy_x::get_owner"] = "player_x()" + export_types_scenario["convoy_x::get_goods_catg_index"] = "array()" + export_types_scenario["convoy_x::get_waytype"] = "way_types()" + export_types_scenario["convoy_x::get_schedule"] = "schedule_x()" + export_types_scenario["convoy_x::get_capacity"] = "array()" + export_types_scenario["convoy_x::get_transported_goods"] = "array()" + export_types_scenario["convoy_x::get_revenue"] = "array()" + export_types_scenario["convoy_x::get_cost"] = "array()" + export_types_scenario["convoy_x::get_profit"] = "array()" + export_types_scenario["convoy_x::get_traveled_distance"] = "array()" + export_types_scenario["convoy_x::get_way_tolls"] = "array()" + export_types_scenario["convoy_x::get_distance_traveled_total"] = "integer()" + export_types_scenario["convoy_x::get_line"] = "line_x()" + export_types_scenario["convoy_x::set_line"] = "void(player_x, line_x)" + export_types_scenario["convoy_x::get_vehicles"] = "array()" + export_types_scenario["convoy_x::get_speed"] = "integer()" + export_types_scenario["convoy_x::get_loading_limit"] = "integer()" + export_types_scenario["convoy_x::get_loading_level"] = "integer()" + export_types_scenario["convoy_x::get_home_depot"] = "coord3d()" + export_types_scenario["convoy_x::has_obsolete_vehicles"] = "bool()" + export_types_scenario["convoy_x::toggle_withdraw"] = "void(player_x)" + export_types_scenario["convoy_x::is_withdrawn"] = "bool()" + export_types_scenario["convoy_x::is_in_depot"] = "bool()" + export_types_scenario["convoy_x::is_waiting"] = "bool()" + export_types_scenario["convoy_x::is_loading"] = "bool()" + export_types_scenario["convoy_x::destroy"] = "void(player_x)" + export_types_scenario["convoy_x::is_schedule_editor_open"] = "bool()" + export_types_scenario["convoy_x::get_tile_length"] = "integer()" + export_types_scenario["convoy_x::change_schedule"] = "void(player_x, schedule_x)" + export_types_scenario["convoy_x::calc_max_speed"] = "integer(integer, integer, integer)" + export_types_scenario["convoy_x::speed_to_tiles_per_month"] = "integer(integer)" + export_types_scenario["factory_x::is_valid"] = "bool()" + export_types_scenario["factory_x::get_consumers"] = "array()" + export_types_scenario["factory_x::get_suppliers"] = "array()" + export_types_scenario["factory_x::get_name"] = "string()" + export_types_scenario["factory_x::get_raw_name"] = "string()" + export_types_scenario["factory_x::set_name"] = "void(string)" + export_types_scenario["factory_x::get_production"] = "array()" + export_types_scenario["factory_x::get_power"] = "array()" + export_types_scenario["factory_x::get_boost_electric"] = "array()" + export_types_scenario["factory_x::get_boost_pax"] = "array()" + export_types_scenario["factory_x::get_boost_mail"] = "array()" + export_types_scenario["factory_x::get_pax_generated"] = "array()" + export_types_scenario["factory_x::get_pax_departed"] = "array()" + export_types_scenario["factory_x::get_pax_arrived"] = "array()" + export_types_scenario["factory_x::get_mail_generated"] = "array()" + export_types_scenario["factory_x::get_mail_departed"] = "array()" + export_types_scenario["factory_x::get_mail_arrived"] = "array()" + export_types_scenario["factory_x::get_tile_list"] = "array()" + export_types_scenario["factory_x::get_halt_list"] = "array()" + export_types_scenario["factory_x::is_transformer_connected"] = "bool()" + export_types_scenario["factory_x::get_transformer"] = "powerline_x()" + export_types_scenario["factory_x::get_field_count"] = "integer()" + export_types_scenario["factory_x::get_min_field_count"] = "integer()" + export_types_scenario["factory_x::get_desc"] = "factory_desc_x()" + export_types_scenario["factory_production_x::get_storage"] = "array()" + export_types_scenario["factory_production_x::get_received"] = "array()" + export_types_scenario["factory_production_x::get_consumed"] = "array()" + export_types_scenario["factory_production_x::get_in_transit"] = "array()" + export_types_scenario["factory_production_x::get_delivered"] = "array()" + export_types_scenario["factory_production_x::get_produced"] = "array()" + export_types_scenario["factory_production_x::get_consumption_factor"] = "integer()" + export_types_scenario["factory_production_x::get_production_factor"] = "integer()" + export_types_scenario["obj_desc_x::get_name"] = "string()" + export_types_scenario["obj_desc_x::is_equal"] = "bool(obj_desc_x)" + export_types_scenario["obj_desc_x::is_valid"] = "bool()" + export_types_scenario["obj_desc_time_x::get_intro_date"] = "time_x()" + export_types_scenario["obj_desc_time_x::get_retire_date"] = "time_x()" + export_types_scenario["obj_desc_time_x::is_future"] = "bool(time_x)" + export_types_scenario["obj_desc_time_x::is_retired"] = "bool(time_x)" + export_types_scenario["obj_desc_time_x::is_available"] = "bool(time_x)" + export_types_scenario["obj_desc_transport_x::get_maintenance"] = "integer()" + export_types_scenario["obj_desc_transport_x::get_cost"] = "integer()" + export_types_scenario["obj_desc_transport_x::get_waytype"] = "way_types()" + export_types_scenario["obj_desc_transport_x::get_topspeed"] = "integer()" + export_types_scenario["vehicle_desc_x::can_be_first"] = "bool()" + export_types_scenario["vehicle_desc_x::can_be_last"] = "bool()" + export_types_scenario["vehicle_desc_x::get_successors"] = "array()" + export_types_scenario["vehicle_desc_x::get_predecessors"] = "array()" + export_types_scenario["vehicle_desc_x::get_available_vehicles"] = "array(way_types)" + export_types_scenario["vehicle_desc_x::get_power"] = "integer()" + export_types_scenario["vehicle_desc_x::needs_electrification"] = "bool()" + export_types_scenario["vehicle_desc_x::get_freight"] = "good_desc_x()" + export_types_scenario["vehicle_desc_x::get_capacity"] = "integer()" + export_types_scenario["vehicle_desc_x::get_running_cost"] = "integer()" + export_types_scenario["vehicle_desc_x::get_maintenance"] = "integer()" + export_types_scenario["vehicle_desc_x::get_weight"] = "integer()" + export_types_scenario["vehicle_desc_x::get_length"] = "integer()" + export_types_scenario["vehicle_desc_x::is_coupling_allowed"] = "bool(vehicle_desc_x, vehicle_desc_x)" + export_types_scenario["tree_desc_x::get_price"] = "integer()" + export_types_scenario["building_desc_x::is_attraction"] = "bool()" + export_types_scenario["building_desc_x::get_maintenance"] = "integer()" + export_types_scenario["building_desc_x::get_cost"] = "integer()" + export_types_scenario["building_desc_x::get_capacity"] = "integer()" + export_types_scenario["building_desc_x::can_be_built_underground"] = "bool()" + export_types_scenario["building_desc_x::can_be_built_aboveground"] = "bool()" + export_types_scenario["building_desc_x::enables_pax"] = "bool()" + export_types_scenario["building_desc_x::enables_mail"] = "bool()" + export_types_scenario["building_desc_x::enables_freight"] = "bool()" + export_types_scenario["building_desc_x::get_type"] = "building_desc_x::building_type()" + export_types_scenario["building_desc_x::get_waytype"] = "way_types()" + export_types_scenario["building_desc_x::get_headquarter_level"] = "integer()" + export_types_scenario["building_desc_x::get_building_list"] = "array(building_desc_x::building_type)" + export_types_scenario["building_desc_x::get_available_stations"] = "array(building_desc_x::building_type, way_types, good_desc_x)" + export_types_scenario["building_desc_x::is_terminus"] = "bool()" + export_types_scenario["factory_desc_x::get_name"] = "string()" + export_types_scenario["factory_desc_x::get_building_desc"] = "building_desc_x()" + export_types_scenario["factory_desc_x::is_electricity_producer"] = "bool()" + export_types_scenario["factory_desc_x::get_productivity_base"] = "integer()" + export_types_scenario["factory_desc_x::get_productivity_range"] = "integer()" + export_types_scenario["factory_desc_x::get_list"] = "table()" + export_types_scenario["way_desc_x::has_double_slopes"] = "bool()" + export_types_scenario["way_desc_x::get_system_type"] = "way_system_types()" + export_types_scenario["way_desc_x::get_available_ways"] = "array(way_types, way_system_types)" + export_types_scenario["tunnel_desc_x::get_available_tunnels"] = "array(way_types)" + export_types_scenario["bridge_desc_x::has_double_ramp"] = "bool()" + export_types_scenario["bridge_desc_x::has_double_start"] = "bool()" + export_types_scenario["bridge_desc_x::get_max_length"] = "integer()" + export_types_scenario["bridge_desc_x::get_max_height"] = "integer()" + export_types_scenario["bridge_desc_x::get_available_bridges"] = "array(way_types)" + export_types_scenario["good_desc_x::get_catg_index"] = "integer()" + export_types_scenario["good_desc_x::is_interchangeable"] = "bool(good_desc_x)" + export_types_scenario["good_desc_x::get_weight_per_unit"] = "integer()" + export_types_scenario["good_desc_x::calc_revenue"] = "integer(way_types, integer)" + export_types_scenario["sign_desc_x::is_one_way"] = "bool()" + export_types_scenario["sign_desc_x::is_private_way"] = "bool()" + export_types_scenario["sign_desc_x::is_traffic_light"] = "bool()" + export_types_scenario["sign_desc_x::is_choose_sign"] = "bool()" + export_types_scenario["sign_desc_x::is_signal"] = "bool()" + export_types_scenario["sign_desc_x::is_pre_signal"] = "bool()" + export_types_scenario["sign_desc_x::is_priority_signal"] = "bool()" + export_types_scenario["sign_desc_x::is_longblock_signal"] = "bool()" + export_types_scenario["sign_desc_x::is_end_choose_signal"] = "bool()" + export_types_scenario["sign_desc_x::get_available_signs"] = "array(way_types)" + export_types_scenario["wayobj_desc_x::is_overhead_line"] = "bool()" + export_types_scenario["wayobj_desc_x::get_available_wayobjs"] = "array(way_types)" + export_types_scenario["gui::open_info_win"] = "void()" + export_types_scenario["gui::open_info_win_at"] = "void(string)" + export_types_scenario["gui::open_info_win_client"] = "void(string, integer)" + export_types_scenario["gui::add_message_at"] = "string(string, coord)" + export_types_scenario["gui::add_message"] = "string(player_x, string)" + export_types_scenario["halt_x::is_valid"] = "bool()" + export_types_scenario["halt_x::get_name"] = "string()" + export_types_scenario["halt_x::set_name"] = "void(string)" + export_types_scenario["halt_x::get_owner"] = "player_x()" + export_types_scenario["halt_x::_cmp"] = "integer(halt_x)" + export_types_scenario["halt_x::is_connected"] = "integer(halt_x, good_desc_x)" + export_types_scenario["halt_x::accepts_good"] = "bool(good_desc_x)" + export_types_scenario["halt_x::get_arrived"] = "array()" + export_types_scenario["halt_x::get_departed"] = "array()" + export_types_scenario["halt_x::get_waiting"] = "array()" + export_types_scenario["halt_x::get_happy"] = "array()" + export_types_scenario["halt_x::get_unhappy"] = "array()" + export_types_scenario["halt_x::get_noroute"] = "array()" + export_types_scenario["halt_x::get_convoys"] = "array()" + export_types_scenario["halt_x::get_walked"] = "array()" + export_types_scenario["halt_x::get_tile_list"] = "array()" + export_types_scenario["halt_x::get_factory_list"] = "array()" + export_types_scenario["halt_x::get_freight_to_dest"] = "integer(good_desc_x, coord)" + export_types_scenario["halt_x::get_freight_to_halt"] = "integer(good_desc_x, halt_x)" + export_types_scenario["halt_x::get_capacity"] = "integer(good_desc_x)" + export_types_scenario["halt_x::get_connections"] = "array(good_desc_x)" + export_types_scenario["halt_x::get_halt"] = "halt_x(coord3d, player_x)" + export_types_scenario["line_x::is_valid"] = "bool()" + export_types_scenario["line_x::get_name"] = "string()" + export_types_scenario["line_x::set_name"] = "void(string)" + export_types_scenario["line_x::get_owner"] = "player_x()" + export_types_scenario["line_x::get_schedule"] = "schedule_x()" + export_types_scenario["line_x::get_goods_catg_index"] = "array()" + export_types_scenario["line_x::get_capacity"] = "array()" + export_types_scenario["line_x::get_transported_goods"] = "array()" + export_types_scenario["line_x::get_convoy_count"] = "array()" + export_types_scenario["line_x::get_revenue"] = "array()" + export_types_scenario["line_x::get_cost"] = "array()" + export_types_scenario["line_x::get_profit"] = "array()" + export_types_scenario["line_x::get_traveled_distance"] = "array()" + export_types_scenario["line_x::get_way_tolls"] = "array()" + export_types_scenario["line_x::get_waytype"] = "way_types()" + export_types_scenario["line_x::change_schedule"] = "void(player_x, schedule_x)" + export_types_scenario["line_x::destroy"] = "void(player_x)" + export_types_scenario["map_object_x::is_valid"] = "bool()" + export_types_scenario["map_object_x::get_owner"] = "player_x()" + export_types_scenario["map_object_x::get_name"] = "string()" + export_types_scenario["map_object_x::get_waytype"] = "way_types()" + export_types_scenario["map_object_x::get_pos"] = "coord3d()" + export_types_scenario["map_object_x::is_removable"] = "string(player_x)" + export_types_scenario["map_object_x::get_type"] = "map_objects()" + export_types_scenario["map_object_x::mark"] = "void()" + export_types_scenario["map_object_x::unmark"] = "void()" + export_types_scenario["map_object_x::is_marked"] = "bool()" + export_types_scenario["tree_x::get_age"] = "integer()" + export_types_scenario["tree_x::get_desc"] = "tree_desc_x()" + export_types_scenario["building_x::get_factory"] = "factory_x()" + export_types_scenario["building_x::get_city"] = "city_x()" + export_types_scenario["building_x::is_townhall"] = "bool()" + export_types_scenario["building_x::is_headquarter"] = "bool()" + export_types_scenario["building_x::is_monument"] = "bool()" + export_types_scenario["building_x::get_passenger_level"] = "integer()" + export_types_scenario["building_x::get_mail_level"] = "integer()" + export_types_scenario["building_x::get_desc"] = "building_desc_x()" + export_types_scenario["building_x::get_tile_list"] = "array()" + export_types_scenario["building_x::is_same_building"] = "bool(building_x)" + export_types_scenario["depot_x::append_vehicle"] = "void(player_x, convoy_x, vehicle_desc_x)" + export_types_scenario["depot_x::start_convoy"] = "void(player_x, convoy_x)" + export_types_scenario["depot_x::start_all_convoys"] = "void(player_x)" + export_types_scenario["depot_x::get_convoy_list"] = "array()" + export_types_scenario["depot_x::get_depot_list"] = "array(player_x, way_types)" + export_types_scenario["way_x::has_sidewalk"] = "bool()" + export_types_scenario["way_x::is_electrified"] = "bool()" + export_types_scenario["way_x::has_sign"] = "bool()" + export_types_scenario["way_x::has_signal"] = "bool()" + export_types_scenario["way_x::has_wayobj"] = "bool()" + export_types_scenario["way_x::is_crossing"] = "bool()" + export_types_scenario["way_x::get_desc"] = "way_desc_x()" + export_types_scenario["way_x::get_max_speed"] = "integer()" + export_types_scenario["way_x::get_transported_goods"] = "array()" + export_types_scenario["way_x::get_convoys_passed"] = "array()" + export_types_scenario["label_x::create"] = "string(coord, player_x, string)" + export_types_scenario["label_x::set_text"] = "void(string)" + export_types_scenario["label_x::get_text"] = "string()" + export_types_scenario["sign_x::get_desc"] = "sign_desc_x()" + export_types_scenario["sign_x::can_pass"] = "bool(player_x)" + export_types_scenario["powerline_x::is_connected"] = "bool(powerline_x)" + export_types_scenario["powerline_x::get_factory"] = "factory_x()" + export_types_scenario["field_x::is_deletable"] = "bool()" + export_types_scenario["field_x::get_factory"] = "factory_x()" + export_types_scenario["wayobj_x::get_desc"] = "wayobj_desc_x()" + export_types_scenario["player_x::is_valid"] = "bool()" + export_types_scenario["player_x::get_headquarter_level"] = "integer()" + export_types_scenario["player_x::get_headquarter_pos"] = "coord()" + export_types_scenario["player_x::get_name"] = "string()" + export_types_scenario["player_x::set_name"] = "void(string)" + export_types_scenario["player_x::get_construction"] = "array()" + export_types_scenario["player_x::get_vehicle_maint"] = "array()" + export_types_scenario["player_x::get_new_vehicles"] = "array()" + export_types_scenario["player_x::get_income"] = "array()" + export_types_scenario["player_x::get_maintenance"] = "array()" + export_types_scenario["player_x::get_assets"] = "array()" + export_types_scenario["player_x::get_cash"] = "array()" + export_types_scenario["player_x::get_net_wealth"] = "array()" + export_types_scenario["player_x::get_profit"] = "array()" + export_types_scenario["player_x::get_operating_profit"] = "array()" + export_types_scenario["player_x::get_margin"] = "array()" + export_types_scenario["player_x::get_transported"] = "array()" + export_types_scenario["player_x::get_powerline"] = "array()" + export_types_scenario["player_x::get_transported_pax"] = "array()" + export_types_scenario["player_x::get_transported_mail"] = "array()" + export_types_scenario["player_x::get_transported_goods"] = "array()" + export_types_scenario["player_x::get_convoys"] = "array()" + export_types_scenario["player_x::get_way_tolls"] = "array()" + export_types_scenario["player_x::book_cash"] = "void(integer)" + export_types_scenario["player_x::get_current_cash"] = "float()" + export_types_scenario["player_x::get_current_net_wealth"] = "integer()" + export_types_scenario["player_x::get_current_maintenance"] = "integer()" + export_types_scenario["player_x::create_line"] = "void(way_types)" + export_types_scenario["player_x::get_type"] = "integer()" + export_types_scenario["::load_language_file"] = "string(string)" + export_types_scenario["rules::forbid_tool"] = "void(integer, integer)" + export_types_scenario["rules::allow_tool"] = "void(integer, integer)" + export_types_scenario["rules::forbid_way_tool"] = "void(integer, integer, way_types)" + export_types_scenario["rules::allow_way_tool"] = "void(integer, integer, way_types)" + export_types_scenario["rules::forbid_way_tool_rect"] = "void(integer, integer, way_types, coord, coord, string)" + export_types_scenario["rules::allow_way_tool_rect"] = "void(integer, integer, way_types, coord, coord)" + export_types_scenario["rules::forbid_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d, string)" + export_types_scenario["rules::allow_way_tool_cube"] = "void(integer, integer, way_types, coord3d, coord3d)" + export_types_scenario["rules::clear"] = "void()" + export_types_scenario["rules::gui_needs_update"] = "void()" + export_types_scenario["debug::get_forbidden_text"] = "string()" + export_types_scenario["coord3d::get_halt"] = "halt_x(player_x)" + export_types_scenario["settings::get_industry_increase_every"] = "integer()" + export_types_scenario["settings::set_industry_increase_every"] = "void(integer)" + export_types_scenario["settings::get_traffic_level"] = "integer()" + export_types_scenario["settings::set_traffic_level"] = "void(integer)" + export_types_scenario["settings::get_start_time"] = "time_x()" + export_types_scenario["settings::get_station_coverage"] = "integer()" + export_types_scenario["settings::get_passenger_factor"] = "integer()" + export_types_scenario["settings::get_factory_worker_radius"] = "integer()" + export_types_scenario["settings::get_factory_worker_minimum_towns"] = "integer()" + export_types_scenario["settings::get_factory_worker_maximum_towns"] = "integer()" + export_types_scenario["settings::avoid_overcrowding"] = "bool()" + export_types_scenario["settings::no_routing_over_overcrowding"] = "bool()" + export_types_scenario["settings::separate_halt_capacities"] = "bool()" + export_types_scenario["settings::obsolete_vehicles_allowed"] = "bool()" + export_types_scenario["settings::get_max_road_convoi_length"] = "integer()" + export_types_scenario["settings::get_max_rail_convoi_length"] = "integer()" + export_types_scenario["settings::get_max_ship_convoi_length"] = "integer()" + export_types_scenario["settings::get_max_air_convoi_length"] = "integer()" + export_types_scenario["settings::get_drive_on_left"] = "bool()" + export_types_scenario["settings::get_pay_for_total_distance_mode"] = "integer()" + export_types_scenario["dir::is_single"] = "bool(dir)" + export_types_scenario["dir::is_twoway"] = "bool(dir)" + export_types_scenario["dir::is_threeway"] = "bool(dir)" + export_types_scenario["dir::is_curve"] = "bool(dir)" + export_types_scenario["dir::is_straight"] = "bool(dir)" + export_types_scenario["dir::double"] = "dir(dir)" + export_types_scenario["dir::backward"] = "dir(dir)" + export_types_scenario["dir::to_slope"] = "slope(dir)" + export_types_scenario["slope::to_dir"] = "dir(slope)" + export_types_scenario["::translate"] = "string(string)" + export_types_scenario["::double_to_string"] = "string(float, integer)" + export_types_scenario["::integer_to_string"] = "string(integer)" + export_types_scenario["::money_to_string"] = "string(integer)" + export_types_scenario["::coord_to_string"] = "string(coord)" + export_types_scenario["::coord3d_to_string"] = "string(coord3d)" + export_types_scenario["::get_month_name"] = "string(integer)" + export_types_scenario["::difftick_to_string"] = "string(integer)" + export_types_scenario["tile_x::is_valid"] = "bool()" + export_types_scenario["tile_x::find_object"] = "map_object_x(map_objects)" + export_types_scenario["tile_x::remove_object"] = "string(player_x, map_objects)" + export_types_scenario["tile_x::get_halt"] = "halt_x()" + export_types_scenario["tile_x::is_water"] = "bool()" + export_types_scenario["tile_x::is_bridge"] = "bool()" + export_types_scenario["tile_x::is_tunnel"] = "bool()" + export_types_scenario["tile_x::is_empty"] = "bool()" + export_types_scenario["tile_x::is_ground"] = "bool()" + export_types_scenario["tile_x::get_slope"] = "slope()" + export_types_scenario["tile_x::get_text"] = "string()" + export_types_scenario["tile_x::has_way"] = "bool(way_types)" + export_types_scenario["tile_x::has_ways"] = "bool()" + export_types_scenario["tile_x::has_two_ways"] = "bool()" + export_types_scenario["tile_x::get_way"] = "way_x(way_types)" + export_types_scenario["tile_x::get_neighbour"] = "tile_x(way_types, dir)" + export_types_scenario["tile_x::get_depot"] = "depot_x()" + export_types_scenario["tile_x::can_remove_all_objects"] = "string(player_x)" + export_types_scenario["tile_x::is_marked"] = "bool()" + export_types_scenario["tile_x::unmark"] = "void()" + export_types_scenario["tile_x::mark"] = "void()" + export_types_scenario["tile_x::get_convoys"] = "array()" + export_types_scenario["square_x::is_valid"] = "bool()" + export_types_scenario["square_x::get_halt"] = "halt_x()" + export_types_scenario["square_x::get_player_halt"] = "halt_x(player_x)" + export_types_scenario["square_x::get_tile_at_height"] = "tile_x(integer)" + export_types_scenario["square_x::get_ground_tile"] = "tile_x()" + export_types_scenario["square_x::get_halt_list"] = "array()" + export_types_scenario["square_x::get_climate"] = "climates()" + export_types_scenario["world::is_coord_valid"] = "bool(coord)" + export_types_scenario["world::find_nearest_city"] = "city_x(coord)" + export_types_scenario["world::get_season"] = "integer()" + export_types_scenario["world::remove_player"] = "bool(player_x)" + export_types_scenario["world::get_player"] = "player_x(integer)" + export_types_scenario["world::get_time"] = "time_ticks_x()" + export_types_scenario["world::get_citizens"] = "array()" + export_types_scenario["world::get_growth"] = "array()" + export_types_scenario["world::get_towns"] = "array()" + export_types_scenario["world::get_factories"] = "array()" + export_types_scenario["world::get_convoys"] = "array()" + export_types_scenario["world::get_citycars"] = "array()" + export_types_scenario["world::get_ratio_pax"] = "array()" + export_types_scenario["world::get_generated_pax"] = "array()" + export_types_scenario["world::get_ratio_mail"] = "array()" + export_types_scenario["world::get_generated_mail"] = "array()" + export_types_scenario["world::get_ratio_goods"] = "array()" + export_types_scenario["world::get_transported_goods"] = "array()" + export_types_scenario["world::get_year_citizens"] = "array()" + export_types_scenario["world::get_year_growth"] = "array()" + export_types_scenario["world::get_year_towns"] = "array()" + export_types_scenario["world::get_year_factories"] = "array()" + export_types_scenario["world::get_year_convoys"] = "array()" + export_types_scenario["world::get_year_citycars"] = "array()" + export_types_scenario["world::get_year_ratio_pax"] = "array()" + export_types_scenario["world::get_year_generated_pax"] = "array()" + export_types_scenario["world::get_year_ratio_mail"] = "array()" + export_types_scenario["world::get_year_generated_mail"] = "array()" + export_types_scenario["world::get_year_ratio_goods"] = "array()" + export_types_scenario["world::get_year_transported_goods"] = "array()" + export_types_scenario["world::use_timeline"] = "bool()" + export_types_scenario["attraction_list_x::_get"] = "building_x(integer)" + export_types_scenario["::get_pakset_name"] = "string()" + export_types_scenario["::get_version_number"] = "string()" + export_types_scenario["simple_heap_x::clear"] = "void()" + export_types_scenario["simple_heap_x::len"] = "integer()" + export_types_scenario["simple_heap_x::is_empty"] = "bool()" + export_types_scenario["simple_heap_x::insert"] = "void(integer, integer)" + export_types_scenario["way_planner_x::set_build_types"] = "void(way_desc_x)" + export_types_scenario["way_planner_x::is_allowed_step"] = "bool(tile_x, tile_x)" + export_types_scenario["bridge_planner_x::find_end"] = "coord3d(player_x, coord3d, dir, bridge_desc_x, integer)" + export_types_scenario["command_x::get_flags"] = "integer()" + export_types_scenario["command_x::set_flags"] = "void(integer)" + export_types_scenario["command_x::build_way"] = "string(player_x, coord3d, coord3d, way_desc_x, bool)" + export_types_scenario["command_x::build_road"] = "string(player_x, coord3d, coord3d, way_desc_x, bool, bool)" + export_types_scenario["command_x::build_depot"] = "string(player_x, coord3d, building_desc_x)" + export_types_scenario["command_x::build_station"] = "string(player_x, coord3d, building_desc_x, dir)" + export_types_scenario["command_x::build_bridge"] = "string(player_x, coord3d, coord3d, bridge_desc_x)" + export_types_scenario["command_x::build_bridge_at"] = "string(player_x, coord3d, bridge_desc_x)" + export_types_scenario["command_x::set_slope"] = "string(player_x, coord3d, slope)" + export_types_scenario["command_x::restore_slope"] = "string(player_x, coord3d)" + export_types_scenario["command_x::can_set_slope"] = "string(player_x, coord3d, slope)" + export_types_scenario["command_x::slope_get_price"] = "integer(slope)" + export_types_scenario["command_x::build_sign_at"] = "string(player_x, coord3d, sign_desc_x)" + export_types_scenario["command_x::build_wayobj"] = "string(player_x, coord3d, coord3d, wayobj_desc_x)" + export_types_scenario["command_x::change_climate_at"] = "string(player_x, coord3d, integer)" } diff --git a/script/api_class.cc b/script/api_class.cc index 06a0f83c503..5c3dd1b6713 100644 --- a/script/api_class.cc +++ b/script/api_class.cc @@ -4,6 +4,7 @@ */ #include "api_class.h" +#include "api_function.h" /** @@ -11,6 +12,7 @@ */ SQInteger script_api::create_class(HSQUIRRELVM vm, const char* classname, const char* baseclass) { + script_api::set_squirrel_type_class(classname); sq_pushroottable(vm); if (baseclass) { sq_pushstring(vm, baseclass, -1); @@ -34,6 +36,7 @@ SQInteger script_api::create_class(HSQUIRRELVM vm, const char* classname, const SQInteger script_api::begin_class(HSQUIRRELVM vm, const char* classname, const char* /* baseclasses - dummy */) { + script_api::set_squirrel_type_class(classname); if(!SQ_SUCCEEDED(push_class(vm, classname))) { // push a dummy class on the stack to prevent failed assertions down the road sq_newclass(vm, false); @@ -45,6 +48,7 @@ SQInteger script_api::begin_class(HSQUIRRELVM vm, const char* classname, const c void script_api::end_class(HSQUIRRELVM vm) { + script_api::set_squirrel_type_class(""); sq_pop(vm,1); } diff --git a/script/api_class.h b/script/api_class.h index 413c013846b..07c73c7facc 100644 --- a/script/api_class.h +++ b/script/api_class.h @@ -11,6 +11,7 @@ #include "../squirrel/squirrel.h" #include "../squirrel/sq_extensions.h" // sq_call_restricted +#include "../tpl/stringhashtable_tpl.h" #include @@ -76,74 +77,45 @@ namespace script_api { */ SQInteger prepare_constructor(HSQUIRRELVM vm, const char* classname); - /** - * Function to create & push instances of squirrel classes. - * @param classname name of squirrel class - */ - inline SQInteger push_instance(HSQUIRRELVM vm, const char* classname) - { - if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { - return -1; - } - bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, 1, true, false)); - sq_remove(vm, ok ? -2 : -1); // remove closure - return ok ? 1 : -1; - } - template - SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const A1 & a1) + template + SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const As &... as) { + // push constructor if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { return -1; } - param::push(vm, a1); - bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, 2, true, false)); - sq_remove(vm, ok ? -2 : -1); // remove closure - return ok ? 1 : -1; - } - - template - SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const A1 & a1, const A2 & a2) - { - if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { + // push parameters + int nparam = push_param(vm, as...); + if (!SQ_SUCCEEDED(nparam)) { return -1; } - param::push(vm, a1); - param::push(vm, a2); - bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, 3, true, false)); - sq_remove(vm, ok ? -2 : -1); // remove closure + // call constructor + bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, nparam+1, true, false)); + sq_remove(vm, ok ? -2 : -1); /* remove closure */ return ok ? 1 : -1; } - template - SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const A1 & a1, const A2 & a2, const A3 & a3) - { - if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { - return -1; - } - param::push(vm, a1); - param::push(vm, a2); - param::push(vm, a3); - bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, 4, true, false)); - sq_remove(vm, ok ? -2 : -1); // remove closure - return ok ? 1 : -1; - } - template - SQInteger push_instance(HSQUIRRELVM vm, const char* classname, const A1 & a1, const A2 & a2, const A3 & a3, const A4 & a4) + /** + * Create instance, set userpointer. + * Does NOT call constructor. + */ + template + inline SQInteger push_instance_up(HSQUIRRELVM vm, const C* ptr) { - if (!SQ_SUCCEEDED(prepare_constructor(vm, classname)) ) { + if (!SQ_SUCCEEDED(push_class(vm, param::squirrel_type()) )) { return -1; } - param::push(vm, a1); - param::push(vm, a2); - param::push(vm, a3); - param::push(vm, a4); - bool ok = SQ_SUCCEEDED(sq_call_restricted(vm, 5, true, false)); - sq_remove(vm, ok ? -2 : -1); // remove closure - return ok ? 1 : -1; + sq_createinstance(vm, -1); + sq_setinstanceup(vm, -1, (void*)const_cast(ptr)); + sq_remove(vm, -2); // remove class + return 1; } + /** + * Implementation of quickstone_tpl specialization + */ template struct param< quickstone_tpl > { /** * Assumes that constructor of corresponding squirrel class @@ -182,5 +154,71 @@ namespace script_api { return param::typemask(); } }; + + /** + * Implementation of stringhashtable_tpl specialization + */ + template struct param< stringhashtable_tpl > { + /** + * Creates table, uses string-keys as table keys. + */ + static SQInteger push(HSQUIRRELVM vm, stringhashtable_tpl const& v) + { + sq_newtable(vm); + + for(auto const&i : v) { + create_slot(vm, i.key, i.value); + } + return 1; + } + /// squirrel_type corresponding to the c++ type/class + static const char* squirrel_type() + { + static cbuffer_t buf; + buf.clear(); + buf.printf("table<%s>", param::squirrel_type() ); + return buf; + } + }; + + // forward declaration + template SQInteger command_release_hook(SQUserPointer up, SQInteger); + /** + * Stores pointer to C++ object as userpointer of squirrel class instance. + * C++ object will be deleted when squirrel class instance gets released. + */ + template + void attach_instance(HSQUIRRELVM vm, SQInteger index, C* o) + { + // set userpointer of class instance + sq_setinstanceup(vm, index, o ); + sq_setreleasehook(vm, index, command_release_hook); + } + + /** + * Retrieves pointer to stored C++ object. + */ + template + C* get_attached_instance(HSQUIRRELVM vm, SQInteger index, SQUserPointer tag) + { + SQUserPointer up; + if (SQ_SUCCEEDED(sq_getinstanceup(vm, index, &up, tag))) { + return (C*)up; + } + return NULL; + } + + /** + * Releases memory allocated by attach_instance. + * Do not call directly! + */ + template + SQInteger command_release_hook(SQUserPointer up, SQInteger) + { + C* p = (C *)up; + delete p; + return 1; + } + }; // end of namespace #endif diff --git a/script/api_function.cc b/script/api_function.cc index 028ab97f7d2..a53dff646cb 100644 --- a/script/api_function.cc +++ b/script/api_function.cc @@ -23,14 +23,22 @@ void script_api::register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const cha } static FILE* file = NULL; +static const char* suffix = NULL; -void script_api::start_squirrel_type_logging() +void script_api::start_squirrel_type_logging(const char* s) { if (env_t::verbose_debug < 2) { return; } - file = dr_fopen("squirrel_types.awk", "w"); + suffix = s; + cbuffer_t buf; + buf.printf("squirrel_types_%s.awk", suffix); + file = dr_fopen(buf, "w"); if (file) { + fprintf(file, "#\n"); + fprintf(file, "# This file is part of the Simutrans-Extended project under the Artistic License.\n"); + fprintf(file, "# (see LICENSE.txt)\n"); + fprintf(file, "#\n"); fprintf(file, "# file used to generate doxygen documentation of squirrel API\n"); fprintf(file, "# needs to be copied to trunk/script/api\n"); fprintf(file, "BEGIN {\n"); @@ -43,14 +51,23 @@ void script_api::end_squirrel_type_logging() fprintf(file, "}\n"); fclose(file); file = NULL; + suffix = NULL; } } +static plainstring current_class; + +void script_api::set_squirrel_type_class(const char* classname) +{ + current_class = classname; +} + void script_api::log_squirrel_type(std::string classname, const char* name, std::string squirrel_type) { if (file) { - fprintf(file, "\texport_types[\"%s::%s\"] = \"%s\"\n", - classname.compare(param::squirrel_type()) == 0 ? "" : classname.c_str(), + fprintf(file, "\texport_types_%s[\"%s::%s\"] = \"%s\"\n", + suffix ? suffix : "", + classname.empty() ? current_class.c_str() : classname.c_str(), name, squirrel_type.c_str() ); diff --git a/script/api_function.h b/script/api_function.h index b80eb73ac12..141822b1319 100644 --- a/script/api_function.h +++ b/script/api_function.h @@ -11,6 +11,8 @@ #include "../squirrel/squirrel.h" #include #include +#include +#include // mark class members static in doxygen documentation of api #define STATIC @@ -19,6 +21,23 @@ namespace script_api { + // forward declarations + template SQInteger generic_squirrel_callback(HSQUIRRELVM vm); + template struct embed_call_t; + template struct func_signature_t; + + /// @{ + /// @name Function to log squirrel-side type of exported functions + void start_squirrel_type_logging(const char* suffix); + + void end_squirrel_type_logging(); + + // sets current class name, does not work with nested classes + void set_squirrel_type_class(const char* classname); + + void log_squirrel_type(std::string classname, const char* name, std::string squirrel_type); + /// @} + /** * Registers custom SQFUNCTION @p funcptr. * @@ -33,6 +52,28 @@ namespace script_api { */ void register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, int nparamcheck, const char* typemask, bool staticmethod = false); + /** + * Registers custom SQFUNCTION @p funcptr. + * + * Same as non-templated register_function. Typemask und paramcheck will be deduced from the template parameter. + * + * @tparam F function pointer signature of c++ method + * @param funcptr function pointer to custom implementation + * @param name name of the method as visible from squirrel + * @param staticmethod if true then register as static method + */ + template + void register_function(HSQUIRRELVM vm, SQFUNCTION funcptr, const char *name, bool staticmethod = false) + { + std::string typemask = func_signature_t::get_typemask(false); + int nparamcheck = func_signature_t::get_nparams(); + + register_function(vm, funcptr, name, nparamcheck, typemask.c_str(), staticmethod); + + log_squirrel_type(func_signature_t::get_squirrel_class(false), name, func_signature_t::get_squirrel_type(false, 0)); + } + + /** * Registers custom SQFUNCTION @p funcptr with templated free variables (default parameters). * @@ -57,26 +98,14 @@ namespace script_api { sq_setparamscheck(vm, nparamcheck, typemask); sq_newslot(vm, -3, staticmethod); } - - // forward declarations - template SQInteger generic_squirrel_callback(HSQUIRRELVM vm); - template struct embed_call_t; - template struct func_signature_t; - // auxiliary struct template struct function_info_t { F funcptr; // pointer to c++ function - bool discard_first; - function_info_t(F f, bool d) : funcptr(f), discard_first(d) {} + bool act_as_member; + function_info_t(F f, bool d) : funcptr(f), act_as_member(d) {} }; - void start_squirrel_type_logging(); - - void end_squirrel_type_logging(); - - void log_squirrel_type(std::string classname, const char* name, std::string squirrel_type); - /** * Registers native c++ method to be called from squirrel. * Squirrel calls generic_squirrel_callback, the pointer to the actual @@ -88,30 +117,28 @@ namespace script_api { * @tparam F function pointer signature of c++ method * @param funcptr pointer to the c++ method * @param name name of the method as visible from squirrel - * @param discard_first if true then a global (non-member) function can be called + * @param act_as_member if true then a global (non-member) function can be called * as if it would be a member function of the class instance * provided as first argument * @param staticmethod if true then register as static method */ template - void register_method(HSQUIRRELVM vm, F funcptr, const char* name, bool discard_first = false, bool staticmethod = false) + void register_method(HSQUIRRELVM vm, F funcptr, const char* name, bool act_as_member = false, bool staticmethod = false) { sq_pushstring(vm, name, -1); // pointer to function info as free variable - function_info_t fi(funcptr, discard_first); + function_info_t fi(funcptr, act_as_member); SQUserPointer up = sq_newuserdata(vm, sizeof(function_info_t)); memcpy(up, &fi, sizeof(function_info_t)); // create function sq_newclosure(vm, generic_squirrel_callback, 1); // ..associate its name for debugging sq_setnativeclosurename(vm, -1, name); - std::string typemask = func_signature_t::get_typemask(discard_first); - sq_setparamscheck(vm, func_signature_t::get_nparams() - discard_first, typemask.c_str()); + std::string typemask = func_signature_t::get_typemask(act_as_member); + sq_setparamscheck(vm, func_signature_t::get_nparams() - act_as_member, typemask.c_str()); sq_newslot(vm, -3, staticmethod); - log_squirrel_type(func_signature_t::get_squirrel_class(discard_first), name, func_signature_t::get_squirrel_type(discard_first, 0)); - //printf("CHECKTPM %d %s::%s: %s vs %s = %s\n", discard_first, func_signature_t::get_squirrel_class(discard_first).c_str(), name, func_signature_t::get_typemask(discard_first).c_str(), embed_call_t::get_typemask(discard_first).c_str(), - // func_signature_t::get_squirrel_type(discard_first, 0).c_str()); + log_squirrel_type(func_signature_t::get_squirrel_class(act_as_member), name, func_signature_t::get_squirrel_type(act_as_member, 0)); } @@ -127,18 +154,18 @@ namespace script_api { * @tparam V class to push default parameters as free variables * @param funcptr pointer to the c++ method * @param name name of the method as visible from squirrel - * @param freevariables values of default parameters - * @param discard_first if true then a global (non-member) function can be called + * @param freevariables values of default parameters to the c++ function (not the squirrel function) + * @param act_as_member if true then a global (non-member) function can be called * as if it would be a member function of the class instance * provided as first argument * @param staticmethod if true then register as static method */ template - void register_method_fv(HSQUIRRELVM vm, F funcptr, const char* name, V const& freevariables, bool discard_first = false, bool staticmethod = false) + void register_method_fv(HSQUIRRELVM vm, F funcptr, const char* name, V const& freevariables, bool act_as_member = false, bool staticmethod = false) { sq_pushstring(vm, name, -1); // pointer to function info as free variable - function_info_t fi(funcptr, discard_first); + function_info_t fi(funcptr, act_as_member); SQUserPointer up = sq_newuserdata(vm, sizeof(function_info_t)); memcpy(up, &fi, sizeof(function_info_t)); // more free variables @@ -147,14 +174,11 @@ namespace script_api { sq_newclosure(vm, generic_squirrel_callback, count+1); // ..associate its name for debugging sq_setnativeclosurename(vm, -1, name); - std::string typemask = func_signature_t::get_typemask(discard_first); - sq_setparamscheck(vm, func_signature_t::get_nparams() - discard_first - count, typemask.c_str()); + std::string typemask = func_signature_t::get_typemask(act_as_member); + sq_setparamscheck(vm, func_signature_t::get_nparams() - act_as_member - count, typemask.c_str()); sq_newslot(vm, -3, staticmethod); - log_squirrel_type(func_signature_t::get_squirrel_class(discard_first), name, func_signature_t::get_squirrel_type(discard_first, count)); - - //printf("CHECKTPM %d %ld %s::%s: %s vs %s = %s\n", discard_first, count, func_signature_t::get_squirrel_class(discard_first).c_str(), name, func_signature_t::get_typemask(discard_first).c_str(), embed_call_t::get_typemask(discard_first).c_str(), - // func_signature_t::get_squirrel_type(discard_first, count).c_str()); + log_squirrel_type(func_signature_t::get_squirrel_class(act_as_member), name, func_signature_t::get_squirrel_type(act_as_member, count)); } @@ -193,527 +217,284 @@ namespace script_api { memcpy(&fi, up, sizeof(function_info_t)); // call the template that automatically fetches right number of parameters - return embed_call_t::call_function(vm, fi.funcptr, fi.discard_first); + return embed_call_t::call_function(vm, fi.funcptr, fi.act_as_member); } /** - * Template to count the first parameter - * @tparam F count is one, except if F is script_api::void_t then it is zero + * Template to split parameter lists */ - template struct count_first_param_t { - static const uint16 count = 1; - }; - template<> struct count_first_param_t { - static const uint16 count = 0; + template struct parameter_pack_t + { + using first = A1; + using back = void (*)(As...); }; - /** - * Class to check first parameters to be not NULL. - * Actually do the check only for pointer types. + * Template to generate strings related to argument lists */ - template struct param_chk_t { - static bool is_null(T) { return false; } + template struct parameter_list_t; + + template + struct parameter_list_t< void (*)(As...) > + { + /// @returns get typemask of first @p nparams parameters only + static std::string get_param_typemask() + { + std::string res = param::first>::typemask() + + parameter_list_t::back >::get_param_typemask(); + return res; + } + /// @returns type list of first @p nparams arguments (without parentheses) + static std::string get_param_squirrel_type(int nparams) + { + if (nparams > 0) { + std::string res = param::first>::squirrel_type(); + std::string add = parameter_list_t< typename parameter_pack_t::back >::get_param_squirrel_type(nparams-1); + return res + (add.empty() ? "" : ", " + add); + } + return ""; + } }; - template struct param_chk_t { - static bool is_null(T* t) { return t == NULL; } + // specialization for empty parameter list + template<> + struct parameter_list_t< void (*)() > + { + static std::string get_param_typemask() { return ""; } + static std::string get_param_squirrel_type(int) { return ""; } }; - /** * Templates to generate squirrel typemasks, * wrapped in a one-parameter template struct, * which is specialized per function-signature. */ - template struct func_signature_t + template struct func_signature_t; + + /** + * Non-member functions. + * May act as member functions if @p act_as_member is true. + */ + template + struct func_signature_t { /// @returns typemask taking into account class argument is taken from 0th or 1st parameter - static std::string get_typemask(bool discard_first) + static std::string get_typemask(bool act_as_member) { - return (discard_first ? "" : param::sig_class>::typemask()) - + get_param_typemask(get_nparams()); + return (act_as_member ? "" : "." ) + parameter_list_t::get_param_typemask(); } - - /// @returns typemask of parameters only - static std::string get_param_typemask(uint16 nparams) - { - if (nparams > 0) { - if (count_first_param_t::sig_first>::count > 0) { - // typemask of first parameter if there is any - return param::sig_first>::typemask() + - func_signature_t::sig_reduced>::get_param_typemask(nparams-1); - } - else { - return func_signature_t::sig_reduced>::get_param_typemask(nparams-1); - } - } - return ""; - } - /// @returns number of parameters - static uint16 get_nparams() + static int get_nparams() { - // number of parameters of reduced signature - return func_signature_t::sig_reduced>::get_nparams() + - // plus first parameter if there is any - count_first_param_t::sig_first>::count; + return sizeof...(As) + 1; } - - /// @returns squirrel class name of class argument of F ("void", "" are allowed) - static std::string get_squirrel_class(bool discard_first) + /// @returns squirrel class name of class argument of F ("" if not a member function) + static std::string get_squirrel_class(bool act_as_member) { - if (discard_first) { - return param::sig_first>::squirrel_type(); - } - else { - return param::sig_class>::squirrel_type(); + if (act_as_member) { + // type of first parameter - if there is none then "" + return parameter_list_t::get_param_squirrel_type(1); } + return ""; } /// @returns function signature if interpreted as squirrel function - static std::string get_squirrel_type(bool discard_first, int freevars) + static std::string get_squirrel_type(bool act_as_member, int freevars) { - // return type - std::string r = param::sig_return>::squirrel_type(); - // parameters - uint16 nparams = get_nparams(); - // if first parameter is interpreted as class ignore 2 parameters: environment, and first parameter - if (nparams > freevars + 1 + discard_first) { - // there are non-default params - if (discard_first) { - // first parameter is interpreted as class and thus ignored here - return r + "("+ func_signature_t::sig_reduced>::get_param_squirrel_type(nparams - freevars - 2) + ")"; + std::string res = get_return_squirrel_type() + "("; + // remaining number of parameters (including class name if act_as_member == true) + int nparams = get_nparams() - freevars - 1; + + std::string params = parameter_list_t< void (*)(As...) >::get_param_squirrel_type(nparams); + + if (act_as_member) { + // ignore class parameter + size_t n = params.find(", "); + if (n == std::string::npos || n+2 > params.size()) { + // not found + params = ""; } else { - return r + "("+ get_param_squirrel_type(nparams - freevars - 1) + ")"; + // drop first parameter + params = params.substr(n+2); } } - return r + "()"; - } - /// @returns type list of arguments (without parentheses and return type) - static std::string get_param_squirrel_type(uint16 nparams) - { - if (nparams > 1) { - return std::string(param::sig_first>::squirrel_type()) + ", " - + func_signature_t::sig_reduced>::get_param_squirrel_type(nparams-1); - } - else if (nparams > 0) { - return param::sig_first>::squirrel_type(); - } - return ""; - } - }; - template<> - struct func_signature_t { - static uint16 get_nparams() { - // first parameter is environment / root table - return 1; - } - static std::string get_typemask(bool discard_first) - { - // first parameter is environment / root table if discard_first==false - // otherwise first parameter is ignored - return (discard_first ? "" : "."); - } - static std::string get_param_typemask(uint16) - { - return ""; + + return res += params + ")"; } - static std::string get_param_squirrel_type(uint16) + /// @return squirrel type of return value + static std::string get_return_squirrel_type() { - return ""; + return param::squirrel_type(); // will resolve R = void } }; /** - * Templates to call functions with automatically fetching the right parameters, - * wrapped in a one-parameter template struct, - * which is specialized per function-signature. + * Member functions */ - template struct embed_call_t; + template + struct func_signature_t + { + // reduce it to the case of an non-member function with act_as_member == true + using function = R (*)(C*,As...); - // 0 parameters - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (*func)(), bool) + static int get_nparams() { - return param::push(vm, (*func)() ); + return func_signature_t::get_nparams() - 1; } - - typedef R sig_return; // return type - typedef void_t sig_class; // type of class - typedef void_t sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter - }; - - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)(), bool) + static std::string get_typemask(bool) { - if (C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)() ); - } - else { - return -1; - } + return func_signature_t::get_typemask(true); } - - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef void_t sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter - }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)() const, bool) + static std::string get_squirrel_class(bool) { - if (const C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)() ); - } - else { - return -1; - } + return param::squirrel_type(); } - - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef void_t sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter - }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(), bool) + static std::string get_squirrel_type(bool, int freevars) { - if (C* instance = param::get(vm, 1)) { - (instance->*func)(); - return 0; - } - else { - return -1; - } + return func_signature_t::get_squirrel_type(true, freevars); } - - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef void_t sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter }; - // 1 parameter - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (*func)(A1), bool discard_first) - { - A1 a1 = param::get(vm, 2-discard_first); - if (discard_first && param_chk_t::is_null(a1)) { - return -1; - } - else { - return param::push(vm, (*func)(a1) ); - } - } - - typedef R sig_return; // return type - typedef script_api::void_t sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter + template + struct func_signature_t : public func_signature_t + { }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1), bool) - { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( param::get(vm, 2) ); - return 0; - } - else { - return -1; - } - } - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter + /** + * Class to check first parameters to be not NULL. + * Actually do the check only for pointer types. + */ + template struct param_chk_t { + static bool is_null(T) { return false; } }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)(A1), bool) - { - if (C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)( param::get(vm, 2) ) ); - } - else { - return -1; - } - } - - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter + template struct param_chk_t { + static bool is_null(T* t) { return t == NULL; } }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)(A1) const, bool) - { - if (const C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)( param::get(vm, 2) ) ); - } - else { - return -1; - } - } - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(); // signature of function with without return type, class, and last parameter - }; - // 2 parameters - template - struct embed_call_t { - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2); // signature of function with without return type, class, and last parameter - }; + /** + * Classes to implement the actual calls. + * Calls to function pointers and member functions are turned into std::function calls. + */ + template class call_std_function_t; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (*func)(A1, A2), bool discard_first) + // specialization for functions with at least one argument: we might have to check for null-pointers + template + class call_std_function_t< R(A1,As...) > + { + template + static SQInteger call_function_helper(HSQUIRRELVM vm, std::function const& func, bool act_as_member, std::index_sequence) { - A1 a1 = param::get(vm, 2-discard_first); - if (discard_first && param_chk_t::is_null(a1)) { + A1 a1 = param::get(vm, 2-act_as_member); + if (act_as_member && param_chk_t::is_null(a1)) { return -1; } - return param::push(vm, (*func)(a1, - param::get(vm, 3-discard_first) - ) ); + // non-void return value + return param::push(vm, func(a1, param::get(vm, is+2 /* index on stack */ +1 /* correct for A1 */ -act_as_member)...) ); + // index on stack: at +1 `this` argument, then parameters starting from +2 + // if act_as_member then `this` is first argument to function, results in offset -1 } + public: - typedef R sig_return; // return type - typedef script_api::void_t sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2); // signature of function with without return type, class, and last parameter - }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1, A2), bool) + static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool act_as_member) { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( - param::get(vm, 2), - param::get(vm, 3) - ); - return 0; - } - else { - return -1; - } + return call_function_helper(vm, func, act_as_member, std::index_sequence_for{}); } - - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2); // signature of function with without return type, class, and last parameter }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)(A1, A2), bool) + + // specialization for void functions with at least one argument: we might have to check for null-pointers + template + class call_std_function_t< void(A1,As...) > + { + template + static SQInteger call_function_helper(HSQUIRRELVM vm, std::function const& func, bool act_as_member, std::index_sequence) { - if (C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)( - param::get(vm, 2), - param::get(vm, 3) - ) ); - } - else { + A1 a1 = param::get(vm, 2-act_as_member); + if (act_as_member && param_chk_t::is_null(a1)) { return -1; } + // void function + func(a1, param::get(vm, is+2 /* index on stack */ +1 /* correct for A1 */ -act_as_member)...); + return 0; } + public: - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2); // signature of function with without return type, class, and last parameter - }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (C::*func)(A1, A2) const, bool) + static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool act_as_member) { - if (const C* instance = param::get(vm, 1)) { - return param::push(vm, (instance->*func)( - param::get(vm, 2), - param::get(vm, 3) - ) ); - } - else { - return -1; - } + return call_function_helper(vm, func, act_as_member, std::index_sequence_for{}); } - - typedef R sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2); // signature of function with without return type, class, and last parameter - }; - - // 3 parameters - template - struct embed_call_t { - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3); // signature of function with without return type, class, and last parameter }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (*func)(A1, A2, A3), bool discard_first) + // functions with no parameters + template + class call_std_function_t< R() > + { + public: + static SQInteger call_function(HSQUIRRELVM vm, std::function const& func, bool) { - A1 a1 = param::get(vm, 2-discard_first); - if (discard_first && param_chk_t::is_null(a1)) { - return -1; - } - return param::push(vm, (*func)(a1, - param::get(vm, 3-discard_first), - param::get(vm, 4-discard_first) - ) ); + return param::push(vm, func()); } - - typedef R sig_return; // return type - typedef script_api::void_t sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3); // signature of function with without return type, class, and last parameter }; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1, A2, A3), bool) + template<> + class call_std_function_t< void() > + { + public: + static SQInteger call_function(HSQUIRRELVM, std::function const& func, bool) { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( - param::get(vm, 2), - param::get(vm, 3), - param::get(vm, 4) - ); - return 0; - } - else { - return -1; - } + func(); + return 0; } - - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3); // signature of function with without return type, class, and last parameter - }; - - // 4 parameters - template - struct embed_call_t { - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4); // signature of function with without return type, class, and last parameter }; + /** + * Templates to call functions with automatically fetching the right parameters, + * wrapped in a one-parameter template struct, + * which is specialized per function-signature. + */ + template struct embed_call_t; - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, R (*func)(A1, A2, A3, A4), bool discard_first) + // Non-member functions + template + struct embed_call_t + { + static SQInteger call_function(HSQUIRRELVM vm, R (*func_ptr)(As...), bool act_as_member) { - A1 a1 = param::get(vm, 2-discard_first); - if (discard_first && param_chk_t::is_null(a1)) { - return -1; - } - return param::push(vm, (*func)(a1, - param::get(vm, 3-discard_first), - param::get(vm, 4-discard_first), - param::get(vm, 5-discard_first) - ) ); + std::function< R(As...)> func = func_ptr; + return call_std_function_t< R(As...) >::call_function(vm, func, act_as_member); } - - typedef R sig_return; // return type - typedef script_api::void_t sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4); // signature of function with without return type, class, and last parameter }; - - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1, A2, A3, A4), bool) + // Non-const member functions + template + struct embed_call_t + { + static SQInteger call_function(HSQUIRRELVM vm, R (C::*member_func_ptr)(As...), bool) { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( - param::get(vm, 2), - param::get(vm, 3), - param::get(vm, 4), - param::get(vm, 5) - ); - return 0; - } - else { - return -1; - } - } - - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4); // signature of function with without return type, class, and last parameter - }; + std::function< R(C*,As...)> func = member_func_ptr; - // 5 parameters - template - struct embed_call_t { - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4,A5); // signature of function with without return type, class, and last parameter - }; - - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1, A2, A3, A4, A5), bool) - { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( - param::get(vm, 2), - param::get(vm, 3), - param::get(vm, 4), - param::get(vm, 5), - param::get(vm, 6) - ); - return 0; - } - else { - return -1; - } + return call_std_function_t< R(C*,As...) >::call_function(vm, func, true); } - - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4,A5); // signature of function with without return type, class, and last parameter }; - - // 6 parameters - template - struct embed_call_t { - static SQInteger call_function(HSQUIRRELVM vm, void (C::*func)(A1, A2, A3, A4, A5, A6), bool) + // Const member functions + template + struct embed_call_t + { + static SQInteger call_function(HSQUIRRELVM vm, R (C::*member_func_ptr)(As...) const, bool) { - if (C* instance = param::get(vm, 1)) { - (instance->*func)( - param::get(vm, 2), - param::get(vm, 3), - param::get(vm, 4), - param::get(vm, 5), - param::get(vm, 6), - param::get(vm, 7) - ); - return 0; - } - else { - return -1; - } - } + std::function< R(const C*,As...)> func = member_func_ptr; - typedef script_api::void_t sig_return; // return type - typedef C* sig_class; // type of class - typedef A1 sig_first; // type of first parameter - typedef void(*sig_reduced)(A2,A3,A4,A5,A6); // signature of function with without return type, class, and last parameter + return call_std_function_t< R(const C*,As...) >::call_function(vm, func, true); + } }; + /** + * Exports function to check whether pointer to in-game object is not null + */ + template SQInteger is_ptr_valid(HSQUIRRELVM vm) + { + P ptr = param

::get(vm, 1); + sq_pushbool(vm, ptr != NULL); + return 1; + } + template void export_is_valid(HSQUIRRELVM vm) + { + register_function(vm, is_ptr_valid

, "is_valid", 1, param

::typemask()); + log_squirrel_type(param

::squirrel_type(), "is_valid", "bool()"); + } }; // end of namespace #endif diff --git a/script/api_param.cc b/script/api_param.cc index 236c9014c38..fc66973dcdb 100644 --- a/script/api_param.cc +++ b/script/api_param.cc @@ -10,23 +10,96 @@ #include "../simfab.h" #include "../bauer/goods_manager.h" #include "../dataobj/schedule.h" +#include "../dataobj/loadsave.h" #include "../dataobj/scenario.h" #include "../player/simplay.h" #include "../utils/plainstring.h" +#include "api/api_command.h" // script_api::my_tool_t +#include "api/api_simple.h" // my_ribi_t namespace script_api { karte_ptr_t welt; - SQInteger param::push(HSQUIRRELVM, script_api::void_t const&) +// rotation handling + void rotate90() { coordinate_transform_t::rotate90(); } + void new_world() { coordinate_transform_t::new_world(); } + + uint8 coordinate_transform_t::rotation = 4; + + void coordinate_transform_t::initialize() + { + if (rotation == 4) { + rotation = welt->get_settings().get_rotation(); + } + } + + void coordinate_transform_t::rdwr(loadsave_t *file) + { + file->rdwr_byte(rotation); + } + + void coordinate_transform_t::koord_w2sq(koord &k) + { + // do not transform koord::invalid + if (k.x == -1 && k.y == -1) { + return; + } + switch( rotation ) { + // 0: do nothing + case 1: k = koord(k.y, welt->get_size().x-1 - k.x); break; + case 2: k = koord(welt->get_size().x-1 - k.x, welt->get_size().y-1 - k.y); break; + case 3: k = koord(welt->get_size().y-1 - k.y, k.x); break; + default: break; + } + } + + void coordinate_transform_t::koord_sq2w(koord &k) { - return 0; + // do not transform koord::invalid + if (k.x == -1 && k.y == -1) { + return; + } + switch( rotation ) { + // 0: do nothing + case 1: k = koord(welt->get_size().x-1 - k.y, k.x); break; + case 2: k = koord(welt->get_size().x-1 - k.x, welt->get_size().y-1 - k.y); break; + case 3: k = koord(k.y, welt->get_size().y-1 - k.x); break; + default: break; + } + } + + void coordinate_transform_t::ribi_w2sq(ribi_t::ribi &r) + { + if (rotation) { + r = ( ( (r << 4) | r) >> rotation) & 15; + } + } + + void coordinate_transform_t::ribi_sq2w(ribi_t::ribi &r) + { + if (rotation) { + r = ( ( (r << 4) | r) << rotation) >> 4 & 15; + } } - script_api::void_t param::get(HSQUIRRELVM, SQInteger) + void coordinate_transform_t::slope_w2sq(slope_t::type &s) { - return script_api::void_t(); + if (s < slope_t::max_number) { + for(uint8 i=1; i <= 4-rotation; i++) { + s = slope_t::rotate90(s); + } + } + } + + void coordinate_transform_t::slope_sq2w(slope_t::type &s) + { + if (s < slope_t::max_number) { + for(uint8 i=1; i <= rotation; i++) { + s = slope_t::rotate90(s); + } + } } // integer arguments @@ -126,25 +199,6 @@ namespace script_api { return 1; } - - waytype_t param::get(HSQUIRRELVM vm, SQInteger index) - { - return (waytype_t)(param::get(vm, index)); - } - SQInteger param::push(HSQUIRRELVM vm, waytype_t const& v) - { - return param::push(vm, v); - } - - - obj_t::typ param::get(HSQUIRRELVM vm, SQInteger index) - { - return (obj_t::typ)(param::get(vm, index)); - } - SQInteger param::push(HSQUIRRELVM vm, obj_t::typ const& v) - { - return param::push(vm, v); - } // floats double param::get(HSQUIRRELVM vm, SQInteger index) { @@ -227,7 +281,7 @@ namespace script_api { koord k(x,y); if (k.x != -1 && k.y != -1) { // transform coordinates - welt->get_scenario()->koord_sq2w(k); + coordinate_transform_t::koord_sq2w(k); } else { k = koord::invalid; @@ -235,23 +289,17 @@ namespace script_api { return k; } - - SQInteger param::push(HSQUIRRELVM vm, koord const& v) { koord k(v); if (k.x != -1 && k.y != -1) { // transform coordinates - welt->get_scenario()->koord_w2sq(k); + coordinate_transform_t::koord_w2sq(k); } else { k = koord::invalid; } - - sq_newtable(vm); - create_slot(vm, "x", k.x); - create_slot(vm, "y", k.y); - return 1; + return push_instance(vm, "coord", k.x, k.y); } koord3d param::get(HSQUIRRELVM vm, SQInteger index) @@ -267,9 +315,43 @@ namespace script_api { SQInteger param::push(HSQUIRRELVM vm, koord3d const& v) { - param::push(vm, v.get_2d()); - create_slot(vm, "z", v.z); - return 1; + koord k(v.get_2d()); + if (k.x != -1 && k.y != -1) { + // transform coordinates + coordinate_transform_t::koord_w2sq(k); + } + else { + k = koord::invalid; + } + return push_instance(vm, "coord3d", k.x, k.y, v.z); + } +// directions / ribis + SQInteger param::push(HSQUIRRELVM vm, my_ribi_t const& v) + { + ribi_t::ribi ribi = v; + coordinate_transform_t::ribi_w2sq(ribi); + return param::push(vm, ribi); + } + + my_ribi_t param::get(HSQUIRRELVM vm, SQInteger index) + { + ribi_t::ribi ribi = param::get(vm, index) & ribi_t::all; + coordinate_transform_t::ribi_sq2w(ribi); + return ribi; + } +// slopes + SQInteger param::push(HSQUIRRELVM vm, my_slope_t const& v) + { + slope_t::type slope = v; + coordinate_transform_t::slope_w2sq(slope); + return param::push(vm, slope); + } + + my_slope_t param::get(HSQUIRRELVM vm, SQInteger index) + { + slope_t::type slope = param::get(vm, index); + coordinate_transform_t::slope_sq2w(slope); + return slope; } // pointers to classes @@ -293,16 +375,25 @@ namespace script_api { return fab; } - SQInteger param::push(HSQUIRRELVM vm, fabrik_t* const& fab) + const fabrik_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + return param::get(vm, index); + } + + SQInteger param::push(HSQUIRRELVM vm, const fabrik_t* const& fab) { if (fab == NULL) { sq_pushnull(vm); return 1; } koord pos(fab->get_pos().get_2d()); - welt->get_scenario()->koord_w2sq(pos); + coordinate_transform_t::koord_w2sq(pos); return push_instance(vm, "factory_x", pos.x, pos.y); } + SQInteger param::push(HSQUIRRELVM vm, fabrik_t* const& fab) + { + return param::push(vm, fab); + } const ware_production_t* param::get(HSQUIRRELVM vm, SQInteger index) { @@ -329,11 +420,56 @@ namespace script_api { return NULL; } + const factory_supplier_desc_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + fabrik_t* fab = param::get(vm, index); + if (fab == NULL) { + return NULL; + } + // obtain index into wareproduction_t arrays + SQInteger i = -1; + if (SQ_SUCCEEDED(get_slot(vm, "index", i, index))) { + if (i>=0 && (uint32)iget_input().get_count()) { + const ware_production_t& in = fab->get_input()[i]; + const factory_supplier_desc_t* desc = fab->get_desc()->get_supplier(i); + // sanity check + if (desc && desc->get_input_type() == in.get_typ()) { + return desc; + } + } + } + sq_raise_error(vm, "No input slot [%d] in factory at (%s)", i, fab->get_pos().get_str()); + return NULL; + } + + const factory_product_desc_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + fabrik_t* fab = param::get(vm, index); + if (fab == NULL) { + return NULL; + } + // obtain index into wareproduction_t arrays + SQInteger i = -1; + if (SQ_SUCCEEDED(get_slot(vm, "index", i, index))) { + i -= fab->get_input().get_count(); + if (i>=0 && (uint32)iget_output().get_count()) { + const ware_production_t& out = fab->get_output()[i]; + const factory_product_desc_t* desc = fab->get_desc()->get_product(i); + // sanity check + if (desc && desc->get_output_type() == out.get_typ()) { + return desc; + } + } + } + sq_raise_error(vm, "No output slot [%d] in factory at (%s)", i, fab->get_pos().get_str()); + return NULL; + } + player_t* param::get(HSQUIRRELVM vm, SQInteger index) { uint8 plnr = 0; get_slot(vm, "nr", plnr, index); - if (plnr < 15) { + if(plnr < 15) { return welt->get_player(plnr); } else { @@ -349,6 +485,19 @@ namespace script_api { } + player_t* get_my_player(HSQUIRRELVM vm) + { + sq_pushregistrytable(vm); + uint8 player_nr = PLAYER_UNOWNED; + player_t *pl = NULL; + if (SQ_SUCCEEDED(get_slot(vm, "my_player_nr", player_nr)) && player_nr < 15) { + pl = welt->get_player(player_nr); + } + sq_poptop(vm); + return pl; + } + + const haltestelle_t* param::get(HSQUIRRELVM vm, SQInteger index) { halthandle_t halt = param::get(vm, index); @@ -389,7 +538,7 @@ namespace script_api { if (v) { koord k = v->get_pos().get_2d(); // transform coordinates - welt->get_scenario()->koord_w2sq(k); + coordinate_transform_t::koord_w2sq(k); return push_instance(vm, "tile_x", k.x, k.y, v->get_pos().z); } else { @@ -403,13 +552,25 @@ namespace script_api { return welt->get_scenario(); } - SQInteger param::push(HSQUIRRELVM vm, schedule_entry_t const& v) { - return push_instance(vm, "schedule_entry_x", v.pos, v.minimum_loading, v.waiting_time_shift); + return push_instance(vm, "schedule_entry_x", + v.pos, + v.minimum_loading, + v.waiting_time_shift, + v.spacing_shift, + v.reverse/*, + v.flags, + v.unique_entry_id, + v.condition_bitfield_broadcaster, + v.condition_bitfield_receiver, + v.target_id_condition_trigger, + v.target_id_couple, + v.target_id_uncouple, + v.target_unique_entry_uncouple, + v.max_speed_kmh*/); } - SQInteger param::push(HSQUIRRELVM vm, schedule_t* const& v) { return param::push(vm, v); @@ -454,7 +615,7 @@ namespace script_api { if (v) { koord k = v->get_pos(); // transform coordinates - welt->get_scenario()->koord_w2sq(k); + coordinate_transform_t::koord_w2sq(k); return push_instance(vm, "city_x", k.x, k.y); } else { @@ -472,4 +633,12 @@ namespace script_api { sq_pushnull(vm); return 1; } + tool_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + my_tool_t *mtool = get_attached_instance(vm, index, param::tag()); + if (mtool) { + return mtool->tool; + } + return NULL; + } }; diff --git a/script/api_param.h b/script/api_param.h index 57b5af401d3..9fd001a10b4 100644 --- a/script/api_param.h +++ b/script/api_param.h @@ -18,6 +18,9 @@ class baum_t; class convoi_t; class fabrik_t; +class factory_supplier_desc_t; +class factory_product_desc_t; +class field_t; class gebaeude_t; class grund_t; class haltestelle_t; @@ -25,8 +28,12 @@ class karte_t; class karte_ptr_t; class koord; class koord3d; -struct schedule_entry_t; class label_t; +class leitung_t; +class loadsave_t; +struct schedule_entry_t; +struct my_ribi_t; +struct my_slope_t; class planquadrat_t; class plainstring; class scenario_t; @@ -35,8 +42,11 @@ class settings_t; class simline_t; class player_t; class stadt_t; +class tool_t; class ware_production_t; class weg_t; +class wayobj_t; +class way_builder_t; /** * @namespace script_api The namespace contains all functions necessary to communicate @@ -52,11 +62,6 @@ namespace script_api { struct mytime_t; struct mytime_ticks_t; - /** - * Cannot specialize templates by void, so use own void type - */ - struct void_t {}; - /** * Templated interface to transfer variables from / to squirrel. */ @@ -257,7 +262,7 @@ namespace script_api { { sq_newarray(vm, 0); - FORT(const vector, const&i, v) { + for(auto const&i : v) { param::push(vm, i); sq_arrayappend(vm, -2); } @@ -280,6 +285,31 @@ namespace script_api { // which has to be included if necessary template struct param< quickstone_tpl >; + /** + * partial specialization for function pointers, + * used in api_function.h + */ + template struct param { + // return type of return value + static const char* squirrel_type() + { + return param::squirrel_type(); + } + static const char* typemask() + { + return param::typemask(); + } + }; + template<> struct param { + static const char* squirrel_type() + { + return "void"; + } + static const char* typemask() + { + return ""; + } + }; #define declare_types(mask, sqtype) \ static const char* typemask() { return mask; } \ @@ -302,18 +332,23 @@ namespace script_api { template<> struct param { \ declare_types(mask, sqtype) \ }; - /// macro to declare fake types, inherited from script_api::void_t, + /// macro to declare fake types /// for documentation purposes #define declare_fake_param(T, sqtype) \ - class T { public: T(script_api::void_t) {}; operator script_api::void_t() const { return script_api::void_t();} }; \ + class T { public: T() {}; }; \ template<> struct param { \ - static T get(HSQUIRRELVM vm, SQInteger index) { return param::get(vm, index); } \ - static SQInteger push(HSQUIRRELVM vm, T const& v) { return param::push(vm, v); } \ + static T get(HSQUIRRELVM, SQInteger) { return T(); } \ declare_types(".", sqtype); \ }; + // macro to declare enums +#define declare_enum_param(T, inttype, sqtype) \ + template<> struct param { \ + static T get(HSQUIRRELVM vm, SQInteger index) { return (T)param::get(vm, index); } \ + static SQInteger push(HSQUIRRELVM vm, T const& v) { return param::push(vm, v); } \ + declare_types("i", sqtype); \ + }; - declare_specialized_param(script_api::void_t, ".", "void"); // no typemask, as we call to_bool declare_specialized_param(bool, ".", "bool"); @@ -325,8 +360,12 @@ namespace script_api { declare_specialized_param(sint32, "i", "integer"); declare_specialized_param(uint64, "i", "integer"); declare_specialized_param(sint64, "i", "integer"); - declare_specialized_param(waytype_t, "i", "way_types"); - declare_specialized_param(obj_t::typ, "i", "map_objects"); + declare_enum_param(waytype_t, sint16, "way_types"); + declare_enum_param(systemtype_t, uint8, "way_system_types"); + declare_enum_param(obj_t::typ, uint8, "map_objects"); + declare_enum_param(climate, uint8, "climates"); + declare_specialized_param(my_ribi_t, "i", "dir"); + declare_specialized_param(my_slope_t, "i", "slope"); declare_specialized_param(double, "i|f", "float"); @@ -339,6 +378,7 @@ namespace script_api { declare_specialized_param(convoi_t*, "t|x|y", "convoy_x"); declare_specialized_param(fabrik_t*, "t|x|y", "factory_x"); + declare_specialized_param(const fabrik_t*, "t|x|y", "factory_x"); declare_specialized_param(grund_t*, "t|x|y", "tile_x"); declare_specialized_param(const haltestelle_t*, "t|x|y", "halt_x"); declare_param_mask(haltestelle_t*, "t|x|y", "halt_x"); @@ -355,71 +395,163 @@ namespace script_api { declare_specialized_param(player_t*, "t|x|y", "player_x"); declare_specialized_param(stadt_t*, "t|x|y", "city_x"); declare_specialized_param(const ware_production_t*, "t|x|y", "factory_production_x"); + declare_specialized_param(const factory_supplier_desc_t*, "t|x|y", "factory_production_x"); + declare_specialized_param(const factory_product_desc_t*, "t|x|y", "factory_production_x"); declare_param_mask(ware_production_t*, "t|x|y", "factory_production_x"); + declare_specialized_param(tool_t*, "x", "command_x"); + declare_specialized_param(way_builder_t*, "t|x|y", "way_planner_x"); // export of obj_t derived classes in api/map_objects.cc declare_specialized_param(obj_t*, "t|x|y", "map_object_x"); declare_specialized_param(baum_t*, "t|x|y", "tree_x"); declare_specialized_param(gebaeude_t*, "t|x|y", "building_x"); declare_specialized_param(label_t*, "t|x|y", "label_x"); + declare_specialized_param(leitung_t*, "t|x|y", "powerline_x"); declare_specialized_param(weg_t*, "t|x|y", "way_x"); + declare_specialized_param(field_t*, "t|x|y", "field_x"); + declare_specialized_param(wayobj_t*, "t|x|y", "wayobj_x"); /** - * Templated interface to declare free variables for - * c++ function calls + * Returns the player associated to the script + * (or NULL for scenarios) */ - template struct freevariable { - A1 arg1; - freevariable(A1 const& a1) : arg1(a1) {} + player_t* get_my_player(HSQUIRRELVM vm); - /** - * Pushes the free variables - * @returns number of pushed parameters - */ - SQInteger push(HSQUIRRELVM vm) const { - SQInteger count = 0; - if (SQ_SUCCEEDED( param::push(vm, arg1) ) ) count++; - return count; + // trivial specialization for zero arguments + inline SQInteger push_param(HSQUIRRELVM) { return 0; } + + /** + * Pushes (recursively) all arguments on the stack. + * First argument is pushed first. + * @returns number of arguments (or SQ_ERROR) + */ + template + SQInteger push_param(HSQUIRRELVM vm, const A1 & a1, const As &... as) + { + // push first parameter + if (SQ_SUCCEEDED(script_api::param::push(vm, a1))) { + // push the other parameters + int res = push_param(vm, as...); + if (SQ_SUCCEEDED(res)) { + return res+1; + } + else { + // error: pop our parameter + sq_poptop(vm); + } } - }; + return -1; + } /** * Templated interface to declare free variables for * c++ function calls */ - template struct freevariable2 : public freevariable { - A2 arg2; - freevariable2(A1 const& a1, A2 const& a2) : freevariable(a1), arg2(a2) {} + template struct freevariable : freevariable + { + using base = freevariable; + A1 arg1; + freevariable(const A1 & a1, const As &... as) : base(as...), arg1(a1) {} /** - * Pushes the free variables + * Pushes the free variables. Lifo. * @returns number of pushed parameters */ - SQInteger push(HSQUIRRELVM vm) const { - SQInteger count = 0; - if (SQ_SUCCEEDED( param::push(vm, arg2) ) ) count++; - count += freevariable::push(vm); - return count; + SQInteger push(HSQUIRRELVM vm) const + { + int res = base::push(vm); + if (SQ_SUCCEEDED(res)) { + // first parameter is pushed last + if (SQ_SUCCEEDED(script_api::param::push(vm, arg1))) { + return res+1; + } + else { + sq_pop(vm, res); // pop other parameters in case of failure + } + } + return -1; } }; - template struct freevariable3 : public freevariable2 { - A3 arg3; - freevariable3(A1 const& a1, A2 const& a2, A3 const& a3) : freevariable2(a1,a2), arg3(a3) {} + template struct freevariable + { + A1 arg1; + freevariable(const A1 & a1) : arg1(a1) {} /** * Pushes the free variables * @returns number of pushed parameters */ - SQInteger push(HSQUIRRELVM vm) const { - SQInteger count = 0; - if (SQ_SUCCEEDED( param::push(vm, arg3) ) ) count++; - count += freevariable2::push(vm); - return count; + SQInteger push(HSQUIRRELVM vm) const + { + return push_param(vm, arg1); } }; + /** + * Static class to handle the translation of world coordinates (which are sensible to rotation) + * to script coordinates (that are independent of rotation). + */ + class coordinate_transform_t { + private: + /// Stores how many times initial map was rotated. + /// Scripts do not take care of rotated maps. + /// Coordinates will be translated between in-game coordinates and script coordinates. + /// First v.m. to be started sets this value + static uint8 rotation; + public: + /// called if a new world is initialized + static void new_world() { rotation=4; /*invalid*/ } + + /// inits rotation from karte_t::settings + static void initialize(); + + /// keep track of rotation + static void rotate90() { if (rotation<4) rotation = (rotation+1)&3; } + + /// read/save rotation to stay consistent after saving & loading + static void rdwr(loadsave_t*); + + /** + * rotate actual world coordinates back, + * coordinates after transform are like in the + * scenario's original savegame + */ + static void koord_w2sq(koord &); + + /** + * rotate original coordinates to actual world coordinates + */ + static void koord_sq2w(koord &); + + /** + * rotate actual world coordinates direction to original direction + */ + static void ribi_w2sq(ribi_t::ribi &r); + + /** + * rotate original direction to actual world coordinates direction + */ + static void ribi_sq2w(ribi_t::ribi &r); + + /** + * rotate actual slope to original slope + */ + static void slope_w2sq(slope_t::type &s); + + /** + * rotate original slope to actual slope + */ + static void slope_sq2w(slope_t::type &s); + + static uint8 get_rotation() { return rotation; } + }; + + /// called by karte_t directly + void rotate90(); + /// called by karte_t directly + void new_world(); }; // end of namespace #endif diff --git a/script/dynamic_string.cc b/script/dynamic_string.cc index 28b94c07839..b8741ad9305 100644 --- a/script/dynamic_string.cc +++ b/script/dynamic_string.cc @@ -38,7 +38,7 @@ struct cached_string_t { } }; -static plainstringhashtable_tpl cached_results; +static plainstringhashtable_tpl cached_results; cached_string_t* get_cached_result(const char* function, uint32 cache_time) @@ -72,6 +72,7 @@ void dynamic_string::init(script_vm_t *script) script->register_callback(&dynamic_string::record_result, "dynamicstring_record_result"); } + void dynamic_string::rdwr_cache(loadsave_t *file) { uint32 count = cached_results.get_count(); @@ -79,10 +80,10 @@ void dynamic_string::rdwr_cache(loadsave_t *file) if (file->is_loading()) { // clear list - while (!cached_results.empty()) { + while(!cached_results.empty()) { delete cached_results.remove_first(); } - for (uint32 i = 0; irdwr_str(key); cached_results.set(key, new cached_string_t(file)); @@ -114,7 +115,7 @@ void dynamic_string::update(script_vm_t *script, player_t *player, bool force_up s = entry->result.c_str(); } else { - script->prepare_callback("dynamicstring_record_result", 2, (const char*)buf, ""); + script->prepare_callback("dynamicstring_record_result", 2, (const char*)buf, (const char*)""); // call script const char* err = script->call_function(script_vm_t::QUEUE, method, s, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED)); @@ -143,7 +144,7 @@ void dynamic_string::update(script_vm_t *script, player_t *player, bool force_up } else { if (env_t::networkmode && !env_t::server) { - s = dynamic_string::fetch_result( function, script, this, force_update); + s = dynamic_string::fetch_result( function, NULL, this, force_update); if ( s == NULL) { s = "Waiting for server response..."; } @@ -174,7 +175,8 @@ const char* dynamic_string::fetch_result(const char* function, script_vm_t *scri else { if (entry == NULL) { cached_string_t *old = cached_results.set(function, new cached_string_t(NULL, dr_time(), listener)); - assert(old == NULL); (void)old; + assert(old == NULL); + (void)old; } // send request nwc_scenario_t *nwc = new nwc_scenario_t(); diff --git a/script/dynamic_string.h b/script/dynamic_string.h index a9254af26f5..4a74512628a 100644 --- a/script/dynamic_string.h +++ b/script/dynamic_string.h @@ -15,7 +15,6 @@ class script_vm_t; class player_t; class loadsave_t; - /** * Class of strings to hold result of text-returning script functions. * diff --git a/script/export_objs.cc b/script/export_objs.cc index 8e4d52e7c11..2d93be16cfa 100644 --- a/script/export_objs.cc +++ b/script/export_objs.cc @@ -13,9 +13,9 @@ #include "api/api.h" -void register_export_function(HSQUIRRELVM vm) +void register_export_function(HSQUIRRELVM vm, bool scenario) { - script_api::start_squirrel_type_logging(); + script_api::start_squirrel_type_logging(scenario ? "scenario" : "ai"); sq_pushroottable(vm); @@ -24,17 +24,23 @@ void register_export_function(HSQUIRRELVM vm) export_convoy(vm); export_factory(vm); export_goods_desc(vm); - export_gui(vm); + export_gui(vm, scenario); export_halt(vm); export_line(vm); export_map_objects(vm); - export_player(vm); - export_scenario(vm); + export_player(vm, scenario); + if (scenario) { + export_scenario(vm); + } export_schedule(vm); export_settings(vm); export_simple(vm); + export_string_methods(vm); export_tiles(vm); - export_world(vm); + export_world(vm, scenario); + export_pathfinding(vm); + + export_commands(vm); sq_pop(vm, 1); // root table diff --git a/script/export_objs.h b/script/export_objs.h index 09108de9d97..9a8f7e10f5d 100644 --- a/script/export_objs.h +++ b/script/export_objs.h @@ -11,7 +11,8 @@ /** * Registers the complete export interface. + * @param scenario true if exporting is for scenario scripting */ -void register_export_function(HSQUIRRELVM vm); +void register_export_function(HSQUIRRELVM vm, bool scenario); #endif diff --git a/script/script.cc b/script/script.cc index 9481b2e60db..623dbdb2921 100644 --- a/script/script.cc +++ b/script/script.cc @@ -10,10 +10,13 @@ #include "../squirrel/sqstdaux.h" // for error handlers #include "../squirrel/sqstdio.h" // for loadfile #include "../squirrel/sqstdstring.h" // export for scripts +#include "../squirrel/sqstdmath.h" // export for scripts +#include "../squirrel/sqstdsystem.h" // export for scripts #include "../squirrel/sq_extensions.h" // for sq_call_restricted #include "../utils/log.h" +#include "../tpl/inthashtable_tpl.h" #include "../tpl/vector_tpl.h" // for error popups #include "../gui/help_frame.h" @@ -22,22 +25,23 @@ #include "../utils/plainstring.h" +namespace script_api { + bool pause_game(); // api_control.cc +} + // debug: store stack pointer #define BEGIN_STACK_WATCH(v) int stack_top = sq_gettop(v); // debug: compare stack pointer with expected stack pointer, raise warning if failure #define END_STACK_WATCH(v, delta) if ( (stack_top+(delta)) != sq_gettop(v)) { dbg->warning( __FUNCTION__, "(%d) stack in %d expected %d out %d", __LINE__,stack_top,stack_top+(delta),sq_gettop(v)); } -// log file -static log_t* script_log = NULL; -// list of active scripts (they share the same log-file, error and print-functions) -static vector_tpl all_scripts; - -static void printfunc(HSQUIRRELVM, const SQChar *s, ...) +void script_vm_t::printfunc(HSQUIRRELVM vm, const SQChar *s, ...) { va_list vl; va_start(vl, s); - script_log->vmessage("Script", "Print", s, vl); + if (script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm)) { + script->log->vmessage("Script", "Print", s, vl); + } va_end(vl); } @@ -51,56 +55,74 @@ void script_vm_t::errorfunc(HSQUIRRELVM vm, const SQChar *s_, ...) while (s && *s=='\n') s++; while (s && s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; - va_list vl; - va_start(vl, s_); - script_log->vmessage("[Script]", "ERROR", s, vl); - va_end(vl); + // get failed script + script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm); + + if (script) { + va_list vl; + va_start(vl, s_); + script->log->vmessage("Script", "Error", s, vl); + va_end(vl); + } + // only one error message at a time - hopefully + // collect into static buffer static cbuffer_t buf; if (strcmp(s, "")==0) { + // start of error message buf.clear(); - buf.printf("Your script made an error!
\n"); - } - if (strcmp(s, "
")==0) { - help_frame_t *win = new help_frame_t(); + buf.printf("An error occurred within a script!
\n"); + } + else if (strcmp(s, "")==0) { + // end of error message + help_frame_t *win = dynamic_cast(win_get_magic(magic_script_error)); + if (win == NULL) { + win = new help_frame_t(); + create_win( win, w_info, magic_script_error); + } win->set_text(buf); win->set_name("Script error occurred"); - create_win( win, w_info, magic_none); - // find failed script - for(uint32 i=0; iuses_vm(vm)) { - all_scripts[i]->set_error(buf); - break; + + if (script) { + script->set_error(buf); + // pause game + if (script->pause_on_error) { + script_api::pause_game(); } } } - - va_start(vl, s_); - buf.vprintf(s, vl); - va_end(vl); - buf.append("
"); - + else { + va_list vl; + va_start(vl, s_); + buf.vprintf(s, vl); + va_end(vl); + buf.append("
"); + } free(s_dup); } void export_include(HSQUIRRELVM vm, const char* include_path); // api_include.cc // virtual machine -script_vm_t::script_vm_t(const char* include_path_) +script_vm_t::script_vm_t(const char* include_path_, const char* log_name) { + pause_on_error = false; + vm = sq_open(1024); sqstd_seterrorhandlers(vm); sq_setprintfunc(vm, printfunc, errorfunc); - if (script_log == NULL) { - script_log = new log_t("script.log", true, true, true, "script engine started.\n"); - } - all_scripts.append(this); + register_vm(vm); + log = new log_t(log_name, true, true, true, "script engine started.\n"); + + // store ptr to us in vm + sq_setforeignptr(vm, this); // create thread, and put it into registry-table sq_pushregistrytable(vm); sq_pushstring(vm, "thread", -1); - thread = sq_newthread(vm, 100); + thread = sq_newthread(vm, 1024); sq_newslot(vm, -3, false); + register_vm(thread); // create queue array sq_pushstring(vm, "queue", -1); sq_newarray(vm, 0); @@ -111,26 +133,36 @@ script_vm_t::script_vm_t(const char* include_path_) sq_newslot(vm, -3, false); // pop registry sq_pop(vm, 1); + // store ptr to us in vm + sq_setforeignptr(vm, this); + sq_setforeignptr(thread, this); error_msg = NULL; include_path = include_path_; // register libraries sq_pushroottable(vm); sqstd_register_stringlib(vm); + sqstd_register_mathlib(vm); + sqstd_register_systemlib(vm); sq_pop(vm, 1); // export include command export_include(vm, include_path); + // initialize coordinate and rotation handling + script_api::coordinate_transform_t::initialize(); } script_vm_t::~script_vm_t() { - sq_close(vm); // also closes thread - all_scripts.remove(this); - if (all_scripts.empty()) { - delete script_log; - script_log = NULL; - } + unregister_vm(thread); + unregister_vm(vm); + // remove from suspended calls list + suspended_scripts_t::remove_vm(thread); + suspended_scripts_t::remove_vm(vm); + // close vm, also closes thread + sq_close(vm); + + delete log; } const char* script_vm_t::call_script(const char* filename) @@ -141,7 +173,7 @@ const char* script_vm_t::call_script(const char* filename) } // call it sq_pushroottable(vm); - if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue))) { + if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000))) { sq_pop(vm, 1); // pop script return "Call script failed"; } @@ -156,16 +188,21 @@ const char* script_vm_t::call_script(const char* filename) const char* script_vm_t::eval_string(const char* squirrel_string) { + if (squirrel_string == NULL) { + return NULL; + } + HSQUIRRELVM &job = thread; + // log string + printfunc(job, "String to compile:\n%s\n<<<\n", squirrel_string); // compile string - if (!SQ_SUCCEEDED(sq_compilebuffer(vm, squirrel_string, strlen(squirrel_string), "userdefinedstringmethod", true))) { + if (!SQ_SUCCEEDED(sq_compilebuffer(job, squirrel_string, strlen(squirrel_string), "userdefinedstringmethod", true))) { set_error("Error compiling string buffer"); return get_error(); } // execute - sq_pushroottable(vm); - sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000); - sq_pop(vm, 1); - return get_error(); + sq_pushroottable(job); + // stack: closure, root table (1st param) + return intern_finish_call(job, QUEUE, 1, true); } @@ -181,7 +218,10 @@ const char* script_vm_t::intern_prepare_call(HSQUIRRELVM &job, call_type_t ct, c switch (ct) { case FORCE: + case FORCEX: job = vm; + // block calls to suspendable functions + sq_block_suspend(job, function); break; case TRY: if (sq_getvmstate(thread) == SQ_VMSTATE_SUSPENDED) { @@ -215,17 +255,29 @@ const char* script_vm_t::intern_finish_call(HSQUIRRELVM job, call_type_t ct, int BEGIN_STACK_WATCH(job); // stack: closure, nparams*objects const char* err = NULL; - bool suspended = sq_getvmstate(job) == SQ_VMSTATE_SUSPENDED; + // only call the closure if vm is idle (maybe in RUN state) + bool suspended = sq_getvmstate(job) != SQ_VMSTATE_IDLE; + // check queue, if not empty resume first job in queue + if (!suspended && ct != FORCE && ct != FORCEX) { + sq_pushregistrytable(job); + sq_pushstring(job, "queue", -1); + sq_get(job, -2); + // stack: registry, queue + if (sq_getsize(job, -1) > 0) { + suspended = true; + } + sq_pop(job, 2); + } // queue function call? if (suspended && ct == QUEUE) { intern_queue_call(job, nparams, retvalue); err = "suspended"; // stack: clean } - if (suspended) { + if (suspended && ct != FORCE && ct != FORCEX) { intern_resume_call(job); } - if (!suspended || ct == FORCE) { + if (!suspended || ct == FORCE || ct == FORCEX) { // set active callback if call could be suspended if (ct == QUEUE) { intern_make_pending_callback_active(); @@ -234,7 +286,6 @@ const char* script_vm_t::intern_finish_call(HSQUIRRELVM job, call_type_t ct, int sq_pushregistrytable(job); script_api::create_slot(job, "was_queued", false); sq_poptop(job); - END_STACK_WATCH(job,0); err = intern_call_function(job, ct, nparams, retvalue); } @@ -251,12 +302,16 @@ const char* script_vm_t::intern_call_function(HSQUIRRELVM job, call_type_t ct, i BEGIN_STACK_WATCH(job); dbg->message("script_vm_t::intern_call_function", "start: stack=%d nparams=%d ret=%d", sq_gettop(job), nparams, retvalue); const char* err = NULL; + uint32 opcodes = ct == FORCEX ? 100000 : 10000; // call the script - if (!SQ_SUCCEEDED(sq_call_restricted(job, nparams, retvalue, ct == FORCE))) { + if (!SQ_SUCCEEDED(sq_call_restricted(job, nparams, retvalue, ct == FORCE || ct == FORCEX, opcodes))) { err = "Call function failed"; retvalue = false; } - if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED) { + if (ct == FORCE || ct == FORCEX) { + sq_block_suspend(job, NULL); + } + if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED || ct == FORCE || ct == FORCEX) { // remove closure sq_remove(job, retvalue ? -2 : -1); if (ct == QUEUE && retvalue) { @@ -299,16 +354,27 @@ void script_vm_t::intern_resume_call(HSQUIRRELVM job) script_api::get_slot(job, "retvalue", retvalue, -1); int nparams = 0; script_api::get_slot(job, "nparams", nparams, -1); + bool wait = false; + script_api::get_slot(job, "wait_external", wait, -1); sq_pop(job, 1); END_STACK_WATCH(job, 0); + if (wait) { + dbg->message("script_vm_t::intern_resume_call", "waits for external call to be able to proceed"); + return; + } + if (!sq_canresumevm(job)) { + // vm waits for return value to suspended call + dbg->message("script_vm_t::intern_resume_call", "waiting for return value"); + return; + } // vm suspended, but not from call to our methods if (nparams < 0) { retvalue = false; } // resume v.m. - if (!SQ_SUCCEEDED(sq_resumevm(job, retvalue))) { + if (!SQ_SUCCEEDED(sq_resumevm(job, retvalue, 10000))) { retvalue = false; } // if finished, clear stack @@ -389,6 +455,7 @@ void script_vm_t::intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue) // stack: [...], queue[i] sint32 n = sq_getsize(job, -1); if (n != nparams+2) { + sq_poptop(job); continue; // different number of arguments } equal = true; @@ -427,7 +494,7 @@ void script_vm_t::intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue) // add callback to queue sq_pushstring(job, "queued_callbacks", -1); sq_get(job, -3); - sq_newarray(job, 10); + sq_newarray(job, 0); // stack: array, registry, queue, queued_callbacks, queued_callbacks[end] sq_pushstring(job, "pending_callback", -1); // delete pending_callback slot and push it @@ -600,7 +667,7 @@ void script_vm_t::intern_make_pending_callback_active() BEGIN_STACK_WATCH(vm); sq_pushregistrytable(vm); sq_pushstring(vm, "active_callbacks", -1); - sq_newarray(vm, 1); + sq_newarray(vm, 0); sq_pushstring(vm, "pending_callback", -1); if (SQ_SUCCEEDED( sq_deleteslot(vm, -4, true) ) ) { // stack: registry, "..", array[], pending_callback @@ -659,3 +726,58 @@ void script_vm_t::intern_call_callbacks(HSQUIRRELVM job) sq_poptop(job); END_STACK_WATCH(job,0); } + + +void script_vm_t::set_my_player(uint8 player_nr) +{ + sq_pushregistrytable(vm); + script_api::create_slot(vm, "my_player_nr", player_nr); + sq_poptop(vm); +} + + +/* -------- management of suspended scripts that wait for return value ----------- */ + +inthashtable_tpl suspended_scripts_t::suspended_scripts; + + +uint32 suspended_scripts_t::get_unique_key(void* ptr) +{ + uint32 key = (uint32)(size_t)ptr; + while (key == 0 || suspended_scripts.get(key)) { + key ++; + } + return key; +} + + +void suspended_scripts_t::register_suspended_script(uint32 key, HSQUIRRELVM vm) +{ + suspended_scripts.set(key, vm); + + // mark vm to wait for external call to allow for wake-up + sq_pushregistrytable(vm); + bool wait = true; + script_api::create_slot(vm, "wait_external", wait); + sq_poptop(vm); +} + + +HSQUIRRELVM suspended_scripts_t::remove_suspended_script(uint32 key) +{ + return suspended_scripts.remove(key); +} + + +void suspended_scripts_t::remove_vm(HSQUIRRELVM vm) +{ + inthashtable_tpl::iterator iter=suspended_scripts.begin(), end=suspended_scripts.end(); + for(; iter != end; ) { + if ( (*iter).value == vm) { + iter = suspended_scripts.erase(iter); + } + else { + ++iter; + } + } +} diff --git a/script/script.h b/script/script.h index f76d9b07b05..05a165f372b 100644 --- a/script/script.h +++ b/script/script.h @@ -16,6 +16,10 @@ #include "../utils/plainstring.h" #include +class log_t; +template class inthashtable_tpl; +void sq_setwakeupretvalue(HSQUIRRELVM v); //sq_extensions + /** * Class providing interface to squirrel's virtual machine. * @@ -24,7 +28,7 @@ */ class script_vm_t { public: - script_vm_t(const char* include_path_); + script_vm_t(const char* include_path, const char* log_name); ~script_vm_t(); /** @@ -45,11 +49,18 @@ class script_vm_t { const char* get_error() const { return error_msg.c_str(); } + /** + * The script can only act as a certain player. + * @param player_nr the number of the player (PLAYER_UNOWNED for scenarios) + */ + void set_my_player(uint8 player_nr); + /// priority of function call enum call_type_t { - FORCE = 1, ///< function has to return, raise error if not - QUEUE = 2, ///< function call can be queued, return value can be propagated by call back - TRY = 3 ///< function call will not be queued, if virtual machine is suspended just return + FORCE, ///< function has to return, raise error if not + FORCEX, ///< function has to return, raise error if not, give more opcodes + QUEUE, ///< function call can be queued, return value can be propagated by call back + TRY ///< function call will not be queued, if virtual machine is suspended just return }; /** @@ -58,76 +69,54 @@ class script_vm_t { */ static bool is_call_suspended(const char* err); -# define prep_function_call() \ - HSQUIRRELVM job; \ - const char* err = intern_prepare_call(job, ct, function); \ - if (err) { \ - return err; \ - } \ - int nparam = 1; - -# define do_function_call() \ - err = intern_finish_call(job, ct, nparam, true); \ - if (err == NULL) { \ - ret = script_api::param::get(job, -1); \ - sq_poptop(job); \ - } \ - return err; - /** * calls scripted function * @param function function name of squirrel function * @returns error msg (or NULL if succeeded) */ - const char* call_function(call_type_t ct, const char* function) { - prep_function_call(); - return intern_finish_call(job, ct, nparam, false); - } - - /** - * calls scripted function - * - * @tparam R type of return value - * @param function function name of squirrel function - * @param ret return value of script function is stored here - * @returns error msg (or NULL if succeeded), if call was suspended ret is invalid - */ - template - const char* call_function(call_type_t ct, const char* function, R& ret) { - prep_function_call(); - do_function_call(); + const char* call_function(call_type_t ct, const char* function) + { + // choose correct vm, push function + HSQUIRRELVM job; + const char* err = intern_prepare_call(job, ct, function); + if (err) { + return err; + } + // now call + return intern_finish_call(job, ct, 1, false); } /** * calls scripted function * * @tparam R type of return value - * @tparam A1 type of first argument + * @tparam As types of arguments * @param function function name of squirrel function - * @param arg1 first argument passed to squirrel function + * @param as any number of arguments passed to squirrel function * @param ret return value of script function is stored here * @returns error msg (or NULL if succeeded), if call was suspended ret is invalid */ - template - const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1) { - prep_function_call(); - script_api::param::push(job, arg1); nparam++; - do_function_call(); - } - template - const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1, A2 arg2) { - prep_function_call(); - script_api::param::push(job, arg1); nparam++; - script_api::param::push(job, arg2); nparam++; - do_function_call(); - } - template - const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1, A2 arg2, A3 arg3) { - prep_function_call(); - script_api::param::push(job, arg1); nparam++; - script_api::param::push(job, arg2); nparam++; - script_api::param::push(job, arg3); nparam++; - do_function_call(); + template + const char* call_function(call_type_t ct, const char* function, R& ret, const As &... as) + { + // choose correct vm, push function + HSQUIRRELVM job; + const char* err = intern_prepare_call(job, ct, function); + if (err) { + return err; + } + // push parameters + int nparam = script_api::push_param(job, as...); + if (!SQ_SUCCEEDED(nparam)) { + return "error pushing argument"; + } + // now call + err = intern_finish_call(job, ct, nparam+1, true); + if (err == NULL) { + ret = script_api::param::get(job, -1); + sq_poptop(job); + } + return err; } /** @@ -150,22 +139,13 @@ class script_vm_t { * @param function name of function to be called as callback * @param nret the nret-th parameter will be replaced by return value of suspended function. */ - template - void prepare_callback(const char* function, int nret, A1 arg1, A2 arg2) { - if (intern_prepare_pending_callback(function, nret)) { - script_api::param::push(vm, arg1); - script_api::param::push(vm, arg2); - intern_store_pending_callback(3); - } - } - - template - void prepare_callback(const char* function, int nret, A1 arg1, A2 arg2, A3 arg3) { + template + void prepare_callback(const char* function, int nret, const As &... as) + { if (intern_prepare_pending_callback(function, nret)) { - script_api::param::push(vm, arg1); - script_api::param::push(vm, arg2); - script_api::param::push(vm, arg3); - intern_store_pending_callback(4); + int nparam = script_api::push_param(vm, as...); + assert(SQ_SUCCEEDED(nparam)); // FIXME + intern_store_pending_callback(nparam+1); } } @@ -181,9 +161,18 @@ class script_vm_t { /// thread in the virtual machine, used to run functions that can be suspended HSQUIRRELVM thread; + /// our log file + log_t* log; plainstring error_msg; + /// path to files to #include + plainstring include_path; + +public: + bool pause_on_error; + +private: /// @{ /// @name Helper functions to call, suspend, queue calls to scripted functions @@ -229,8 +218,43 @@ class script_vm_t { /// set error message, used in errorhandlers void set_error(const char* error) { error_msg = error; } - /// path to files to #include - plainstring include_path; + /// custom print handler + static void printfunc(HSQUIRRELVM, const SQChar *s, ...); +}; + +/** + * Class to manage all vm's that are suspended and waiting for the return of + * a call to a tool. + */ +class suspended_scripts_t { +private: + static inthashtable_tpl suspended_scripts; + + static HSQUIRRELVM remove_suspended_script(uint32 key); + +public: + // generates key from a pointer + static uint32 get_unique_key(void* key); + + static void register_suspended_script(uint32 key, HSQUIRRELVM vm); + + // remove any reference to given vm + static void remove_vm(HSQUIRRELVM vm); + + template + static void tell_return_value(uint32 key, R& ret) + { + HSQUIRRELVM vm = remove_suspended_script(key); + if (vm) { + script_api::param::push(vm, ret); + sq_setwakeupretvalue(vm); + // this vm can be woken up now + sq_pushregistrytable(vm); + bool wait = false; + script_api::create_slot(vm, "wait_external", wait); + sq_poptop(vm); + } + } }; #endif diff --git a/script/script_loader.cc b/script/script_loader.cc new file mode 100644 index 00000000000..71412042614 --- /dev/null +++ b/script/script_loader.cc @@ -0,0 +1,76 @@ +/* + * This file is part of the Simutrans-Extended project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "script_loader.h" +#include "script.h" +#include "api/api.h" +#include "export_objs.h" +#include "../dataobj/environment.h" +#include "../utils/cbuffer_t.h" + + + +bool script_loader_t::load_base_script(script_vm_t *script, const char* base) +{ + cbuffer_t buf; + buf.printf("%sscript/%s", env_t::data_dir, base); + if (const char* err = script->call_script(buf)) { + // should not happen + dbg->error("load_base_script", "error [%s] calling %s", err, (const char*)buf); + return false; + } + return true; +} + + +script_vm_t* script_loader_t::start_vm(const char* base_file_name, const char* logfile_name, const char* include_path, bool is_scenario) +{ + script_vm_t* script = new script_vm_t(include_path, logfile_name); + // load global stuff + // constants must be known compile time + export_global_constants(script->get_vm()); + + // load scripting base definitions + bool ok = load_base_script(script, "script_base.nut"); + // load specific base definitions + ok = ok && load_base_script(script, base_file_name); + + // register api functions + if (ok) { + register_export_function(script->get_vm(), is_scenario); + if (script->get_error()) { + dbg->error("start_vm", "error [%s] calling register_export_function", script->get_error()); + ok = false; + } + } + if (ok) { + return script; + } + else { + delete script; + return NULL; + } +} + + +void script_loader_t::load_compatibility_script(script_vm_t *script) +{ + // check api version + plainstring api_version; + if (const char* err = script->call_function(script_vm_t::FORCE, "get_api_version", api_version)) { + dbg->warning("scenario_t::init", "error [%s] calling get_api_version", err); + api_version = "120.1"; + } + if (api_version != "*") { + // load scenario compatibility script + if (load_base_script(script, "script_compat.nut")) { + plainstring dummy; + // call compatibility function + if (const char* err = script->call_function(script_vm_t::FORCE, "compat", dummy, api_version) ) { + dbg->warning("scenario_t::init", "error [%s] calling compat", err); + } + } + } +} diff --git a/script/script_loader.h b/script/script_loader.h new file mode 100644 index 00000000000..19b07d2bcea --- /dev/null +++ b/script/script_loader.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Simutrans-Extended project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef SCRIPT_SCRIPT_LOADER_H +#define SCRIPT_SCRIPT_LOADER_H + + +class script_vm_t; + +struct script_loader_t +{ + /** + * Starts vm, loads the scripting interface, loads script/[base_file_name]. + * @returns vm or NULL in case of error + */ + static script_vm_t* start_vm(const char* base_file_name, const char* logfile_name, const char* include_path, bool is_scenario); + + /** + * Loads base script files: env_t::base_dir/script/script_base.nut and env_t::base_dir/script/[base]. + */ + static bool load_base_script(script_vm_t *vm, const char* base); + + /** + * loads necessary compatibility scripts + */ + static void load_compatibility_script(script_vm_t *script); +}; + +#endif diff --git a/script/script_tool_manager.cc b/script/script_tool_manager.cc new file mode 100644 index 00000000000..8ee21baee18 --- /dev/null +++ b/script/script_tool_manager.cc @@ -0,0 +1,133 @@ +/* + * This file is part of the Simutrans-Extended project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "script_tool_manager.h" + +#include "../dataobj/tabfile.h" + +#include "../simdebug.h" +#include "../tool/simtool-scripted.h" +#include "../simskin.h" +#include "../descriptor/skin_desc.h" +#include "../sys/simsys.h" +#include "../gui/tool_selector.h" +#include "../utils/cbuffer.h" +#include "../utils/searchfolder.h" + + +vector_tpl script_tool_manager_t::one_click_script_tools; +vector_tpl script_tool_manager_t::two_click_script_tools; + + +bool script_tool_manager_t::check_file( const char *path ) +{ + cbuffer_t buf; + buf.printf("%s/tool.nut", path ); + if (FILE* const f = dr_fopen(buf, "r")) { + fclose(f); + return true; + } + return false; +} + + +const scripted_tool_info_t* script_tool_manager_t::get_script_info(const char* path) +{ + scripted_tool_info_t* info = new scripted_tool_info_t(); + info->path = path; + + // read description.tab + cbuffer_t buf; + buf.printf("%s/description.tab", path); + tabfile_t file; + + if ( file.open(buf) ) { + tabfileobj_t contents; + file.read( contents ); + info->title = translator::translate(contents.get_string("title", path)); + info->menu_arg = contents.get_string("menu", ""); + info->tooltip = translator::translate(contents.get_string("tooltip", "")); + info->cursor_area = contents.get_koord("cursor_area", koord(1,1)); + info->cursor_offset = contents.get_koord("cursor_offset", koord(0,0)); + if( info->cursor_area.x<1 || info->cursor_area.y<1 ) { + dbg->warning("script_tool_manager_t::get_script_info()", "The cursor area of scripted tool %s must have at least 1 tile length. Sanitized to (1,1).", info->title.c_str()); + info->cursor_area = koord(1,1); + } + + const char* skin_name = contents.get_string("icon", ""); + info->desc = skinverwaltung_t::get_extra(skin_name, strlen(skin_name), skinverwaltung_t::cursor); + info->is_one_click = !( strcmp(contents.get_string("type", "one_click"), "two_click")==0 ); + } + else { + // no description.tab, use default values + info->title = path; + } + return info; +} + + +tool_t* script_tool_manager_t::load_tool(char const* path, tool_t* tool) +{ + if (!check_file(path)) { + return tool; + } + const scripted_tool_info_t* info = get_script_info(path); + + if (tool) { + // reinitialize existing tool + exec_script_base_t* esb = dynamic_cast(tool); + assert(esb); + esb->set_info(info); + esb->init_images(tool); + } + else { + // create new tool + if (info->is_one_click) { + tool = new tool_exec_script_t(info); + } + else { + tool = new tool_exec_two_click_script_t(info); + } + } + return tool; +} + + +void script_tool_manager_t::load_scripts(char const* path) +{ + searchfolder_t find; + find.search(path, "", searchfolder_t::SF_ONLYDIRS); + + for(const char* const &name : find) { + cbuffer_t fullname; + fullname.printf("%s%s",path,name); + + tool_t *tool = load_tool(fullname); + + if (tool_exec_script_t* ot = dynamic_cast(tool)) { + one_click_script_tools.append(ot); + } + else if (tool_exec_two_click_script_t* tt = dynamic_cast(tool)) { + two_click_script_tools.append(tt); + } + } +} + + +void script_tool_manager_t::fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 /*sound_ok*/) +{ + for(uint32 i=0; iget_menu_arg(), arg)==0 ) { + tool_selector->add_tool_selector(tool); + } + } + for(uint32 i=0; iget_menu_arg(), arg)==0 ) { + tool_selector->add_tool_selector(tool); + } + } +} diff --git a/script/script_tool_manager.h b/script/script_tool_manager.h new file mode 100644 index 00000000000..3e17e9023df --- /dev/null +++ b/script/script_tool_manager.h @@ -0,0 +1,61 @@ +/* + * This file is part of the Simutrans-Extended project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef SCRIPT_SCRIPT_TOOL_MANAGER_H +#define SCRIPT_SCRIPT_TOOL_MANAGER_H + + +#include "../tpl/vector_tpl.h" + +class exec_script_base_t; +struct scripted_tool_info_t; +class tool_t; +class tool_selector_t; +class tool_exec_script_t; +class tool_exec_two_click_script_t; + +/** + * There's no need to construct an instance since everything is static here. + */ +class script_tool_manager_t +{ +private: + /// All one-click script tools + static vector_tpl one_click_script_tools; + /// All two-click script tools + static vector_tpl two_click_script_tools; + +public: + + /// Looks for tool.nut in path + static bool check_file(char const* path); + + /** + * Reads description.tab at path. + * If no description is found, use default values. + * @returns filled info structure. + */ + static const scripted_tool_info_t* get_script_info(const char* path); + + /** + * Loads tool from path. + * If @p tool is not NULL it is reinitialized with the scripted tool. + * @returns tool + */ + static tool_t* load_tool(char const* path, tool_t* tool = NULL); + + /** + * Reads all tools from directory @p path. + */ + static void load_scripts(char const* path); + + /** + * Fills toolbar with scripted tools. + * Select only tools with menu entry equal to @p arg. + */ + static void fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 sound_ok); +}; + +#endif diff --git a/scripts/run-automated-tests.sh b/scripts/run-automated-tests.sh new file mode 100644 index 00000000000..bd10f6d53ff --- /dev/null +++ b/scripts/run-automated-tests.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + + +pushd simutrans +../simutrans-extended -use_workdir -objects pak128.britain-ex-nightly -lang en -scenario automated-tests -debug 2 2>&1 | ts -s | tee output.log & +pid=$! + +result=1 + +while : +do + sleep 1 + + if [[ ! -d /proc/$pid/ ]] + then + # process crashed etc. + echo "Process crashed (test failed)" + result=1 + break + fi + + if [[ -n "$(grep 'Tests completed successfully.' output.log)" ]] + then + # kill + echo "Killing process (test succeeded)" + kill %1 + result=0 + break + fi + + if [[ -n "$(grep 'error \[Call function failed\] calling' output.log)" || + -n "$(grep 'error \[Reading / compiling script failed] calling' output.log)" || + -n "$(grep '' output.log)" ]] + then + # kill + echo "Killing process (test failed)" + kill %1 + result=1 + break + fi +done + +popd + +exit $result diff --git a/simcity.cc b/simcity.cc index 0caf6f5537c..446b0f550c3 100644 --- a/simcity.cc +++ b/simcity.cc @@ -661,7 +661,7 @@ bool stadt_t::bewerte_loc(const koord pos, const rule_t ®el, int rotation) //printf("Test for (%s) in rotation %d\n", pos.get_str(), rotation); koord k; - FOR(vector_tpl, const& r, regel.rule) { + for(rule_entry_t const& r : regel.rule) { uint8 x,y; switch (rotation) { default: @@ -1490,7 +1490,7 @@ bool stadt_t::is_within_players_network(const player_t* player) const } // Check if these stations are in the player's network... - FOR(vector_tpl, const halt, halts) + for(halthandle_t const halt : halts) { if (halt->has_available_network(player)) { @@ -1603,7 +1603,7 @@ stadt_t::~stadt_t() welt->remove_queued_city(this); // Remove references to this city from factories. - FOR(vector_tpl, factory, city_factories) + for(fabrik_t* factory : city_factories) { factory->clear_city(); } @@ -1639,13 +1639,13 @@ stadt_t::~stadt_t() // avoid the bookkeeping if world geets destroyed } // Remove substations - FOR(vector_tpl, sub, substations) + for(senke_t* sub : substations) { sub->city = NULL; } const weighted_vector_tpl& cities = welt->get_cities(); - FOR(weighted_vector_tpl, const i, cities) + for(stadt_t* const i : cities) { i->remove_connected_city(this); } @@ -1661,7 +1661,7 @@ stadt_t::~stadt_t() static bool name_used(weighted_vector_tpl const& cities, char const* const name) { - FOR(weighted_vector_tpl, const i, cities) { + for(stadt_t* const i : cities) { if (strcmp(i->get_name(), name) == 0) { return true; } @@ -2855,7 +2855,7 @@ void stadt_t::calc_growth() // now iterate over all factories to get the ratio of producing version non-producing factories // we use the incoming storage as a measure and we will only look for end consumers (power stations, markets) - FOR(const vector_tpl, const& fab, welt->get_fab_list()) + for(fabrik_t* const fab : welt->get_fab_list()) { if(fab && fab->get_city() == this && fab->get_consumers().empty() && !fab->get_suppliers().empty()) { @@ -2912,7 +2912,7 @@ void stadt_t::calc_growth() // Now that we have the percentages, calculate how large that this city is compared to others in the game. uint32 number_of_larger_cities = 0; uint32 number_of_smaller_cities = 0; - FOR(const weighted_vector_tpl, const& city, welt->get_cities()) + for(stadt_t* city : world()->get_cities()) { if (city == this) { @@ -4888,7 +4888,7 @@ uint32 stadt_t::get_jobs_by_class(uint8 p_class) sum += building->get_adjusted_jobs_by_class(p_class); } } - FOR(vector_tpl, factory, city_factories) { + for(fabrik_t* factory : city_factories) { sum += factory->get_building()->get_adjusted_jobs_by_class(p_class); } return sum; @@ -4904,7 +4904,7 @@ uint32 stadt_t::get_visitor_demand_by_class(uint8 p_class) sum += building->get_adjusted_visitor_demand_by_class(p_class); } } - FOR(vector_tpl, factory, city_factories) { + for(fabrik_t* factory : city_factories) { sum += factory->get_building()->get_adjusted_visitor_demand_by_class(p_class); } return sum; @@ -6040,7 +6040,7 @@ bool private_car_destination_finder_t::is_target(const grund_t* gr, const grund_ return false; } -int private_car_destination_finder_t::get_cost(const grund_t* gr, sint32 max_speed, koord) +int private_car_destination_finder_t::get_cost(const grund_t* gr, sint32 max_speed, ribi_t::ribi) { const weg_t *w = gr->get_weg(road_wt); if(!w) diff --git a/simcity.h b/simcity.h index 89c9887d4af..024a249a2d1 100644 --- a/simcity.h +++ b/simcity.h @@ -100,7 +100,7 @@ class private_car_destination_finder_t : public test_driver_t virtual ribi_t::ribi get_ribi( const grund_t* gr) const; - virtual int get_cost(const grund_t* gr, const sint32 max_speed, koord from_pos); + virtual int get_cost(const grund_t* gr, const sint32 max_speed, ribi_t::ribi from); }; /** diff --git a/simconvoi.cc b/simconvoi.cc index cd85218eeab..d60dfae15d3 100644 --- a/simconvoi.cc +++ b/simconvoi.cc @@ -572,7 +572,7 @@ DBG_MESSAGE("convoi_t::finish_rd()","next_stop_index=%d", next_stop_index ); vector_tpl lines; get_owner()->simlinemgmt.get_lines(schedule->get_type(), &lines); new_line = linehandle_t(); - FOR(vector_tpl, const l, lines) { + for(linehandle_t const l : lines) { if( schedule->matches( welt, l->get_schedule() ) ) { // if a line is assigned, set line! new_line = l; @@ -2358,10 +2358,8 @@ void convoi_t::step() case LEAVING_DEPOT: last_stop_was_depot = true; get_owner()->simlinemgmt.get_lines(schedule->get_type(), &lines); - FOR(vector_tpl, const l, lines) - { - if(schedule->matches(welt, l->get_schedule())) - { + for(linehandle_t const l : lines) { + if( schedule->matches( welt, l->get_schedule() ) ) { // if a line is assigned, set line! set_line(l); line->renew_stops(); @@ -5803,16 +5801,18 @@ void convoi_t::hat_gehalten(halthandle_t halt) // harbour has any size vehicles_loading = vehicle_count; } - else - { - // calculate real station length - // and numbers of vehicles that can be (un)loaded + else if (vehicle_count == 1 && CARUNITS_PER_TILE >= vehicle[0]->get_desc()->get_length()) { + vehicles_loading = 1; + // one vehicle, which fits into one tile + } + else { + // difference between actual station length and vehicle lenghts + sint16 station_length = -vehicle[vehicles_loading]->get_desc()->get_length(); + // iterate through tiles in straight line and look for station koord zv = koord( ribi_t::backward(front()->get_direction()) ); koord3d pos = front()->get_pos(); // start on bridge? pos.z += gr->get_weg_yoff() / TILE_HEIGHT_STEP; - // difference between actual station length and vehicle lenghts - sint16 station_length = -vehicle[vehicles_loading]->get_desc()->get_length(); do { // advance one station tile station_length += CARUNITS_PER_TILE; @@ -6647,13 +6647,10 @@ void convoi_t::check_pending_updates() */ void convoi_t::register_stops() { - if(schedule) - { - FOR(minivec_tpl, const &i, schedule->entries) - { + if( schedule ) { + for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, get_owner()); - if(halt.is_bound()) - { + if( halt.is_bound() ) { halt->add_convoy(self); } } @@ -6667,7 +6664,7 @@ void convoi_t::register_stops() void convoi_t::unregister_stops() { if( schedule ) { - FOR(minivec_tpl, const& i, schedule->entries) { + for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, get_owner()); if( halt.is_bound() ) { halt->remove_convoy(self); @@ -6946,9 +6943,9 @@ class depot_finder_t : public test_driver_t { return master->get_ribi(gr); }; - virtual int get_cost( const grund_t* gr, const sint32, koord from_pos) + virtual int get_cost( const grund_t* gr, const sint32, ribi_t::ribi from) { - return master->get_cost(gr, 0, from_pos); + return master->get_cost(gr, 0, from); }; }; diff --git a/simfab.cc b/simfab.cc index 7af9a9f9485..77d86814132 100644 --- a/simfab.cc +++ b/simfab.cc @@ -315,7 +315,7 @@ void fabrik_t::update_transit( const ware_t& ware, bool add ) // just for simplicity ... void fabrik_t::update_transit_intern( const ware_t& ware, bool add ) { - FOR( array_tpl, &w, input ) { + for(ware_production_t &w : input ) { if( w.get_typ()->get_index() == ware.index ) { w.book_stat_no_negative(add ? (sint64)ware.menge : -(sint64)ware.menge, FAB_GOODS_TRANSIT ); @@ -586,7 +586,7 @@ void fabrik_t::recalc_storage_capacities() // first, for input goods // This minimum storage capacity (set in the pak's simuconf.tab) allows for loading a whole (small) truck. uint32 min_in = welt->get_settings().get_minimum_industry_input_storage_raw(); - FOR(array_tpl, &g, input) { + for(ware_production_t & g : input) { if(const factory_supplier_desc_t *const input = desc->get_supplier(g.get_typ())){ // Inputs are now normalized to factory production. uint32 prod_factor = welt->get_settings().using_fab_contracts() ? 256 : input->get_consumption(); @@ -599,7 +599,7 @@ void fabrik_t::recalc_storage_capacities() // then, for output goods // This minimum storage capacity (set in the pak's simuconf.tab) allows for loading a whole (small) truck. uint32 min_out = welt->get_settings().get_minimum_industry_output_storage_raw(); - FOR(array_tpl, &g, output) { + for(ware_production_t & g : output) { if(const factory_product_desc_t *const output = desc->get_product(g.get_typ())){ // Outputs are now normalized to factory production. uint32 prod_factor = welt->get_settings().using_fab_contracts() ? 256 : output->get_factor(); @@ -616,7 +616,7 @@ void fabrik_t::recalc_storage_capacities() // first, for input goods // This minimum storage capacity (set in the pak's simuconf.tab) allows for loading a whole (small) truck. uint32 min_in = welt->get_settings().get_minimum_industry_input_storage_raw(); - FOR(array_tpl, &g, input) { + for(ware_production_t & g : input) { if(const factory_supplier_desc_t *const input = desc->get_supplier(g.get_typ())){ // Inputs are now normalized to factory production. uint32 prod_factor = welt->get_settings().using_fab_contracts() ? 256 : input->get_consumption(); @@ -629,7 +629,7 @@ void fabrik_t::recalc_storage_capacities() // then, for output goods // This minimum storage capacity (set in the pak's simuconf.tab) allows for loading a whole (small) truck. uint32 min_out = welt->get_settings().get_minimum_industry_output_storage_raw(); - FOR(array_tpl, &g, output) { + for(ware_production_t & g : output) { if(const factory_product_desc_t *const output = desc->get_product(g.get_typ())){ // Outputs are now normalized to factory production. uint32 prod_factor = welt->get_settings().using_fab_contracts() ? 256 : output->get_factor(); @@ -975,7 +975,7 @@ fabrik_t::fabrik_t(koord3d pos_, player_t* owner, const factory_desc_t* desc, si recalc_storage_capacities(); if (input.empty()) { - FOR(array_tpl, & g, output) { + for(ware_production_t & g : output) { if (g.max > 0) { // if source then start with full storage, so that AI will build line(s) immediately g.menge = g.max - 1; @@ -1952,7 +1952,7 @@ sint32 fabrik_t::get_input_stock(const goods_desc_t *ware) { sint32 menge = -1; - FOR(array_tpl, const& i, input) { + for(ware_production_t const& i : input) { if (ware == i.get_typ()) { menge = i.menge >> precision_bits; break; @@ -1967,7 +1967,7 @@ sint32 fabrik_t::get_output_stock(const goods_desc_t *ware) { sint32 menge = -1; - FOR(array_tpl, const& i, output) { + for(ware_production_t const& i : output) { if (ware == i.get_typ()) { menge = i.menge >> precision_bits; break; @@ -2152,7 +2152,7 @@ bool fabrik_t::out_of_stock_selective() sint32 weight_of_all_items = 0; bool all_out_of_stock = true; - FOR(array_tpl, const& i, input) + for(ware_production_t const& i : input) { if (i.menge <= 0) { @@ -3002,15 +3002,13 @@ void fabrik_t::verteile_waren(const uint32 product) // Auswertung der Ergebnisse // "Evaluation of the results" (Babelfish) - if(!dist_list.empty()) - { + if( !dist_list.empty() ) { distribute_ware_t *best = NULL; // Assume a fixed 1km/h transshipment time of goods to industries. This gives a minimum transfer time // of 15 minutes for each stop at 125m/tile. const uint32 transfer_journey_time_factor = ((uint32)welt->get_settings().get_meters_per_tile() * 6u) * 10u; uint32 current_journey_time; - FOR(vector_tpl, & i, dist_list) - { + for(distribute_ware_t & i : dist_list) { if (!i.nearby_halt.halt.is_bound()) { best = &i; @@ -3716,7 +3714,7 @@ void fabrik_t::recalc_factory_status() uint32 i = 0; if(const uint32 input_count = input.get_count()){ uint32 active_input_count = 0; - FOR(array_tpl, const& j, input) { + for(ware_production_t const& j : input) { if (j.menge >= j.max) { status_ein |= FL_WARE_LIMIT; } @@ -4112,7 +4110,7 @@ void fabrik_t::finish_rd() // now we have a valid storage limit if( welt->get_settings().is_crossconnect_factories() ) { - FOR( vector_tpl, const fab, welt->get_fab_list() ) { + for(fabrik_t* const fab : welt->get_fab_list() ) { fab->add_supplier(this); } } @@ -4209,7 +4207,7 @@ void fabrik_t::rotate90( const sint16 y_size ) for(auto i : get_suppliers()) { i.rotate90(y_size); } - FOR(vector_tpl, & i, fields) { + for(field_data_t & i : fields) { i.location.rotate90(y_size); } @@ -4267,7 +4265,7 @@ void fabrik_t::remove_supplier(koord supplier_pos, fabrik_t* supplier) if( welt->get_settings().get_factory_maximum_intransit_percentage() ) { // set to zero - FOR( array_tpl, &w, input ) { + for(ware_production_t &w : input ) { w.max_transit = 0; } @@ -4283,7 +4281,7 @@ void fabrik_t::add_all_suppliers() const factory_supplier_desc_t *supplier = desc->get_supplier(i); const goods_desc_t *ware = supplier->get_input_type(); - FOR(vector_tpl, const fab, welt->get_fab_list()) { + for(fabrik_t* const fab : welt->get_fab_list()) { // connect to an existing one, if this is an producer if(fab!=this && fab->get_output_stock(ware) > -1) { // add us to this factory @@ -4386,7 +4384,7 @@ slist_tpl *fabrik_t::get_produced_goods() const { slist_tpl *goods = new slist_tpl(); - FOR(array_tpl, const& i, output) { + for(ware_production_t const& i : output ) { goods->append(i.get_typ()); } @@ -4435,8 +4433,7 @@ void fabrik_t::calc_max_intransit_percentages() } uint32 index = 0; - FOR(array_tpl, &w, input) - { + for(ware_production_t &w : input ) { const uint32 lead_time = get_lead_time(w.get_typ()); if(lead_time == UINT32_MAX_VALUE) { @@ -4508,9 +4505,9 @@ sint64 fabrik_t::adjust_consumption_by_passenger_level(sint64 consumed_per_month uint32 fabrik_t::get_total_input_capacity() const { - uint32 i = 0; uint32 capacity_sum = 0; - FORX(array_tpl, const& goods, input, i++) { + for(uint32 i=0; i < input.get_count(); i++) { + ware_production_t const& goods = input[i]; const sint64 pfactor =desc->get_supplier(i) ? (sint64)desc->get_supplier(i)->get_consumption() : 1ll; const uint32 storage_capacity = (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.max * pfactor) >> (fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); @@ -4522,8 +4519,8 @@ uint32 fabrik_t::get_total_input_capacity() const uint32 fabrik_t::get_total_output_capacity() const { uint32 sum = 0; - uint32 i = 0; - FORX(array_tpl, const& goods, output, i++) { + for(uint32 i=0; i < output.get_count(); i++) { + ware_production_t const& goods = output[i]; const sint64 pfactor = (sint64)desc->get_product(i)->get_factor(); sum += (uint32)((FAB_DISPLAY_UNIT_HALF + (sint64)goods.max * pfactor) >> (fabrik_t::precision_bits + DEFAULT_PRODUCTION_FACTOR_BITS)); } @@ -4553,8 +4550,8 @@ void fabrik_t::display_status(sint16 xpos, sint16 ypos) } display_fillbox_wh_clip_rgb(xpos-1, ypos, D_WAITINGBAR_WIDTH*input.get_count()+2, 4, color_idx_to_rgb(COL_DODGER_BLUE-1), true); - int i = 0; - FORX(array_tpl, const& goods, input, i++) { + for(uint32 i=0; i < input.get_count(); i++) { + ware_production_t const& goods = input[i]; if (!desc->get_supplier(i)) { continue; } @@ -4587,8 +4584,8 @@ void fabrik_t::display_status(sint16 xpos, sint16 ypos) } display_fillbox_wh_clip_rgb(xpos-1, ypos, D_WAITINGBAR_WIDTH*output.get_count()+2, 4, color_idx_to_rgb(COL_ORANGE), true); - int i = 0; - FORX(array_tpl, const& goods, output, i++) { + for(uint32 i=0; i < output.get_count(); i++) { + ware_production_t const& goods = output[i]; const sint64 pfactor = (sint64)desc->get_product(i)->get_factor(); const uint32 stock_quantity = (uint32)goods.get_storage(); const uint32 storage_capacity = (uint32)goods.get_capacity(pfactor); diff --git a/simhalt.cc b/simhalt.cc index 1fc47cbea91..77ce4f6d864 100644 --- a/simhalt.cc +++ b/simhalt.cc @@ -641,7 +641,7 @@ haltestelle_t::~haltestelle_t() for(uint8 i = 0; i < max_categories; i++) { if (cargo[i]) { - FOR(vector_tpl, const &w, *cargo[i]) { + for(ware_t const &w : *cargo[i]) { fabrik_t::update_transit(w, false); } delete cargo[i]; @@ -917,7 +917,7 @@ char* haltestelle_t::create_name(koord const k, char const* const typ) if (self.is_bound()) { // first factories (so with same distance, they have priority) int this_distance = 999; - FOR(slist_tpl, const f, get_fab_list()) { + for(fabrik_t* const f : get_fab_list()) { int distance = koord_distance(f->get_pos().get_2d(), k); if( distance < this_distance ) { fabs.insert(f); @@ -939,7 +939,7 @@ char* haltestelle_t::create_name(koord const k, char const* const typ) } // are there fabs? - FOR(slist_tpl, const f, fabs) { + for(fabrik_t* const f : get_fab_list()) { // with factories buf.printf(fab_base, city_name, f->get_name(), stop); if( !all_names.get(buf).is_bound() ) { @@ -1068,13 +1068,13 @@ char* haltestelle_t::create_name(koord const k, char const* const typ) // allow for names without direction uint8 count_s = count_printf_param( base_name ); if(count_s==3) { - if (cbuffer_t::check_format_strings("%s %s %s", base_name) ) { + if (cbuffer_t::check_and_repair_format_strings("%s %s %s", base_name) ) { // ok, try this name, if free ... buf.printf( base_name, city_name, dirname, stop ); } } else { - if (cbuffer_t::check_format_strings("%s %s", base_name) ) { + if (cbuffer_t::check_and_repair_format_strings("%s %s", base_name) ) { // ok, try this name, if free ... buf.printf( base_name, city_name, stop ); } @@ -1126,7 +1126,7 @@ char* haltestelle_t::create_name(koord const k, char const* const typ) // as count % offset != 0 we are guaranteed to test all street names for (uint32 i = 0; igrund->get_pos().get_2d(); koord p0 = p - coverage, p1 = p + coverage; - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { const koord& k = i.grund->get_pos().get_2d(); koord k0 = k - coverage, k1 = k + coverage; if (p0.x > k0.x) p0.x = k0.x; @@ -1690,7 +1690,7 @@ void haltestelle_t::verbinde_fabriken() *ptr++ = 0; // set 1 to koords, that are covered by the halt: - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { const koord& k = i.grund->get_pos().get_2d(); koord k0 = k - coverage, k1 = k + coverage; for (int y = k0.y; y <= k1.y; ++y) { @@ -2389,7 +2389,7 @@ bool haltestelle_t::recall_ware( ware_t& w, uint32 menge ) w.menge = 0; vector_tpl *warray = cargo[w.get_desc()->get_catg_index()]; if(warray!=NULL) { - FOR(vector_tpl, & tmp, *warray) { + for(ware_t & tmp : *warray) { // skip empty entries if(tmp.menge==0 || w.get_index()!=tmp.get_index() || w.get_zielpos()!=tmp.get_zielpos()) { continue; @@ -2906,7 +2906,7 @@ uint32 haltestelle_t::get_ware_summe(const goods_desc_t *wtyp) const int sum = 0; const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { - FOR(vector_tpl, const& i, *warray) { + for(ware_t const& i : *warray) { if (wtyp->get_index() == i.get_index()) { sum += i.menge; } @@ -2923,7 +2923,7 @@ uint32 haltestelle_t::get_ware_summe(const goods_desc_t *wtyp, uint8 wealth_clas int sum = 0; const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if (warray != NULL) { - FOR(vector_tpl, const& i, *warray) { + for(ware_t const& i : *warray) { if (wtyp->get_index() == i.get_index() && wealth_class == i.get_class() && (!chk_only_commuter || (chk_only_commuter && wtyp == goods_manager_t::passengers && i.is_commuting_trip))) { sum += i.menge; @@ -2938,7 +2938,7 @@ uint32 haltestelle_t::get_ware_summe(const goods_desc_t *wtyp, linehandle_t line int sum = 0; const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { - FOR(vector_tpl, const& i, *warray) { + for(ware_t const& i : *warray) { if (wtyp->get_index() == i.get_index()) { if (wealth_class !=255 && wealth_class != i.get_class()) { continue; @@ -3052,7 +3052,7 @@ uint32 haltestelle_t::get_ware_fuer_zielpos(const goods_desc_t *wtyp, const koor { const vector_tpl * warray = cargo[wtyp->get_catg_index()]; if(warray!=NULL) { - FOR(vector_tpl, const& ware, *warray) { + for(ware_t const& ware : *warray) { if(wtyp->get_index()==ware.get_index() && ware.get_zielpos()==zielpos) { return ware.menge; } @@ -3069,8 +3069,7 @@ bool haltestelle_t::vereinige_waren(const ware_t &ware) //"unite were" (Google) vector_tpl * warray = cargo[ware.get_desc()->get_catg_index()]; if(warray != NULL) { - FOR(vector_tpl, & tmp, *warray) - { + for(ware_t & tmp : *warray) { /* * OLD SYSTEM - did not take account of origins and timings when merging. @@ -3138,7 +3137,7 @@ void haltestelle_t::add_ware_to_halt(ware_t ware, bool from_saved) if(!from_saved) { // the ware will be put into the first entry with menge==0 - FOR(vector_tpl, & i, *warray) { + for(ware_t & i : *warray) { if (i.menge == 0) { i = ware; return; @@ -3350,8 +3349,7 @@ void haltestelle_t::liefere_an(ware_t ware, uint8 walked_between_stations) if(!ware.is_freight()) { // Not a factory: must check manually. - FOR(slist_tpl, const& t, tiles) - { + for (tile_t const& t : tiles) { gebaeude_t* check_building = t.grund->get_building(); if(check_building->is_same_building(gb)) { @@ -3514,10 +3512,8 @@ void haltestelle_t::show_detail() sint64 haltestelle_t::calc_maintenance() const { sint64 maintenance = 0; - FOR(slist_tpl, const& i, tiles) - { - if (gebaeude_t* const gb = i.grund->find()) - { + for(tile_t const& i : tiles ) { + if( gebaeude_t* const gb = i.grund->find() ) { const building_desc_t* desc = gb->get_tile()->get_desc(); if(desc->get_base_maintenance() == PRICE_MAGIC) { @@ -3547,8 +3543,7 @@ bool haltestelle_t::make_public_and_join(player_t *player) { // First run through to see if we can afford this. sint64 total_charge = 0; - FOR(slist_tpl, const& i, tiles) - { + for(tile_t const& i : tiles) { grund_t* const gr = i.grund; gebaeude_t* gb = gr->find(); if(gb) @@ -3586,8 +3581,7 @@ bool haltestelle_t::make_public_and_join(player_t *player) // Now run through to actually transfer ownership // and recalculate maintenance; must do maintenance here, not above, // in order to properly assign it by waytype - FOR(slist_tpl, const& i, tiles) - { + for(tile_t const& i : tiles) { grund_t* const gr = i.grund; gebaeude_t* gb = gr->find(); if(gb) @@ -3737,7 +3731,7 @@ void haltestelle_t::transfer_goods(halthandle_t halt) for(uint8 i=0; i * warray = cargo[i]; if (warray) { - FOR(vector_tpl, const& j, *warray) { + for(ware_t const& j : *warray) { halt->add_ware_to_halt(j); } delete cargo[i]; @@ -3863,12 +3857,12 @@ void haltestelle_t::recalc_station_type() station_type = invalid; // iterate over all tiles - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { grund_t* const gr = i.grund; add_to_station_type( gr ); } // and set halt info again - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { i.grund->set_halt( self ); } recalc_status(); @@ -3987,7 +3981,7 @@ void haltestelle_t::rdwr(loadsave_t *file) } } else { - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { k = i.grund->get_pos(); k.rdwr( file ); } @@ -4039,8 +4033,7 @@ void haltestelle_t::rdwr(loadsave_t *file) file->rdwr_long(count); has_uint16_count = false; } - FOR(vector_tpl, & ware, *warray) - { + for(ware_t & ware : *warray) { if(has_uint16_count && ware_count++ > 65535) { // Discard ware packets > 65535 if the version is < 11, as trying @@ -4786,8 +4779,7 @@ void haltestelle_t::finish_rd(bool need_recheck_for_walking_distance) if(cargo[i]) { vector_tpl * warray = cargo[i]; - FOR(vector_tpl, & j, *warray) - { + for(ware_t & j : *warray) { j.finish_rd(welt); } // merge identical entries (should only happen with old games) @@ -5436,13 +5428,10 @@ bool haltestelle_t::add_grund(grund_t *gr, bool relink_factories, bool recalc_ne uint8 const pl_min = public_halt ? 0 : get_owner()->get_player_nr(); uint8 const pl_max = public_halt ? MAX_PLAYER_COUNT : get_owner()->get_player_nr() + 1; // iterate over all lines (public halt: all lines, other: only player's lines) - for(uint8 i = pl_min; i < pl_max; i++) - { - if(player_t *player = welt->get_player(i)) - { + for( uint8 i=pl_min; iget_player(i) ) { player->simlinemgmt.get_lines(simline_t::line, &check_line); - FOR(vector_tpl, const j, check_line) - { + for(linehandle_t const j : check_line ) { // only add unknown lines if( !registered_lines.is_contained(j) && j->count_convoys() > 0 ) { FOR( minivec_tpl, const& k, j->get_schedule()->entries ) { @@ -5456,12 +5445,12 @@ bool haltestelle_t::add_grund(grund_t *gr, bool relink_factories, bool recalc_ne } } // iterate over all convoys - FOR(vector_tpl, const cnv, welt->convoys()) { + for(convoihandle_t const cnv : welt->convoys()) { // only check lineless convoys which have matching ownership and which are not yet registered if( !cnv->get_line().is_bound() && (public_halt || cnv->get_owner()==get_owner()) && !registered_convoys.is_contained(cnv) ) { if( const schedule_t *const schedule = cnv->get_schedule() ) { - FOR(minivec_tpl, const& k, schedule->entries) { - if (get_halt(k.pos, cnv->get_owner()) == self) { + for(schedule_entry_t const& k : schedule->entries ) { + if( get_halt(k.pos, cnv->get_owner()) == self ) { registered_convoys.append(cnv); break; } @@ -5605,7 +5594,7 @@ bool haltestelle_t::rem_grund(grund_t *gr) // remove lines eventually for( size_t j = registered_lines.get_count(); j-- != 0; ) { bool ok = false; - FOR( minivec_tpl, const& k, registered_lines[j]->get_schedule()->entries ) { + for(schedule_entry_t const& k : registered_lines[j]->get_schedule()->entries ) { if( get_halt(k.pos, registered_lines[j]->get_owner()) == self ) { ok = true; break; @@ -5621,7 +5610,7 @@ bool haltestelle_t::rem_grund(grund_t *gr) // remove registered lineless convoys as well for( size_t j = registered_convoys.get_count(); j-- != 0; ) { bool ok = false; - FOR( minivec_tpl, const& k, registered_convoys[j]->get_schedule()->entries ) { + for(schedule_entry_t const& k : registered_convoys[j]->get_schedule()->entries ) { if( get_halt(k.pos, registered_convoys[j]->get_owner()) == self ) { ok = true; break; @@ -5662,7 +5651,7 @@ koord haltestelle_t::get_next_pos( koord start, bool square ) const if (!tiles.empty()) { // find the closest one sint32 dist = 0x7FFF; - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { koord const p = i.grund->get_pos().get_2d(); sint32 d; if(square) @@ -5692,7 +5681,7 @@ void haltestelle_t::mark_unmark_coverage(const bool mark, const bool factories) // iterate over all tiles uint16 const cov = factories ? welt->get_settings().get_station_coverage_factories() : welt->get_settings().get_station_coverage(); koord const size(cov * 2 + 1, cov * 2 + 1); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { welt->mark_area(i.grund->get_pos() - size / 2, size, mark); } } @@ -5705,7 +5694,7 @@ uint32 haltestelle_t::get_around_population(uint8 g_class) const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5732,7 +5721,7 @@ uint32 haltestelle_t::get_around_visitor_demand(uint8 g_class) const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5759,7 +5748,7 @@ uint32 haltestelle_t::get_around_job_demand(uint8 g_class) const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5786,7 +5775,7 @@ uint32 haltestelle_t::get_around_visitor_generated() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5813,7 +5802,7 @@ uint32 haltestelle_t::get_around_succeeded_visiting() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5841,7 +5830,7 @@ uint32 haltestelle_t::get_around_commuter_generated() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5868,7 +5857,7 @@ uint32 haltestelle_t::get_around_succeeded_commuting() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5896,7 +5885,7 @@ uint32 haltestelle_t::get_around_employee_factor() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5923,7 +5912,7 @@ uint32 haltestelle_t::get_around_mail_demand() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5950,7 +5939,7 @@ uint32 haltestelle_t::get_around_mail_generated() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -5977,7 +5966,7 @@ uint32 haltestelle_t::get_around_mail_delivery_succeeded() const koord ul(32767, 32767); koord lr(0, 0); - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { ul.clip_max(i.grund->get_pos().get_2d()); lr.clip_min(i.grund->get_pos().get_2d()); } @@ -6004,7 +5993,7 @@ uint32 haltestelle_t::get_around_mail_delivery_succeeded() const const grund_t *haltestelle_t::find_matching_position(const waytype_t w) const { // iterate over all tiles - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { if (i.grund->hat_weg(w)) { return i.grund; } @@ -6021,7 +6010,7 @@ bool haltestelle_t::find_free_position(const waytype_t w,convoihandle_t cnv,cons // iterate over all tiles // for road, we have to consider passing lane. if( w==road_wt ) { - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { if( !i.reservation[0].is_bound() || !i.reservation[1].is_bound() ) { // possibly there is empty slots. grund_t* const gr = i.grund; @@ -6035,7 +6024,7 @@ bool haltestelle_t::find_free_position(const waytype_t w,convoihandle_t cnv,cons return false; } // for other waytypes... - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { if (i.reservation[0] == cnv || !i.reservation[0].is_bound()) { // not reserved grund_t* const gr = i.grund; @@ -6117,7 +6106,7 @@ DBG_MESSAGE("haltestelle_t::unreserve_position()","failed for gr=%p",gr); */ bool haltestelle_t::is_reservable(const grund_t *gr, convoihandle_t cnv) const { - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { if (gr == i.grund) { if (i.reservation[0] == cnv) { DBG_MESSAGE("haltestelle_t::is_reservable()","gr=%d,%d already reserved by cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id()); @@ -6148,7 +6137,7 @@ DBG_MESSAGE("haltestelle_t::reserve_position()","failed for gr=%i,%i, cnv=%d",gr * @author THLeaderH */ uint8 haltestelle_t::get_empty_lane(const grund_t *gr, convoihandle_t cnv) const { - FOR(slist_tpl, const& i, tiles) { + for(tile_t const& i : tiles) { if ( gr == i.grund ) { if ( i.reservation[0] == cnv ) { // already reserved the traffic lane. @@ -6263,8 +6252,7 @@ void haltestelle_t::remove_halt_within_walking_distance(halthandle_t halt) void haltestelle_t::check_nearby_halts() { halts_within_walking_distance.clear(); - FOR(slist_tpl, const& iter, tiles) - { + for(tile_t const& iter : tiles) { planquadrat_t *plan = welt->access(iter.grund->get_pos().get_2d()); if(plan) { @@ -6368,8 +6356,7 @@ void haltestelle_t::calc_transfer_time() koord ul(32767,32767); koord lr(0,0); koord pos; - FOR(slist_tpl, const& tile, tiles) - { + for(tile_t const& tile : tiles) { pos = tile.grund->get_pos().get_2d(); // First time through, this sets ul / lr to pos. @@ -6688,8 +6675,7 @@ void haltestelle_t::set_all_building_tiles() if (!tiles.empty()) { - FOR(slist_tpl, const& i, tiles) - { + for(tile_t const& i : tiles) { gebaeude_t* building = i.grund->get_building(); if(building) { diff --git a/siminteraction.cc b/siminteraction.cc index 8f586ba9d61..28730721960 100644 --- a/siminteraction.cc +++ b/siminteraction.cc @@ -202,7 +202,7 @@ void interaction_t::interactive_event( const event_t &ev ) default: { bool ok=false; - FOR(vector_tpl, const i, tool_t::char_to_tool) { + for(tool_t* const i : tool_t::char_to_tool) { if( i->command_key == ev.ev_code ) { if( i->command_flags == 0 || (ev.ev_key_mod & (SIM_MOD_SHIFT|SIM_MOD_CTRL)) == i->command_flags ) { world->set_tool(i, world->get_active_player()); diff --git a/simintr.cc b/simintr.cc index 364f46212fa..dba1667d66d 100644 --- a/simintr.cc +++ b/simintr.cc @@ -23,13 +23,13 @@ static main_view_t *main_view = NULL; -static long last_time; +static uint32 last_time; static bool enabled = false; #define FRAME_TIME_MULTI (16) // pause between two frames -static long frame_time = 36*FRAME_TIME_MULTI; +static uint32 frame_time = 36*FRAME_TIME_MULTI; bool reduce_frame_time() @@ -60,11 +60,13 @@ bool increase_frame_time() } } -sint32 get_frame_time() + +uint32 get_frame_time() { return frame_time/FRAME_TIME_MULTI; } + void set_frame_time(uint32 time) { frame_time = clamp( time, 1000/env_t::max_fps, 1000/env_t::min_fps )*FRAME_TIME_MULTI; @@ -108,8 +110,9 @@ void interrupt_check(const char* caller_info) if( diff>0 ) { enabled = false; last_time = now; - world()->sync_step( diff, !world()->is_fast_forward(), true ); - // since pause may have been activated in this sync_step + world()->sync_step(diff, !world()->is_fast_forward(), true); + + // since pause maz have been activated in this sync_step enabled = !world()->is_paused(); } } @@ -129,6 +132,7 @@ void intr_set_last_time(sint32 time) last_time = time; } + void intr_disable() { enabled = false; @@ -140,6 +144,7 @@ void intr_enable() enabled = true; } + char const *tick_to_string( sint64 ticks, bool show_full ) { static sint32 tage_per_month[12]={31,28,31,30,31,30,31,31,30,31,30,31}; @@ -310,3 +315,100 @@ char const *tick_to_string( sint64 ticks, bool show_full ) } return time; } + + + +char const *difftick_to_string( sint32 ticks, bool round_to_quaters ) +{ + static char time [128]; + + time[0] = 0; + + // World model might not be initalized if this is called while reading saved windows. + if ( world() == NULL) { + return time; + } + + // suppress as much as possible, assuming this is an relative offset to the current month + sint32 num_days = ( ticks * (env_t::show_month==env_t::DATE_FMT_MONTH? 1 : 31) ) >> world()->ticks_per_world_month_shift; + char days[64]; + days[0] = 0; + if( num_days!=0 ) { + sprintf( days, "%+i ", num_days ); + } + + uint32 hours, minuten; + if( env_t::show_month > env_t::DATE_FMT_MONTH ) { + if( world()->ticks_per_world_month_shift>=16 ) { + hours = (((sint64)ticks*31) >> (world()->ticks_per_world_month_shift-16)); + } + else { + hours = (((sint64)ticks*31) << (16-world()->ticks_per_world_month_shift)); + } + } + else { + if( world()->ticks_per_world_month_shift>=16 ) { + uint32 precision = round_to_quaters ? 0 : 15; + hours = (ticks >> (world()->ticks_per_world_month_shift-16)) + precision; + } + else { + uint32 precision = round_to_quaters ? 0 : 15 >> (16-world()->ticks_per_world_month_shift); + hours = (((sint64)ticks) << (16-world()->ticks_per_world_month_shift)) + precision; + } + } + minuten = (((hours * 3) % 8192) * 60) / 8192; + hours = ((hours * 3) / 8192) % 24; + + // maybe round minutes + if( round_to_quaters ) { + int switchtick = world()->ticks_per_world_month_shift; + if( env_t::show_month == env_t::DATE_FMT_MONTH ) { + // since a month is then just three days instead of about 30 ... + switchtick += 3; + } + if( switchtick <= 19 ) { + minuten = ( (minuten + 30 ) / 60 ) * 60; + hours += minuten /60; + if( switchtick < 18 ) { + // four hour intervals + hours = (hours + 3 ) & 0xFFFFC; + } + else if( switchtick == 18 ) { + // two hour intervals + hours = (hours + 1 ) & 0xFFFFE; + } + } + else if( switchtick == 20 ) { + minuten = ( (minuten + 15) / 30 ) * 30; + } + else if( switchtick == 21 ) { + minuten = ( (minuten + 7) / 15 ) * 15; + } + else if( switchtick == 22 ) { + minuten = ( (minuten + 2) / 5 ) * 5; + } + } + // take care of overflow + hours += (minuten / 60); + minuten %= 60; + hours %= 24; + + switch(env_t::show_month) { + case env_t::DATE_FMT_GERMAN: + case env_t::DATE_FMT_GERMAN_NO_SEASON: + case env_t::DATE_FMT_US: + case env_t::DATE_FMT_US_NO_SEASON: + sprintf(time, "%s%2d:%02dh", days, hours, minuten); + break; + + case env_t::DATE_FMT_JAPANESE: + case env_t::DATE_FMT_JAPANESE_NO_SEASON: + sprintf(time, "%s%2d:%02dh", days, hours, minuten); + break; + + case env_t::DATE_FMT_MONTH: + sprintf(time, "%s%2d:%02dh", days, hours, minuten); + break; + } + return time; +} diff --git a/simintr.h b/simintr.h index e0c096fa4a3..581e997b140 100644 --- a/simintr.h +++ b/simintr.h @@ -18,7 +18,8 @@ bool reduce_frame_time(); /// Try to decrease fps bool increase_frame_time(); -sint32 get_frame_time(); + +uint32 get_frame_time(); void set_frame_time(uint32 ms); @@ -47,9 +48,15 @@ void interrupt_check(const char* caller_info = "0"); #define INT_CHECK(info) do { interrupt_check( __FILE__ ":" QUOTEME(__LINE__) ); } while(false) #endif #endif -#endif - // returns a time string in the desired format // Returns an empty string if called before the world model defining time is initalized. char const *tick_to_string( sint64 ticks, bool show_full ); + + +// returns a time difference string in the desired format +// assume the month has 31 days +// round will reduce presion to qrater or less (depending on month length) +char const *difftick_to_string( sint32 ticks, bool round ); + +#endif diff --git a/simline.cc b/simline.cc index cb9202dcef1..d5117e3dc9d 100644 --- a/simline.cc +++ b/simline.cc @@ -644,7 +644,7 @@ void simline_t::finish_rd() void simline_t::register_stops(schedule_t * schedule) { DBG_DEBUG("simline_t::register_stops()", "%d schedule entries in schedule %p", schedule->get_count(),schedule); - FOR(minivec_tpl,const &i, schedule->entries) { + for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, player); if(halt.is_bound()) { //DBG_DEBUG("simline_t::register_stops()", "halt not null"); @@ -673,8 +673,7 @@ void simline_t::unregister_stops() // It is necessary to clear all departure data, // which might be out of date on a change of schedule. - FOR(vector_tpl, & i, line_managed_convoys) - { + for(convoihandle_t const &i : line_managed_convoys) { i->clear_departures(); } financial_history[0][LINE_DEPARTURES_SCHEDULED] = calc_departures_scheduled(); @@ -683,7 +682,7 @@ void simline_t::unregister_stops() void simline_t::unregister_stops(schedule_t * schedule) { - FOR(minivec_tpl, const& i, schedule->entries) { + for(schedule_entry_t const& i : schedule->entries) { halthandle_t const halt = haltestelle_t::get_halt(i.pos, player); if(halt.is_bound()) { halt->remove_line(self); @@ -728,7 +727,7 @@ void simline_t::set_line_color(uint8 color_idx, uint8 style) void simline_t::check_freight() { - FOR(vector_tpl, const i, line_managed_convoys) { + for(convoihandle_t const i : line_managed_convoys) { i->check_freight(); } } @@ -790,8 +789,7 @@ void simline_t::recalc_status() { const uint16 month_now = welt->get_timeline_year_month(); - FOR(vector_tpl, const i, line_managed_convoys) - { + for(convoihandle_t const i : line_managed_convoys) { for (uint16 j = 0; j < i->get_vehicle_count(); j++) { vehicle_t *v = i->get_vehicle(j); @@ -926,8 +924,7 @@ void simline_t::calc_classes_carried() passenger_classes_carried.clear(); mail_classes_carried.clear(); - FOR(vector_tpl, const i, line_managed_convoys) - { + for(convoihandle_t const i : line_managed_convoys) { convoi_t const& cnv = *i; if (cnv.get_goods_catg_index().is_contained(goods_manager_t::INDEX_PAS)) @@ -1019,7 +1016,7 @@ void simline_t::recalc_catg_index() convoi_t const& cnv = *i; withdraw &= cnv.get_withdraw(); - FOR(minivec_tpl, const catg_index, cnv.get_goods_catg_index()) { + for(uint8 const catg_index : cnv.get_goods_catg_index()) { goods_catg_index.append_unique( catg_index ); } } @@ -1057,8 +1054,7 @@ void simline_t::recalc_catg_index() } // added categories : present in new category list but not in old category list - FOR(minivec_tpl, const i, goods_catg_index) - { + for(uint8 const i : goods_catg_index) { if (!old_goods_catg_index.is_contained(i)) { catg_differences.append(i); diff --git a/simlinemgmt.cc b/simlinemgmt.cc index 555515f0b82..5e958f93e87 100644 --- a/simlinemgmt.cc +++ b/simlinemgmt.cc @@ -101,7 +101,7 @@ void simlinemgmt_t::rdwr(loadsave_t *file, player_t *player) uint32 count = all_managed_lines.get_count(); file->rdwr_long(count); - FOR(vector_tpl, const i, all_managed_lines) { + for(linehandle_t const i : all_managed_lines) { simline_t::linetype lt = i->get_linetype(); file->rdwr_enum(lt); i->rdwr(file); @@ -180,7 +180,7 @@ void simlinemgmt_t::sort_lines() void simlinemgmt_t::finish_rd() { - FOR(vector_tpl, const i, all_managed_lines) { + for(linehandle_t const i : all_managed_lines) { i->finish_rd(); } sort_lines(); @@ -189,7 +189,7 @@ void simlinemgmt_t::finish_rd() void simlinemgmt_t::rotate90( sint16 y_size ) { - FOR(vector_tpl, const i, all_managed_lines) { + for(linehandle_t const i : all_managed_lines) { if (schedule_t* const schedule = i->get_schedule()) { schedule->rotate90( y_size ); } @@ -199,7 +199,7 @@ void simlinemgmt_t::rotate90( sint16 y_size ) void simlinemgmt_t::new_month() { - FOR(vector_tpl, const i, all_managed_lines) { + for(linehandle_t const i : all_managed_lines) { i->new_month(); } } @@ -232,7 +232,7 @@ linehandle_t simlinemgmt_t::create_line(int ltype, player_t * player, schedule_t void simlinemgmt_t::get_lines(int type, vector_tpl* lines, uint8 freight_type_bits, bool show_empty_line) const { lines->clear(); - FOR(vector_tpl, const line, all_managed_lines) { + for(linehandle_t const line : all_managed_lines) { if (type == simline_t::line || line->get_linetype() == simline_t::line || line->get_linetype() == type) { if (!show_empty_line && !line->get_convoys().get_count()) { continue; diff --git a/simmain.cc b/simmain.cc index a194e1b3c6b..c3b361a97da 100644 --- a/simmain.cc +++ b/simmain.cc @@ -60,6 +60,7 @@ #include "dataobj/loadsave.h" #include "dataobj/environment.h" #include "dataobj/tabfile.h" +#include "dataobj/scenario.h" #include "dataobj/settings.h" #include "dataobj/translator.h" #include "network/pakset_info.h" @@ -181,7 +182,7 @@ static void show_times(karte_t *welt, main_view_t *view) ms = dr_time(); for (i = 0; i < 40000000/(int)weg_t::get_alle_wege().get_count(); i++) { - FOR( vector_tpl, const w, weg_t::get_alle_wege() ) { + for(weg_t * const w : weg_t::get_alle_wege() ) { grund_t *dummy; welt->lookup( w->get_pos() )->get_neighbour( dummy, invalid_wt, ribi_t::north ); } @@ -815,7 +816,7 @@ int simu_main(int argc, char** argv) } // will fail fatal on the opening routine ... dbg->message( "simu_main()", "Server started on port %i", env_t::server_port ); - env_t::networkmode = network_init_server( env_t::server_port ); + env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); // query IP and try to open ports on router char IP[256], altIP[256]; altIP[0] = 0; @@ -842,7 +843,7 @@ int simu_main(int argc, char** argv) } // will fail fatal on the opening routine ... dbg->message( "simu_main()", "Server started on port %i", env_t::server_port ); - env_t::networkmode = network_init_server( env_t::server_port ); + env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); } else { // no announce for clients ... @@ -1551,11 +1552,34 @@ int simu_main(int argc, char** argv) setsimrand(dr_time(), dr_time()); clear_random_mode( 7 ); // allow all - if( loadgame=="" || !welt->load(loadgame.c_str()) ) { - // create a default map - DBG_MESSAGE("simu_main()", "Init with default map (failing will be a pak error!)"); + scenario_t *scen = NULL; + if( const char *scen_name = args.gimme_arg("-scenario", 1) ) { + scen = new scenario_t(welt); - // no autosave on initial map during the first six month ... + intr_set_view(view); + win_set_world(welt); + + const char *err = ""; + if (env_t::default_settings.get_with_private_paks()) { + // try addon directory first + err = scen->init(("addons/" + env_t::objfilename + "scenario/").c_str(), scen_name, welt); + } + if (err) { + // no addon scenario, look in pakset + err = scen->init((env_t::data_dir + env_t::objfilename + "scenario/").c_str(), scen_name, welt); + } + if( err ) { + dbg->error("simu_main()", "Could not load scenario %s%s: %s", env_t::objfilename.c_str(), scen_name, err); + delete scen; + scen = NULL; + } + else { + new_world = false; + } + } + + if( scen == NULL && (loadgame=="" || !welt->load(loadgame.c_str())) ) { + // no autosave on initial map during the first six months loadgame = ""; new_world = true; diff --git a/simmenu.cc b/simmenu.cc index 12eaa6c95a0..2aa2c7903f5 100644 --- a/simmenu.cc +++ b/simmenu.cc @@ -1037,7 +1037,7 @@ void tool_t::update_toolbars() // iterate twice, to get correct icons if a toolbar changes between empty and non-empty for(uint j=0; j<2; j++) { bool change = false; - FOR(vector_tpl, const i, toolbar_tool) { + for(toolbar_t* const i : toolbar_tool) { bool old_icon_empty = i->get_icon(welt->get_active_player()) == IMG_EMPTY; i->update(welt->get_active_player()); change |= old_icon_empty ^ (i->get_icon(welt->get_active_player()) == IMG_EMPTY); @@ -1149,7 +1149,7 @@ void toolbar_t::update(player_t *player) tool_selector->reset_tools(); // now (re)fill it - FOR(slist_tpl, const w, tools) { + for(tool_t* const w : tools) { // no way to call this tool? => then it is most likely a metatool if(w->command_key==1 && w->get_icon(player)==IMG_EMPTY) { diff --git a/simmenu.h b/simmenu.h index e8e23a586f4..ba10e1d782d 100644 --- a/simmenu.h +++ b/simmenu.h @@ -242,6 +242,9 @@ class tool_t { sint16 ok_sound; + /// a script is waiting for a call-back + uint32 callback_id; + enum { WFL_SHIFT = 1 << 0, ///< shift-key was pressed when mouse-click happened WFL_CTRL = 1 << 1, ///< ctrl-key was pressed when mouse-click happened @@ -289,6 +292,7 @@ class tool_t { command_key = 0; cursor_centered = false; flags = 0; + callback_id = 0; } virtual ~tool_t() {} diff --git a/simmesg.cc b/simmesg.cc index 3160b5ef1c8..52a266220e8 100644 --- a/simmesg.cc +++ b/simmesg.cc @@ -130,7 +130,7 @@ DBG_MESSAGE("message_t::add_msg()","%40s (at %i,%i)", text, pos.x, pos.y ); if( what_bit == (1<get_current_month()-2; uint32 i = 0; - FOR(slist_tpl, const iter, list) { + for(node* const iter : list) { node const& n = *iter; if (n.time >= now && strcmp(n.msg, text) == 0 && @@ -146,7 +146,7 @@ DBG_MESSAGE("message_t::add_msg()","%40s (at %i,%i)", text, pos.x, pos.y ); // filter out AI messages for a similar area to recent activity messages if( what_bit == (1<, const iter, list) { + for(node* const iter : list) { node const& n = *iter; if ((n.pos.x & 0xFFE0) == (pos.x & 0xFFE0) && (n.pos.y & 0xFFE0) == (pos.y & 0xFFE0)) { @@ -232,7 +232,7 @@ koord message_t::get_coord_from_text(const char* text) void message_t::rotate90( sint16 size_w ) { - FOR(slist_tpl, const i, list) { + for(node* const i : list) { i->pos.rotate90(size_w); } } @@ -246,14 +246,14 @@ void message_t::rdwr( loadsave_t *file ) if( env_t::server ) { // do not save local messages and expired messages uint32 current_time = world()->get_current_month(); - FOR(slist_tpl, const i, list) { + for(node* const i : list) { if( i->type & do_not_rdwr_flag || (i->type & expire_after_one_month_flag && current_time - i->time > 1) ) { continue; } if (++msg_count == MAX_SAVED_MESSAGES) break; } file->rdwr_short( msg_count ); - FOR(slist_tpl, const i, list) { + for(node* const i : list) { if (msg_count == 0) break; if( i->type & do_not_rdwr_flag || (i->type & expire_after_one_month_flag && current_time - i->time > 1) ) { continue; @@ -264,13 +264,13 @@ void message_t::rdwr( loadsave_t *file ) } else { // do not save discardable messages (like changing player) - FOR(slist_tpl, const i, list) { + for(node* const i : list) { if (!(i->type & do_not_rdwr_flag)) { if (++msg_count == MAX_SAVED_MESSAGES) break; } } file->rdwr_short( msg_count ); - FOR(slist_tpl, const i, list) { + for(node* const i : list) { if (msg_count == 0) break; if( !(i->type & do_not_rdwr_flag) ) { i->rdwr(file); diff --git a/simskin.cc b/simskin.cc index 79e7bc8b273..8def91c88d3 100644 --- a/simskin.cc +++ b/simskin.cc @@ -254,7 +254,7 @@ bool skinverwaltung_t::register_desc(skintyp_t type, const skin_desc_t* desc) // return the extra_obj with this name const skin_desc_t *skinverwaltung_t::get_extra( const char *str, int len ) { - FOR(slist_tpl, const s, skinverwaltung_t::extra_obj) { + for(skin_desc_t const* const s : skinverwaltung_t::extra_obj) { if (strncmp(str, s->get_name(), len) == 0) { return s; } diff --git a/simticker.cc b/simticker.cc index 7dab3b826d5..140fa39fe3c 100644 --- a/simticker.cc +++ b/simticker.cc @@ -117,7 +117,7 @@ void ticker::update() const int dx = X_DIST; const int display_width = display_get_width(); - FOR(slist_tpl, & n, list) { + for(node & n : list) { n.xpos -= dx; if (n.xpos < display_width) { @@ -165,7 +165,7 @@ void ticker::draw() // ok, ready for the text PUSH_CLIP( 0, start_y, width - 1, TICKER_HEIGHT ); - FOR(slist_tpl, & n, list) { + for(node & n : list) { if (n.xpos < width) { display_proportional_clip_rgb(n.xpos, start_y + TICKER_V_SPACE, n.msg, ALIGN_LEFT, n.color, true); } @@ -192,7 +192,7 @@ void ticker::redraw() // just draw the ticker in its colour ... (to be sure ... ) display_fillbox_wh_rgb(0, start_y, width, TICKER_HEIGHT, SYSCOL_TICKER_BACKGROUND, true); - FOR(slist_tpl, & n, list) { + for(node & n : list) { if (n.xpos < width) { display_proportional_clip_rgb(n.xpos, start_y + TICKER_V_SPACE, n.msg, ALIGN_LEFT, n.color, true); } diff --git a/simtool.cc b/simtool.cc index 0d9a9eae01d..16e0c939fa8 100644 --- a/simtool.cc +++ b/simtool.cc @@ -819,7 +819,7 @@ DBG_MESSAGE("tool_remover()", "took out powerline"); if(zeiger) { gr->obj_remove(zeiger); } - // do not delete other players label + // do not delete other players labelcr label_t *label = gr->find(); if(label) { gr->obj_remove(label); @@ -852,7 +852,7 @@ DBG_MESSAGE("tool_remover()", "took out powerline"); // only delete everything if there is more than pedestrians to delete if (num_obj > num_pedestrians) { msg = gr->kann_alle_obj_entfernen(player); - return_ok = msg==NULL && !(gr->get_typ()==grund_t::brueckenboden || gr->get_typ()==grund_t::tunnelboden); + return_ok = (msg == NULL && !(gr->get_typ() == grund_t::brueckenboden || gr->get_typ() == grund_t::tunnelboden) && gr->obj_loesche_alle(player)); DBG_MESSAGE("tool_remover()", "removing everything from %d,%d,%d", gr->get_pos().x, gr->get_pos().y, gr->get_pos().z); } } @@ -1145,7 +1145,7 @@ void tool_path_remover_t::tile_mark(player_t *, const koord3d &pos, const koord3 const char * tool_flatten_path_t::tile_work(player_t *player, const koord3d &pos, const koord3d &start){ if(is_shift_pressed()){ - tool_setslope_t::tool_set_slope_work(player,koord3d(pos.get_2d(),welt->lookup_hgt(pos.get_2d())),RESTORE_SLOPE); + tool_setslope_t::tool_set_slope_work(player,koord3d(pos.get_2d(), welt->lookup_hgt(pos.get_2d())), RESTORE_SLOPE); } int n=0; @@ -1465,7 +1465,7 @@ const char *tool_restoreslope_t::check_pos( player_t *, koord3d pos) return NULL; } -const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, int new_slope ) +const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, int new_slope) { if( !ground_desc_t::double_grounds ) { // translate old single slope parameter to new double slope @@ -1524,10 +1524,8 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, } // finally: empty enough - if( gr1->get_grund_hang()!=gr1->get_weg_hang() || gr1->get_halt().is_bound() || gr1->kann_alle_obj_entfernen(player) - || gr1->find() || gr1->get_depot() || gr1->get_signalbox() - || (gr1->get_leitung() && gr1->hat_wege()) || gr1->get_weg(air_wt) || gr1->find() - || gr1->get_typ()==grund_t::brueckenboden || gr1->find() || gr1->get_typ()==grund_t::pierdeck) { + if( gr1->get_grund_hang()!=gr1->get_weg_hang() || gr1->get_halt().is_bound() || gr1->kann_alle_obj_entfernen(player) || + gr1->find() || gr1->get_depot() || (gr1->get_leitung() && gr1->hat_wege()) || gr1->get_weg(air_wt) || gr1->find() || gr1->get_typ()==grund_t::brueckenboden) { return NOTICE_TILE_FULL; } @@ -1555,6 +1553,9 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, // has the wrong tilt return NOTICE_TILE_FULL; } + // reverse ribis: up to here was direction leaving the tile, + // now it will be the direction on the tile when moving onto the slope + ribis = ribi_t::reverse_single(ribis); /* new things getting tricky: * A single way on an all up or down slope will result in * a slope with the way as hinge. @@ -1619,25 +1620,6 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, else { return "Maximum tile height difference reached."; } - if(tunnel_t *tunnel=gr1->find()){ - if(!tunnel->get_desc()->get_subwaterline_allowed()){ - if( tunnel_builder_t::get_is_below_waterline(new_pos)) { - return "This tunnel cannot be brought below the waterline"; - } - } - if(tunnel->get_desc()->get_depth_limit()){ - if(welt->lookup_hgt(new_pos.get_2d()) - new_pos.z > (sint8)tunnel->get_desc()->get_depth_limit()){ - return "This tunnel cannot be brought any deeper"; - } - } - if(tunnel->get_desc()->get_underwater_limit()){ - if(const grund_t* gr = welt->lookup_kartenboden(new_pos.get_2d())){ - if(gr->is_water() && gr->get_pos().z - new_pos.z > (sint8)tunnel->get_desc()->get_underwater_limit()){ - return "This tunnel cannot be bround any further below the water surface"; - } - } - } - } } } @@ -1746,8 +1728,8 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, ok |= slope_changed; if(ok) { + // check if clear if( gr1->kann_alle_obj_entfernen(player) ) { - // not empty ... return NOTICE_TILE_FULL; } @@ -1756,7 +1738,22 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, if(gr1->get_weg_nr(0)->is_deletable(player)!=NULL) { return NOTICE_TILE_FULL; } - if(gr1->has_two_ways() && gr1->get_weg_nr(1)-> is_deletable(player)!=NULL) { + if(gr1->has_two_ways() && gr1->get_weg_nr(1)->is_deletable(player)!=NULL) { + return NOTICE_TILE_FULL; + } + } + + // check funds + settings_t const& s = welt->get_settings(); + sint64 const cost = new_slope == RESTORE_SLOPE ? s.cst_alter_land : s.cst_set_slope; + if( !player->can_afford(cost) ) { + return NOTICE_INSUFFICIENT_FUNDS; + } + + // one last check + if ( gr1->is_water() && (new_pos.z > water_hgt || new_slope != 0) ) { + // we have to build underwater hill first + if( !welt->can_flatten_tile( player, k, water_hgt, false, true ) ) { return NOTICE_TILE_FULL; } } @@ -1847,7 +1844,6 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, welt->calc_climate( k, true ); } - settings_t const& s = welt->get_settings(); player_t::book_construction_costs(player, new_slope == RESTORE_SLOPE ? s.cst_alter_land : s.cst_set_slope, k, ignore_wt); } @@ -1864,6 +1860,7 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, + // set marker const char *tool_marker_t::work( player_t *player, koord3d pos ) { @@ -2275,7 +2272,7 @@ const char *tool_change_city_size_t::work( player_t *, koord3d pos ) const char *tool_set_climate_t::get_tooltip(player_t const*) const { char temp[1024]; - sprintf( temp, translator::translate( "Set tile climate" ), translator::translate( ground_desc_t::get_climate_name_from_bit((climate)atoi(default_param)) ) ); + sprintf( temp, translator::translate( "Set tile climate %s" ), translator::translate( ground_desc_t::get_climate_name_from_bit((climate)atoi(default_param)) ) ); return tooltip_with_price( temp, welt->get_settings().cst_alter_climate ); } @@ -4109,7 +4106,8 @@ bool tool_build_tunnel_t::vent_checker_t::check_next_tile(const grund_t *gr) con return true; } -int tool_build_tunnel_t::vent_checker_t::get_cost(const grund_t */*gr*/, const sint32, koord /*from_pos*/){ +int tool_build_tunnel_t::vent_checker_t::get_cost(const grund_t *, const sint32, ribi_t::ribi) +{ return welt->get_settings().get_meters_per_tile(); } @@ -4154,7 +4152,7 @@ class electron_t : public test_driver_t { bool check_next_tile(const grund_t* gr) const { return gr->get_leitung()!=NULL; } virtual ribi_t::ribi get_ribi(const grund_t* gr) const { return gr->get_leitung()->get_ribi(); } virtual waytype_t get_waytype() const { return invalid_wt; } - virtual int get_cost(const grund_t *, const sint32, koord) { return 1; } + virtual int get_cost(const grund_t *, const sint32, ribi_t::ribi) { return 1; } virtual bool is_target(const grund_t *,const grund_t *) { return false; } }; @@ -4186,7 +4184,7 @@ class scenario_checker_t : public test_driver_t { bool check_next_tile(const grund_t* gr) const { return other->check_next_tile(gr) && scenario->is_work_allowed_here(player, id, other->get_waytype(), gr->get_pos())==NULL;} virtual ribi_t::ribi get_ribi(const grund_t* gr) const { return other->get_ribi(gr); } virtual waytype_t get_waytype() const { return other->get_waytype(); } - virtual int get_cost(const grund_t *gr, const sint32 c, koord p) { return other->get_cost(gr,c,p); } + virtual int get_cost(const grund_t *gr, const sint32 c, ribi_t::ribi from) { return other->get_cost(gr,c,from); } virtual bool is_target(const grund_t *gr,const grund_t *gr2) { return other-> is_target(gr,gr2); } }; @@ -4195,7 +4193,7 @@ void tool_wayremover_t::mark_tiles( player_t *player, const koord3d &start, cons route_t verbindung; bool can_built = calc_route( verbindung, player, start, end ); if( can_built ) { - FOR(vector_tpl, const& pos, verbindung.get_route()) { + for(koord3d const& pos : verbindung.get_route()) { zeiger_t *marker = new zeiger_t(pos, NULL ); marker->set_image( cursor ); marker->mark_image_dirty( marker->get_image(), 0 ); @@ -4268,7 +4266,7 @@ bool tool_wayremover_t::calc_route( route_t &verbindung, player_t *player, const bool can_delete = start == end || verbindung.get_count()>1; if( can_delete ) { // found a route => check if I can delete anything on it - FOR(koord3d_vector_t, const& i, verbindung.get_route()) { + for(koord3d const& i : verbindung.get_route()) { if (!can_delete) break; grund_t const* const gr = welt->lookup(i); if( wt!=powerline_wt ) { @@ -5937,7 +5935,7 @@ bool tool_build_station_t::init( player_t * ) { sint8 rotation = -1; const building_desc_t *bdsc = get_desc( rotation ); - if( bdsc==NULL ) { + if( bdsc==NULL || bdsc->get_cursor()==NULL) { return false; } cursor = bdsc->get_cursor()->get_image_id(0); @@ -7140,7 +7138,7 @@ const char *tool_build_roadsign_t::do_work( player_t *player, const koord3d &sta mark_tiles(player, start, end); // only search the marked tiles uint32 j=0; - FOR(slist_tpl, const i, marked) { + for(zeiger_t* const i : marked) { grund_t* const gr = welt->lookup(i->get_pos()); weg_t *weg = gr->get_weg(desc->get_wtyp()); ribi_t::ribi dir = directions[j++]; @@ -7753,7 +7751,7 @@ bool tool_build_depot_t::init( player_t * ) const building_tile_desc_t *tile_desc = hausbauer_t::find_tile(default_param, 0); building_desc_t const* desc = tile_desc ? tile_desc->get_desc() : NULL; - if (desc == NULL) { + if (desc == NULL || desc->get_cursor()==NULL) { return false; } @@ -8081,11 +8079,9 @@ const char *tool_build_land_chain_t::work( player_t *player, koord3d pos ) player_t::book_construction_costs(player, count * welt->get_settings().cst_multiply_found_industry, build_pos.get_2d(), ignore_wt); // crossconnect all? - if(welt->get_settings().is_crossconnect_factories()) - { - FOR(vector_tpl, factory, welt->get_fab_list()) - { - factory->add_all_suppliers(); + if (welt->get_settings().is_crossconnect_factories()) { + for(fabrik_t* const f : welt->get_fab_list()) { + f->add_all_suppliers(); } } return NULL; @@ -8150,11 +8146,9 @@ const char *tool_city_chain_t::work( player_t *player, koord3d pos ) welt->get_viewport()->change_world_position( pos ); // crossconnect all? - if(welt->get_settings().is_crossconnect_factories()) - { - FOR(vector_tpl, factory, welt->get_fab_list()) - { - factory->add_all_suppliers(); + if (welt->get_settings().is_crossconnect_factories()) { + for(fabrik_t* const f : welt->get_fab_list()) { + f->add_all_suppliers(); } } // ain't going to be cheap @@ -8278,11 +8272,9 @@ const char *tool_build_factory_t::work( player_t *player, koord3d pos ) player_t::book_construction_costs(player, welt->get_settings().cst_multiply_found_industry, pos.get_2d(), ignore_wt); // crossconnect all? - if(welt->get_settings().is_crossconnect_factories()) - { - FOR(vector_tpl, factory, welt->get_fab_list()) - { - factory->add_all_suppliers(); + if (welt->get_settings().is_crossconnect_factories()) { + for(fabrik_t* const f : welt->get_fab_list()) { + f->add_all_suppliers(); } } return NULL; @@ -8754,14 +8746,14 @@ const char *tool_stop_mover_t::do_work( player_t *player, const koord3d &last_po } // first, check convoi without line - FOR(vector_tpl, const cnv, welt->convoys()) { + for(convoihandle_t const cnv : welt->convoys()) { // check line and owner if(!cnv->get_line().is_bound() && cnv->get_owner()==player) { schedule_t *schedule = cnv->get_schedule(); // check waytype if(schedule && schedule->is_stop_allowed(bd)) { bool updated = false; - FOR(minivec_tpl, & k, schedule->entries) { + for(schedule_entry_t & k : schedule->entries) { if ((catch_all_halt && haltestelle_t::get_halt( k.pos, cnv->get_owner()) == last_halt) || old_platform.is_contained(k.pos)) { k.pos = pos; @@ -8791,12 +8783,12 @@ const char *tool_stop_mover_t::do_work( player_t *player, const koord3d &last_po // next, check lines serving old_halt (no owner check needed for own lines ... vector_tpllines; player->simlinemgmt.get_lines(simline_t::line,&lines); - FOR(vector_tpl, const line, lines) { + for(linehandle_t const line : lines) { schedule_t *schedule = line->get_schedule(); // check waytype if(schedule->is_stop_allowed(bd)) { bool updated = false; - FOR(minivec_tpl, & k, schedule->entries) { + for(schedule_entry_t & k : schedule->entries) { // ok! if ((catch_all_halt && haltestelle_t::get_halt( k.pos, line->get_owner()) == last_halt) || old_platform.is_contained(k.pos)) { @@ -10112,7 +10104,7 @@ bool tool_change_line_t::init( player_t *player ) break; } - FOR(vector_tpl,line,lines) { + for(linehandle_t line : lines) { if( line->get_linetype() == linetype && line->get_convoys().get_count() > 2 ) { // correct waytpe and more than one,n now some up usage for the last six months sint64 transported = 0, capacity = 0; @@ -10129,7 +10121,7 @@ bool tool_change_line_t::init( player_t *player ) // less than 33 % usage => remove concois vector_tpl const& cnvs = line->get_convoys(); sint64 old_sum_capacity = 0; - FOR(vector_tpl,cnv,cnvs) { + for(convoihandle_t cnv : cnvs) { for( int i=0; iget_vehicle_count(); i++ ) { old_sum_capacity += cnv->get_vehicle(i)->get_desc()->get_capacity(); } @@ -10182,13 +10174,13 @@ bool tool_change_line_t::init( player_t *player ) { array_tpl > cnvs(welt->convoys().get_count()); uint32 max_cnvs=0; - FOR(vector_tpl, cnv, welt->convoys()) { + for(convoihandle_t cnv : welt->convoys()) { // only check lineless convoys if( !cnv->get_line().is_bound() ) { bool found = false; // check, if already matches existing convois schedule for( uint32 i=0; i, cnvcomp, cnvs[i] ) { + for(convoihandle_t cnvcomp : cnvs[i] ) { if( cnvcomp->get_schedule()->matches( welt, cnv->get_schedule() ) ) { found = true; cnvs[i].append( cnv ); @@ -10207,7 +10199,7 @@ bool tool_change_line_t::init( player_t *player ) // if there is more than one convois => new line if( cnvs[i].get_count()>1 ) { line = player->simlinemgmt.create_line( cnvs[i][0]->get_schedule()->get_type(), player, cnvs[i][0]->get_schedule() ); - FOR(vector_tpl, cnv, cnvs[i] ) { + for(convoihandle_t cnv : cnvs[i] ) { line->add_convoy( cnv ); cnv->set_line( line ); } @@ -10561,6 +10553,16 @@ bool tool_change_player_t::init( player_t *player_in) player->set_player_color( c1, c2 ); } break; + + case '$': // change bank account + if( player && player_in==welt->get_public_player() ) { + int delta; + if (sscanf(p, "%c,%i,%i", &tool, &id, &delta) == 3) { + player->get_finance()->book_account(delta); + } + } + break; + case 'n': // WAS: new player with type state case 'f': // WAS: activate/deactivate freeplay dbg->error( "tool_change_player_t::init()", "deprecated command called" ); diff --git a/simtool.h b/simtool.h index 735b4495c91..d7d14eb8992 100644 --- a/simtool.h +++ b/simtool.h @@ -166,7 +166,12 @@ class tool_flatten_path_t : public tool_path_tool_t{ /* slope tool definitions */ class tool_setslope_t : public tool_t { public: - tool_setslope_t() : tool_t(TOOL_SETSLOPE | GENERAL_TOOL) {} + tool_setslope_t() : tool_t(TOOL_SETSLOPE | GENERAL_TOOL), old_slope_compatibility_mode(true) {} + + // if true then slope by default_param will be translated to new double-height system + // true by default, can be set to false (used for scripts) + bool old_slope_compatibility_mode; + /** * Create an artificial slope * @param player the player doing the task @@ -412,7 +417,7 @@ class tool_build_tunnel_t : public two_click_tool_t { bool check_next_tile(const grund_t *) const override; waytype_t get_waytype() const override {return invalid_wt;} ribi_t::ribi get_ribi(const grund_t *) const override {return ribi_t::all;} - int get_cost(const grund_t *, const sint32, koord from_pos) override; + int get_cost(const grund_t *, const sint32, ribi_t::ribi from) override; bool is_target(const grund_t *, const grund_t *) override; }; diff --git a/simtypes.h b/simtypes.h index 8962bfa6363..64c98881f35 100644 --- a/simtypes.h +++ b/simtypes.h @@ -446,6 +446,13 @@ union value_t long i; }; + +// Unicode type large enough to hold every single possible Unicode code point. +typedef uint32 utf32; + +typedef unsigned char utf8; +typedef unsigned short utf16; + #define IGNORE_ZERO_WEIGHT #endif diff --git a/simutrans/font/Prop-Latin1.bdf b/simutrans/font/Prop-Latin1.bdf index 8681895c99d..23ba85cd594 100644 --- a/simutrans/font/Prop-Latin1.bdf +++ b/simutrans/font/Prop-Latin1.bdf @@ -2512,3 +2512,4 @@ cc cc 78 ENDCHAR +ENDFONT diff --git a/simutrans/font/Prop-Latin2.bdf b/simutrans/font/Prop-Latin2.bdf index 8912e85e9fd..f7b0cdf4ebd 100644 --- a/simutrans/font/Prop-Latin2.bdf +++ b/simutrans/font/Prop-Latin2.bdf @@ -2714,3 +2714,4 @@ cc cc 78 ENDCHAR +ENDFONT diff --git a/simutrans/script/ai_base.nut b/simutrans/script/ai_base.nut new file mode 100644 index 00000000000..994ff79f1be --- /dev/null +++ b/simutrans/script/ai_base.nut @@ -0,0 +1,40 @@ +/** + * Base file for AI players + */ + + +/** + * initialization, called when AI player starts + * @param pl player number of our AI player + */ +function start(pl) +{ +} + +/** + * the heart-beat of the AI player, + * called regularly, does not need to finish + */ +function step() +{ +} + +/** + * initialization, called when savegame is loaded + * default behavior: calls start() + * @param pl player number of our AI player + */ +function resume_game(pl) +{ + start(pl) +} + +/** + * Happy New Month and Year! + */ +function new_month() +{ +} +function new_year() +{ +} diff --git a/simutrans/script/new_scenario_template.nut b/simutrans/script/new_scenario_template.nut index b0b154e78d8..8e79f388e8e 100644 --- a/simutrans/script/new_scenario_template.nut +++ b/simutrans/script/new_scenario_template.nut @@ -71,7 +71,7 @@ function start() /** * Main function: returns percentage of completion * If returned value >= 100 then scenario is won - * If less than zero scenarion is lost + * If less than zero scenario is lost */ function is_scenario_completed(pl) { diff --git a/simutrans/script/scenario_base.nut b/simutrans/script/scenario_base.nut index c3fc9bec46c..9dd8200b080 100644 --- a/simutrans/script/scenario_base.nut +++ b/simutrans/script/scenario_base.nut @@ -15,16 +15,11 @@ scenario.short_description <- "This is a random scripted scenario" scenario.author <- "" scenario.version <- "" scenario.translation <- "" - -// table to hold routines for gui access -gui <- {} +scenario.api <- "112.3" // table to hold routines for forbidding/allowing player tools rules <- {} -// table to hold routines for debug -debug <- {} - // table containing all waytypes all_waytypes <- [wt_road, wt_rail, wt_water, wt_monorail, wt_maglev, wt_tram, wt_narrowgauge, wt_air, wt_power] @@ -44,7 +39,7 @@ foreach(tool_id in map.editing_tools) { /** * Called when filling toolbars, activating tools - * Results are not transfered over network, use the rules.forbid_* functions in this case + * Results are not transferred over network, use the rules.forbid_* functions in this case * * @return 1 if allowed, null otherwise */ @@ -101,9 +96,6 @@ function get_info_text(pl) { return "Random scenario." } function get_result_text(pl) { return "You are owned." } function get_debug_text(pl) { return debug.get_forbidden_text() } -function min(a, b) { return a < b ? a : b } -function max(a, b) { return a > b ? a : b } - /** * initialization, called when scenario starts */ @@ -129,547 +121,3 @@ function new_month() function new_year() { } - -/** - * load / save support - * the persistent table will be written / restored during save / load - * only plain data is saved: no classes / instances / functions, no cyclic references - */ -persistent <- {} - -/** - * writes the persistent table to a string - */ -function save() -{ - if (typeof(persistent) != "table") { - throw("error during saving: the variable persistent is not a table") - } - local str = "persistent = " + recursive_save(persistent, "\t", [ persistent ] ) - - return str -} - -function recursive_save(table, indent, table_stack) -{ - local isarray = typeof(table) == "array" - local str = (isarray ? "[" : "{") + "\n" - foreach(key, val in table) { - str += indent - if (!isarray) { - if (typeof(key)=="string") { - str += key + " = " - } - else { - str += "[" + key + "] = " - } - } - while( typeof(val) == "weakref" ) - val = val.ref - - switch( typeof(val) ) { - case "null": - str += "null" - break - case "integer": - case "float": - case "bool": - str += val - break - case "string": - str += "@\"" + val + "\"" - break - case "array": - case "table": - if (!table_stack.find(val)) { - table_stack.push( table ) - str += recursive_save(val, indent + "\t", table_stack ) - table_stack.pop() - } - else { - // cyclic reference - good luck with resolving - str += "null" - } - break - case "instance": - if ("_save" in val) { - str += val._save() - break - } - default: - str += "\"unknown\"" - } - if (str.slice(-1) != "\n") { - str += ",\n" - } - else { - str = str.slice(0,-1) + ",\n" - } - - } - str += indent.slice(0,-1) + (isarray ? "]" : "}") + "\n" - return str -} - - -///////////////////////////////////// -/** - * translatable texts - */ -class ttext { - text_raw = "" // raw text - text_tra = "" // translated text - // tables with information to replace variables {key} by their values - var_strings = null - var_values = null - // these cannot be initialized here to {}, otherwise - // all instances will refer to the same table! - - constructor(k) { - if (type(k) != "string") { - throw("ttext must be initialized with string") - } - text_raw = k - text_tra = k - var_strings = [] - var_values = {} - find_variables(text_raw) - } - - function get_translated_text() - { - return translate(text_raw) - } - - function find_variables(k) { - var_strings = [] - // find variable tags - local reg = regexp(@"\{([a-zA-Z_]+[a-zA-Z_0-9]*)\}"); - local start = 0; - while (start < k.len()) { - local res = reg.capture(k, start) - if (res) { - local vname = k.slice(res[1].begin, res[1].end) - var_strings.append({ name = vname, begin = res[0].begin, end = res[0].end }) - start = res[0].end - } - else { - start = k.len(); - } - } - } - - function _set(key, val) - { - var_values[key] <- val - } - - function _tostring() - { - // get translated text - local text_tra_ = get_translated_text() - if (text_tra_ != text_tra) { - text_tra = text_tra_ - // new text, search for identifiers - find_variables( text_tra ) - } - // create array of values, sort it per index in string - local values = [] - foreach(val in var_strings) { - local key = val.name - if ( key in var_values ) { - local valx = { begin = val.begin, end = val.end, value = var_values[key] } - values.append(valx) - } - } - values.sort( @(a,b) a.begin <=> b.begin ) - // create the resulting text piece by piece - local res = "" - local ind = 0 - foreach( e in values) { - res += text_tra.slice(ind, e.begin) - res += e.value - ind = e.end - } - res += text_tra.slice(ind) - return res - } -} - -/** - * text from files - */ -class ttextfile extends ttext { - - file = null - - constructor(file_) { - file = file_ - base.constructor("") - } - - function get_translated_text() - { - return load_language_file(file) - } - - -} - -///////////////////////////////////// - -function _extend_get(index) { - if (index == "rawin" || index == "rawget") { - throw null // invoke default delegate - return - } - local fname = "get_" + index - if (fname in this) { - local func = this[fname] - if (typeof(func)=="function") { - return func.call(this) - } - } - throw null // invoke default delegate -} - -/** - * this class implements an extended get method: - * everytime an index is not found it tries to call the method 'get_'+index - */ -class extend_get { - - _get = _extend_get - -} - -/** - * class that contains data to get access to an in-game factory - */ -class factory_x extends extend_get { - /// coordinates - x = -1 - y = -1 - - /// input / output slots, will be filled by constructor - input = {} - output = {} - - /// constructor will be overwritten by a native method - constructor(x_, y_) { - x = x_ - y = y_ - } -} - - -/** - * class that contains data to get access to an production slot of a factory - */ -class factory_production_x extends extend_get { - /// coordinates of factory - x = -1 - y = -1 - good = "" /// name of the good to be consumed / produced - index = -1 /// index to identify an io slot - max_storage = 0 /// max storage of this slot - scaling = 0 - - constructor(x_, y_, n_, i_) { - x = x_ - y = y_ - good = n_ - index = i_ - } -} - - -/** - * class to provide access to the game's list of all factories - */ -class factory_list_x { - - /// meta-method to be called in a foreach loop - function _nexti(prev_index) { - } - - /// meta method to retrieve factory by index in the global C++ array - function _get(index) { - } -} - - -/** - * class that contains data to get access to an in-game player company - */ -class player_x extends extend_get { - nr = 0 /// player number - - constructor(n_) { - nr = n_ - } -} - - -/** - * class that contains data to get access to an in-game halt - */ -class halt_x extends extend_get { - id = 0 /// halthandle_t - - constructor(i_) { - id = i_ - } -} - - -/** - * class that contains data to get access to a line of convoys - */ -class line_x extends extend_get { - id = 0 /// linehandle_t - - constructor(i_) { - id = i_ - } -} - -/** - * class to provide access to line lists - */ -class line_list_x { - - halt_id = 0 - player_id = 0 -} - -/** - * class that contains data to get access to a tile (grund_t) - */ -class tile_x extends extend_get { - /// coordinates - x = -1 - y = -1 - z = -1 - - constructor(x_, y_, z_) { - x = x_ - y = y_ - z = z_ - } - - function get_objects() - { - return tile_object_list_x(x,y,z) - } -} - -class tile_object_list_x { - /// coordinates - x = -1 - y = -1 - z = -1 - - constructor(x_, y_, z_) { - x = x_ - y = y_ - z = z_ - } -} - -/** - * class that contains data to get access to a grid square (planquadrat_t) - */ -class square_x extends extend_get { - /// coordinates - x = -1 - y = -1 - - constructor(x_, y_) { - x = x_ - y = y_ - } -} - - -/** - * class to provide access to convoy lists - */ -class convoy_list_x { - - use_world = 0 - halt_id = 0 - line_id = 0 -} - - -/** - * class that contains data to get access to an in-game convoy - */ -class convoy_x extends extend_get { - id = 0 /// convoihandle_t - - constructor(i_) { - id = i_ - } -} - - -/** - * class to provide access to the game's list of all cities - */ -class city_list_x { - - /// meta-method to be called in a foreach loop - function _nexti(prev_index) { - } - - /// meta method to retrieve city by index in the global C++ array - function _get(index) { - } -} - - -/** - * class that contains data to get access to a city - */ -class city_x extends extend_get { - /// coordinates - x = -1 - y = -1 - - constructor(x_, y_) { - x = x_ - y = y_ - } -} - -/** - * class to access in-game settings - */ -class settings { -} - -/** - * base class of map objects (ding_t) - */ -class map_object_x extends extend_get { - /// coordinates - x = -1 - y = -1 - z = -1 -} - -class schedule_x { - /// waytype - waytype = 0 - /// the entries - entries = null - - constructor(w, e) - { - waytype = w - entries = e - } -} - -class schedule_entry_x { - /// coordinate - x = -1 - y = -1 - z = -1 - /// load percentage - load = 0 - /// waiting - wait = 0 - - constructor(pos, l, w) - { - x = pos.x - y = pos.y - z = pos.z - load = l - wait = w - } -} - -class dir { - static none = 0 - static north = 1 - static east = 2 - static northeast = 3 - static south = 4 - static northsouth = 5 - static southeast = 6 - static northsoutheast = 7 - static west = 8 - static northwest = 9 - static eastwest = 10 - static northeastwest = 11 - static southwest = 12 - static northsouthwest = 13 - static southeastwest = 14 - static all = 15 - - static nsew = [1, 4, 2, 8] -} - -class time_x { - raw = 1 - year = 0 - month = 1 -} - -class time_ticks_x extends time_x { - ticks = 0 - ticks_per_month = 0 - next_month_ticks = 0 -} - -class coord { - x = -1 - y = -1 - - constructor(_x, _y) { x = _x; y = _y } - function _add(other) { return coord(x + other.x, y + other.y) } - function _sub(other) { return coord(x - other.x, y - other.y) } - function _mul(fac) { return coord(x * fac, y * fac) } - function _div(fac) { return coord(x / fac, y / fac) } - function _unm() { return coord(-x, -y) } - function _typeof() { return "coord" } - function _tostring() { return coord_to_string(this) } - function _save() { return "coord(" + x + ", " + y + ")" } - function href(text) { return "" + text + "" } -} - -class coord3d extends coord { - z = -1 - - constructor(_x, _y, _z) { x = _x; y = _y; z = _z } - function _add(other) { return coord3d(x + other.x, y + other.y, z + getz(other)) } - function _sub(other) { return coord3d(x - other.x, y - other.y, z - getz(other)) } - function _mul(fac) { return coord3d(x * fac, y * fac, z * fac) } - function _div(fac) { return coord3d(x / fac, y / fac, z / fac) } - function _unm() { return coord3d(-x, -y, -z) } - function _typeof() { return "coord3d" } - function _tostring() { return coord3d_to_string(this) } - function _save() { return "coord3d(" + x + ", " + y + ", " + z + ")" } - function href(text) { return "" + text + "" } - - function getz(other) { return ("z" in other) ? other.z : 0 } -} - -/** - * The same metamethod magic as in the class extend_get. - */ -table_with_extend_get <- { - - _get = _extend_get - -} - -/** - * table to hold routines to access the world - */ -world <- {} -world.setdelegate(table_with_extend_get) diff --git a/simutrans/script/script_base.nut b/simutrans/script/script_base.nut new file mode 100644 index 00000000000..3cec879f0a3 --- /dev/null +++ b/simutrans/script/script_base.nut @@ -0,0 +1,572 @@ +/** + * Base file for scripting + */ + +// table to hold routines for debug +debug <- {} + +function min(a, b) { return a < b ? a : b } +function max(a, b) { return a > b ? a : b } + +/** + * load / save support + * the persistent table will be written / restored during save / load + * only plain data is saved: no classes / instances / functions, no cyclic references + */ +persistent <- {} + +/** + * writes the persistent table to a string + */ +function save() +{ + if (typeof(persistent) != "table") { + throw("error during saving: the variable persistent is not a table") + } + local str = "persistent = " + recursive_save(persistent, "\t", [ persistent ] ) + + return str +} + +function is_identifier(str) +{ + return (str.toalnum() == str) && (str[0] < '0' || str[0] > '9') +} + +function recursive_save(table, indent, table_stack) +{ + local isarray = typeof(table) == "array" + local str = (isarray ? "[" : "{") + "\n" + foreach(key, val in table) { + str += indent + if (!isarray) { + if (typeof(key)=="string") { + if (is_identifier(key)) { + str += key + " = " + } + else { + str += "[\"" + key + "\"] = " + } + } + else { + str += "[" + key + "] = " + } + } + while( typeof(val) == "weakref" ) + val = val.ref + + switch( typeof(val) ) { + case "null": + str += "null" + break + case "integer": + case "float": + case "bool": + str += val + break + case "string": + str += "@\"" + val + "\"" + break + case "array": + case "table": + if (!table_stack.find(val)) { + table_stack.push( table ) + str += recursive_save(val, indent + "\t", table_stack ) + table_stack.pop() + } + else { + // cyclic reference - good luck with resolving + str += "null" + } + break + case "generator": + str += "null" + break + case "instance": + default: + if ("_save" in val) { + str += val._save() + break + } + str += "\"unknown(" + typeof(val) + ")\"" + } + if (str.slice(-1) != "\n") { + str += ",\n" + } + else { + str = str.slice(0,-1) + ",\n" + } + + } + str += indent.slice(0,-1) + (isarray ? "]" : "}") + "\n" + return str +} + + +///////////////////////////////////// +/** + * translatable texts + */ +class ttext { + text_raw = "" // raw text + text_tra = "" // translated text + // tables with information to replace variables {key} by their values + var_strings = null + var_values = null + // these cannot be initialized here to {}, otherwise + // all instances will refer to the same table! + + constructor(k) { + if (type(k) != "string") { + throw("ttext must be initialized with string") + } + text_raw = k + text_tra = k + var_strings = [] + var_values = {} + find_variables(text_raw) + } + + function get_translated_text() + { + return translate(text_raw) + } + + function find_variables(k) { + var_strings = [] + // find variable tags + local reg = regexp(@"\{([a-zA-Z_]+[a-zA-Z_0-9]*)\}"); + local start = 0; + while (start < k.len()) { + local res = reg.capture(k, start) + if (res) { + local vname = k.slice(res[1].begin, res[1].end) + var_strings.append({ name = vname, begin = res[0].begin, end = res[0].end }) + start = res[0].end + } + else { + start = k.len(); + } + } + } + + function _set(key, val) + { + var_values[key] <- val + } + + function _tostring() + { + // get translated text + local text_tra_ = get_translated_text() + if (text_tra_ != text_tra) { + text_tra = text_tra_ + // new text, search for identifiers + find_variables( text_tra ) + } + // create array of values, sort it per index in string + local values = [] + foreach(val in var_strings) { + local key = val.name + if ( key in var_values ) { + local valx = { begin = val.begin, end = val.end, value = var_values[key] } + values.append(valx) + } + } + values.sort( @(a,b) a.begin <=> b.begin ) + // create the resulting text piece by piece + local res = "" + local ind = 0 + foreach( e in values) { + res += text_tra.slice(ind, e.begin) + res += e.value + ind = e.end + } + res += text_tra.slice(ind) + return res + } +} + +/** + * text from files + */ +class ttextfile extends ttext { + + file = null + + constructor(file_) { + file = file_ + base.constructor("") + } + + function get_translated_text() + { + return load_language_file(file) + } + + +} + +///////////////////////////////////// + +function _extend_get(index) { + if (index == "rawin" || index == "rawget") { + throw null // invoke default delegate + return + } + local fname = "get_" + index + if (fname in this) { + local func = this[fname] + if (typeof(func)=="function") { + return func.call(this) + } + } + throw null // invoke default delegate +} + +/** + * this class implements an extended get method: + * everytime an index is not found it tries to call the method 'get_'+index + */ +class extend_get { + + _get = _extend_get + +} + +/** + * class that contains data to get access to an in-game factory + */ +class factory_x extends extend_get { + /// coordinates + x = -1 + y = -1 + + /// input / output slots, will be filled by constructor + input = {} + output = {} + + /// constructor will be overwritten by a native method + constructor(x_, y_) { + x = x_ + y = y_ + } +} + + +/** + * class that contains data to get access to an production slot of a factory + */ +class factory_production_x extends extend_get { + /// coordinates of factory + x = -1 + y = -1 + good = "" /// name of the good to be consumed / produced + index = -1 /// index to identify an io slot + max_storage = 0 /// max storage of this slot + scaling = 0 + + constructor(x_, y_, n_, i_) { + x = x_ + y = y_ + good = n_ + index = i_ + } +} + + +/** + * class to provide access to the game's list of all factories + */ +class factory_list_x { + + /// meta-method to be called in a foreach loop + function _nexti(prev_index) { + } + + /// meta method to retrieve factory by index in the global C++ array + function _get(index) { + } +} + + +/** + * class that contains data to get access to an in-game player company + */ +class player_x extends extend_get { + nr = 0 /// player number + + constructor(n_) { + nr = n_ + } +} + + +/** + * class that contains data to get access to an in-game halt + */ +class halt_x extends extend_get { + id = 0 /// halthandle_t + + constructor(i_) { + id = i_ + } +} + + +/** + * class that contains data to get access to a line of convoys + */ +class line_x extends extend_get { + id = 0 /// linehandle_t + + constructor(i_) { + id = i_ + } +} + +/** + * class to provide access to line lists + */ +class line_list_x { + + halt_id = 0 + player_id = 0 +} + +/** + * class that contains data to get access to a tile (grund_t) + */ +class tile_x extends extend_get { + /// coordinates + x = -1 + y = -1 + z = -1 + + constructor(x_, y_, z_) { + x = x_ + y = y_ + z = z_ + } + + function get_objects() + { + return tile_object_list_x(x,y,z) + } +} + +class tile_object_list_x { + /// coordinates + x = -1 + y = -1 + z = -1 + + constructor(x_, y_, z_) { + x = x_ + y = y_ + z = z_ + } +} + +/** + * class that contains data to get access to a grid square (planquadrat_t) + */ +class square_x extends extend_get { + /// coordinates + x = -1 + y = -1 + + constructor(x_, y_) { + x = x_ + y = y_ + } +} + + +/** + * class to provide access to convoy lists + */ +class convoy_list_x { + + use_world = 0 + halt_id = 0 + line_id = 0 +} + + +/** + * class that contains data to get access to an in-game convoy + */ +class convoy_x extends extend_get { + id = 0 /// convoihandle_t + + constructor(i_) { + id = i_ + } +} + + +/** + * class to provide access to the game's list of all cities + */ +class city_list_x { + + /// meta-method to be called in a foreach loop + function _nexti(prev_index) { + } + + /// meta method to retrieve city by index in the global C++ array + function _get(index) { + } +} + + +/** + * class that contains data to get access to a city + */ +class city_x extends extend_get { + /// coordinates + x = -1 + y = -1 + + constructor(x_, y_) { + x = x_ + y = y_ + } +} + +/** + * class to access in-game settings + */ +class settings { +} + +/** + * base class of map objects (ding_t) + */ +class map_object_x extends extend_get { + /// coordinates + x = -1 + y = -1 + z = -1 +} + +class schedule_entry_x { + /// coordinate + x = -1 + y = -1 + z = -1 + /// load percentage + load = 0 + /// waiting + wait = 0 + + constructor(pos, l, w) + { + x = pos.x + y = pos.y + z = pos.z + load = l + wait = w + } +} + +class dir { + static none = 0 + static north = 1 + static east = 2 + static northeast = 3 + static south = 4 + static northsouth = 5 + static southeast = 6 + static northsoutheast = 7 + static west = 8 + static northwest = 9 + static eastwest = 10 + static northeastwest = 11 + static southwest = 12 + static northsouthwest = 13 + static southeastwest = 14 + static all = 15 + + static nsew = [1, 4, 2, 8] +} + +class slope { + static flat=0 + static north = 3+1 ///< North slope + static west = 9+3 ///< West slope + static east = 27+1 ///< East slope + static south = 27+9 ///< South slope + static northwest = 27 ///< NW corner + static northeast = 9 ///< NE corner + static southeast = 3 ///< SE corner + static southwest = 1 ///< SW corner + static raised = 80 ///< special meaning: used as slope of bridgeheads + static all_up_slope = 82 ///< used for terraforming tools + static all_down_slope = 83 ///< used for terraforming tools +} + +class time_x { + raw = 1 + year = 0 + month = 1 +} + +class time_ticks_x extends time_x { + ticks = 0 + ticks_per_month = 0 + next_month_ticks = 0 +} + +class coord { + x = -1 + y = -1 + + constructor(_x, _y) { x = _x; y = _y } + function _add(other) { return coord(x + other.x, y + other.y) } + function _sub(other) { return coord(x - other.x, y - other.y) } + function _mul(fac) { return coord(x * fac, y * fac) } + function _div(fac) { return coord(x / fac, y / fac) } + function _unm() { return coord(-x, -y) } + function _typeof() { return "coord" } + function _tostring() { return coord_to_string(this) } + function _save() { return "coord(" + x + ", " + y + ")" } + function href(text) { return "" + text + "" } +} + +class coord3d extends coord { + z = -1 + + constructor(_x, _y, _z) { x = _x; y = _y; z = _z } + function _add(other) { return coord3d(x + other.x, y + other.y, z + getz(other)) } + function _sub(other) { return coord3d(x - other.x, y - other.y, z - getz(other)) } + function _mul(fac) { return coord3d(x * fac, y * fac, z * fac) } + function _div(fac) { return coord3d(x / fac, y / fac, z / fac) } + function _unm() { return coord3d(-x, -y, -z) } + function _typeof() { return "coord3d" } + function _tostring() { return coord3d_to_string(this) } + function _save() { return "coord3d(" + x + ", " + y + ", " + z + ")" } + function href(text) { return "" + text + "" } + + function getz(other) { return ("z" in other) ? other.z : 0 } +} + +/** + * The same metamethod magic as in the class extend_get. + */ +table_with_extend_get <- { + + _get = _extend_get + +} + +/** + * table to hold routines to access the world + */ +world <- {} +world.setdelegate(table_with_extend_get) + + +// table to hold routines for gui access +gui <- {} diff --git a/simutrans/script/scenario_compat.nut b/simutrans/script/script_compat.nut similarity index 83% rename from simutrans/script/scenario_compat.nut rename to simutrans/script/script_compat.nut index a6afc19a3ca..aa1f886fc91 100644 --- a/simutrans/script/scenario_compat.nut +++ b/simutrans/script/script_compat.nut @@ -50,4 +50,9 @@ function compat_120_1() return false } } + // gui.add_message got extra parameter (r7890) + gui.add_message_new <- gui.add_message + gui.add_message <- function(text) { + gui.add_message_new( player_x(-1) /*will be set to active player*/, text) + } } diff --git a/simutrans/script/tool_base.nut b/simutrans/script/tool_base.nut new file mode 100644 index 00000000000..7128ee1e655 --- /dev/null +++ b/simutrans/script/tool_base.nut @@ -0,0 +1,61 @@ +// stubs for functions needed by scripted tools +function init(player) +{ + return true +} + +function exit(player) +{ + return true +} +// one-click tools +function work(player, pos) +{ + return null +} + +// two-click tools +function do_work(player, start, end) +{ + return null +} + +function mark_tiles(player, start, end) +{ +} + +/** + * Can the tool start/end on pos? If it is the second click, start is the position of the first click + * 0 = no + * 1 = This tool can work on this tile (with single click) + * 2 = On this tile can dragging start/end + * 3 = Both (1 and 2) + * + */ +function is_valid_pos(player, start, pos) +{ + return 2 +} + +// dummy auxiliary function, will be regularly called +function step() +{ + // do not implement +} + +// check whether all functions support the flags parameter +function correct_missing_flags_argument() +{ + if (work.getinfos().parameters.len() == 3) { + work_old <- work + work = function(player, pos, flags) { return work_old(player, pos) } + } + if (do_work.getinfos().parameters.len() == 4) { + do_work_old <- do_work + do_work = function(player, start, end, flags) { return do_work_old(player, start, end) } + } + if (mark_tiles.getinfos().parameters.len() == 4) { + mark_tiles_old <- mark_tiles + mark_tiles = function(player, start, end, flags) { mark_tiles_old(player, start, end) } + } +} diff --git a/simutrans/text/ja.tab b/simutrans/text/ja.tab index 495435995de..7746f8c3a7e 100644 --- a/simutrans/text/ja.tab +++ b/simutrans/text/ja.tab @@ -1734,7 +1734,7 @@ Retire. date: return ticket 往復路線 Revenue -収入 +運賃収入 Revision: リビジョン: Right-click to open the finance dialog. diff --git a/simworld.cc b/simworld.cc index 9a75eaf26ec..58d6f88a790 100644 --- a/simworld.cc +++ b/simworld.cc @@ -1379,7 +1379,7 @@ void karte_t::init(settings_t* const sets, sint8 const* const h_field) } for( uint i=0; iwarning("karte_t::set_tool", "Ignored tool %s during loading.", tool_in->get_name() ); + dbg->warning("karte_t::set_tool_api", "Ignored tool %s during loading.", tool_in->get_name() ); return; } - bool scripted_call = tool_in->is_scripted(); + bool needs_check = !tool_in->no_check(); // check for scenario conditions - if( !scripted_call && (scenario && !scenario->is_tool_allowed(player, tool_in->get_id(), tool_in->get_waytype())) ) { + if( needs_check && !scenario->is_tool_allowed(player, tool_in->get_id(), tool_in->get_waytype()) ) { return; } - - player_t* action_player = player; - if(tool_in->get_id() == (TOOL_ACCESS_TOOL | SIMPLE_TOOL)) - { - uint16 id_setting_player; - uint16 id_receiving_player; - sint16 allow_access; - - if(3 != sscanf(tool_in->get_default_param(), "g%hi,%hi,%hi", &id_setting_player, &id_receiving_player, &allow_access)) - { - dbg->error( "karte_t::set_tool", "could not perform (%s)", tool_in->get_default_param() ); - return; - } - action_player = this->get_player(id_setting_player); - } - // check for password-protected players - if( (!tool_in->is_init_network_safe() || !tool_in->is_work_network_safe()) && !scripted_call && - !(tool_in->get_id()==(TOOL_CHANGE_PLAYER|SIMPLE_TOOL) || tool_in->get_id()==(TOOL_ADD_MESSAGE| GENERAL_TOOL)) && - action_player && action_player->is_locked() ) { + if( (!tool_in->is_init_network_safe() || !tool_in->is_work_network_safe()) && needs_check && + !(tool_in->get_id()==(TOOL_CHANGE_PLAYER|SIMPLE_TOOL) || tool_in->get_id()==(TOOL_ADD_MESSAGE | GENERAL_TOOL)) && + player && player->is_locked() ) { // player is currently password protected => request unlock first - create_win( -1, -1, new password_frame_t(action_player), w_info, magic_pwd_t + action_player->get_player_nr() ); + create_win( -1, -1, new password_frame_t(player), w_info, magic_pwd_t + player->get_player_nr() ); return; } tool_in->flags |= event_get_last_control_shift(); if(!env_t::networkmode || tool_in->is_init_network_safe() ) { - if (tool_in->is_init_network_safe()) { + if (called_from_script || tool_in->is_init_network_safe()) { local_set_tool(tool_in, player); } else { // queue tool for execution nwc_tool_t* nwc = new nwc_tool_t(player, tool_in, zeiger->get_pos(), steps, map_counter, true); command_queue_append(nwc); + suspended = true; } } else { // queue tool for network nwc_tool_t *nwc = new nwc_tool_t(player, tool_in, zeiger->get_pos(), steps, map_counter, true); network_send_server(nwc); + suspended = true; } } + // set a new tool on our client, calls init void karte_t::local_set_tool( tool_t *tool_in, player_t * player ) { @@ -8822,7 +8810,7 @@ void karte_t::switch_server( bool start_server, bool port_forwarding ) } else { // now start a server with defaults - env_t::networkmode = network_init_server( env_t::server_port ); + env_t::networkmode = network_init_server( env_t::server_port, env_t::listen ); if( env_t::networkmode ) { // query IP and try to open ports on router @@ -10459,6 +10447,50 @@ const char* karte_t::call_work(tool_t *tool, player_t *player, koord3d pos, bool } +const char* karte_t::call_work_api(tool_t *tool, player_t *player, koord3d pos, bool &suspended, bool called_from_api ) +{ + suspended = false; + const char *err = NULL; + bool network_safe_tool = tool->is_work_network_safe() || tool->is_work_here_network_safe(player, pos); + if( !env_t::networkmode || network_safe_tool ) { + // do the work + tool->flags |= tool_t::WFL_LOCAL; + // check allowance by scenario + if ( (tool->flags & tool_t::WFL_NO_CHK) == 0 && get_scenario()->is_scripted()) { + if (!get_scenario()->is_tool_allowed(player, tool->get_id(), tool->get_waytype()) ) { + err = ""; + } + else { + err = get_scenario()->is_work_allowed_here(player, tool->get_id(), tool->get_waytype(), pos); + } + } + if (err == NULL) { + if (called_from_api || network_safe_tool) { + err = tool->work(player, pos); + suspended = false; + } + else { + // queue tool for execution (will be only done when NOT in networkmode!) + nwc_tool_t* nwc = new nwc_tool_t(player, tool, pos, get_steps(), get_map_counter(), false); + command_queue_append(nwc); + // reset tool + tool->init(player); + suspended = true; + } + } + } + else { + // queue tool for network + nwc_tool_t *nwc = new nwc_tool_t(player, tool, pos, get_steps(), get_map_counter(), false); + network_send_server(nwc); + suspended = true; + // reset tool + tool->init(player); + } + return err; +} + + static slist_tpl command_queue; void karte_t::command_queue_append(network_world_command_t* nwc) const diff --git a/simworld.h b/simworld.h index c32e17d96d2..87ca1a1a6ff 100644 --- a/simworld.h +++ b/simworld.h @@ -1794,10 +1794,12 @@ class karte_t public: void flood_to_depth(sint8 new_water_height, sint8 *stage); + void set_tool_api(tool_t* tool_in, player_t* player, bool& suspended, bool called_from_api ); + /** * Set a new tool as current: calls local_set_tool or sends to server. */ - void set_tool( tool_t *tool_in, player_t * player ); + void set_tool( tool_t *tool_in, player_t * player ) { bool b; set_tool_api(tool_in, player, b, false); } /** * Set a new tool on our client, calls init. @@ -2030,6 +2032,13 @@ class karte_t inline void decrease_actual_industry_density(uint32 value) { actual_industry_density -= value; } inline void increase_actual_industry_density(uint32 value) { actual_industry_density += value; } + /** + * Calls the work method of the tool. + * Takes network and scenarios into account. + * (There is the flags for scripted calls in the tool structure, but it seems not used so far?!) + */ + const char *call_work_api(tool_t *t, player_t *pl, koord3d pos, bool &suspended, bool called_from_api); + /** * Initialize map. * @param sets Game settings. diff --git a/squirrel/sq_extensions.cc b/squirrel/sq_extensions.cc index e83c5727295..6936c634f96 100644 --- a/squirrel/sq_extensions.cc +++ b/squirrel/sq_extensions.cc @@ -3,15 +3,35 @@ #include "squirrel/sqpcheader.h" // for declarations... #include "squirrel/sqvm.h" // for Raise_Error_vl #include +#include "../tpl/ptrhashtable_tpl.h" +// store data associate to vm's here +struct my_vm_info_t { + const char* suspend_blocker; /// if not null then no suspendable functions should be called +}; + +static ptrhashtable_tpl vm_info; + +void register_vm(HSQUIRRELVM v) +{ + vm_info.put(v); +} + +void unregister_vm(HSQUIRRELVM v) +{ + vm_info.remove(v); +} void* get_instanceup(HSQUIRRELVM vm, SQInteger index, void* tag, const char* type) { void* ptr = NULL; - if(!SQ_SUCCEEDED(sq_getinstanceup(vm, index, &ptr, tag))) { + if(SQ_SUCCEEDED(sq_getinstanceup(vm, index, &ptr, tag))) { + return ptr; + } + else { sq_raise_error(vm, "Expected instance of class %s.", type ? type : ""); + return NULL; } - return ptr; } @@ -53,3 +73,48 @@ SQRESULT sq_resumevm(HSQUIRRELVM v, SQBool retval, SQInteger ops) return ret; } + +// see sq_wakeupvm +void sq_setwakeupretvalue(HSQUIRRELVM v) +{ + if(!v->_suspended) { + return; + } + SQInteger target = v->_suspended_target; + if(target != -1) { + v->GetAt(v->_stackbase+v->_suspended_target) = v->GetUp(-1); //retval + } + v->Pop(); + // make target invalid + v->_suspended_target = -1; +} + +bool sq_canresumevm(HSQUIRRELVM v) +{ + // true if not suspended or not waiting for any return value + return (!v->_suspended || v->_suspended_target==-1); +} + +SQRESULT sq_get_ops_total(HSQUIRRELVM v) +{ + sq_pushinteger(v, v->_ops_total); + return 1; +} + +SQRESULT sq_get_ops_remaing(HSQUIRRELVM v) +{ + sq_pushinteger(v, v->_ops_remaining); + return 1; +} + +void sq_block_suspend(HSQUIRRELVM v, const char* f) +{ + if (my_vm_info_t* i = vm_info.access(v)) { + i->suspend_blocker = f; + } +} + +const char* sq_get_suspend_blocker(HSQUIRRELVM v) +{ + return vm_info.get(v).suspend_blocker; +} diff --git a/squirrel/sq_extensions.h b/squirrel/sq_extensions.h index cf59b68b3f2..e568c1a2cbb 100644 --- a/squirrel/sq_extensions.h +++ b/squirrel/sq_extensions.h @@ -8,6 +8,15 @@ * for simutrans */ +/** + * Register vm to store internal information. + */ +void register_vm(HSQUIRRELVM v); +/** + * Should be called if vm closes. + */ +void unregister_vm(HSQUIRRELVM v); + /** * Returns instance userpointer of instance, checks type tag. * @param index of instance in stack @@ -30,10 +39,36 @@ SQRESULT sq_raise_error(HSQUIRRELVM vm, const SQChar *s, ...); */ SQRESULT sq_call_restricted(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool throw_if_no_ops, SQInteger ops = 1000); +/** + * if @r is not null, then no suspendable functions should be called in this vm + */ +void sq_block_suspend(HSQUIRRELVM v, const char* f); + +/** + * @returns the name of the suspend-blocking function + */ +const char* sq_get_suspend_blocker(HSQUIRRELVM v); + /** * resumes suspended vm. * returns and suspends (again) vm if opcode limit is exceeded. */ SQRESULT sq_resumevm(HSQUIRRELVM v, SQBool retval, SQInteger ops = 1000); +/** + * pops return value from stack and sets it for the suspended script. + */ +void sq_setwakeupretvalue(HSQUIRRELVM v); + +/** + * checks whether vm can be resumed. in particular if script waits for return value on wakeup. + */ +bool sq_canresumevm(HSQUIRRELVM v); + +/// @returns total amount of opcodes executed by vm +SQRESULT sq_get_ops_total(HSQUIRRELVM v); + +/// @returns amount of remaining opcodes until vm will be suspended +SQRESULT sq_get_ops_remaing(HSQUIRRELVM v); + #endif diff --git a/squirrel/sqstdaux.h b/squirrel/sqstdaux.h index 1051a1d33c2..45ff654a9d7 100644 --- a/squirrel/sqstdaux.h +++ b/squirrel/sqstdaux.h @@ -9,6 +9,8 @@ extern "C" { SQUIRREL_API void sqstd_seterrorhandlers(HSQUIRRELVM v); SQUIRREL_API void sqstd_printcallstack(HSQUIRRELVM v); +SQUIRREL_API SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const SQChar *err,...); + #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/squirrel/sqstdio.h b/squirrel/sqstdio.h index bbc0ef3168d..4b987a35814 100644 --- a/squirrel/sqstdio.h +++ b/squirrel/sqstdio.h @@ -7,6 +7,7 @@ #define SQSTD_STREAM_TYPE_TAG 0x80000000 struct SQStream { + virtual ~SQStream() {} virtual SQInteger Read(void *buffer, SQInteger size) = 0; virtual SQInteger Write(void *buffer, SQInteger size) = 0; virtual SQInteger Flush() = 0; @@ -15,7 +16,6 @@ struct SQStream { virtual SQInteger Seek(SQInteger offset, SQInteger origin) = 0; virtual bool IsValid() = 0; virtual bool EOS() = 0; - virtual ~SQStream() { /* happy compiler */ } }; extern "C" { diff --git a/squirrel/sqstdlib/sqstdaux.cc b/squirrel/sqstdlib/sqstdaux.cc index 05af35bd5c7..0d196d9d739 100644 --- a/squirrel/sqstdlib/sqstdaux.cc +++ b/squirrel/sqstdlib/sqstdaux.cc @@ -1,14 +1,16 @@ /* see copyright notice in squirrel.h */ #include "../squirrel.h" #include "../sqstdaux.h" +#include #include +#include void sqstd_printcallstack(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { SQStackInfos si; - SQInteger i = 0; + SQInteger i; SQFloat f; const SQChar *s; SQInteger level=1; //1 is to skip this function that is level 0 @@ -54,10 +56,10 @@ void sqstd_printcallstack(HSQUIRRELVM v) pf(v,_SC("- - - [%s] \"%s\"\n"),name,s); break; case OT_TABLE: - pf(v,_SC("- - - [%s] TABLE\n"),name); + pf(v,_SC("- - - [%s] TABLE (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_ARRAY: - pf(v,_SC("- - - [%s] ARRAY\n"),name); + pf(v,_SC("- - - [%s] ARRAY (%d entries)\n"),name, sq_getsize(v, -1)); break; case OT_CLOSURE: pf(v,_SC("- - - [%s] CLOSURE\n"),name); @@ -78,8 +80,27 @@ void sqstd_printcallstack(HSQUIRRELVM v) pf(v,_SC("- - - [%s] CLASS\n"),name); break; case OT_INSTANCE: + { + // try to obtain class name + sq_getclass(v, -1); + sq_pushnull(v); + if (SQ_SUCCEEDED(sq_getattributes(v, -2))) { + // stack: instance, class, attributes + sq_pushstring(v, "classname", -1); + if (SQ_SUCCEEDED(sq_get(v, -2))) { + const char* cn; + if (SQ_SUCCEEDED(sq_getstring(v, -1, &cn))) { + pf(v,_SC("- - - [%s] INSTANCE(%s)\n"),name, cn); + sq_pop(v, 3); + break; + } + } + sq_poptop(v); + } + sq_poptop(v); pf(v,_SC("- - - [%s] INSTANCE\n"),name); break; + } case OT_WEAKREF: pf(v,_SC("- - - [%s] WEAKREF\n"),name); break; @@ -134,3 +155,22 @@ void sqstd_seterrorhandlers(HSQUIRRELVM v) sq_newclosure(v,_sqstd_aux_printerror,0); sq_seterrorhandler(v); } + +SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const SQChar *err,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,err); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,err,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + return sq_throwerror(v,_SC("@failed to generate formatted error message")); + } else { + return sq_throwerror(v,b); + } +} diff --git a/squirrel/sqstdlib/sqstdblob.cc b/squirrel/sqstdlib/sqstdblob.cc index bc228c72a90..dac94cc6268 100644 --- a/squirrel/sqstdlib/sqstdblob.cc +++ b/squirrel/sqstdlib/sqstdblob.cc @@ -7,7 +7,7 @@ #include "sqstdstream.h" #include "sqstdblobimpl.h" -#define SQSTD_BLOB_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000002) +#define SQSTD_BLOB_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000002)) //Blob @@ -82,6 +82,12 @@ static SQInteger _blob__get(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger idx; + + if ((sq_gettype(v, 2) & SQOBJECT_NUMERIC) == 0) + { + sq_pushnull(v); + return sq_throwobject(v); + } sq_getinteger(v,2,&idx); if(idx < 0 || idx >= self->Len()) return sq_throwerror(v,_SC("index out of range")); @@ -168,7 +174,7 @@ static const SQRegFunction _blob_methods[] = { _DECL_BLOB_FUNC(swap2,1,_SC("x")), _DECL_BLOB_FUNC(swap4,1,_SC("x")), _DECL_BLOB_FUNC(_set,3,_SC("xnn")), - _DECL_BLOB_FUNC(_get,2,_SC("xn")), + _DECL_BLOB_FUNC(_get,2,_SC("x.")), _DECL_BLOB_FUNC(_typeof,1,_SC("x")), _DECL_BLOB_FUNC(_nexti,2,_SC("x")), _DECL_BLOB_FUNC(_cloned,2,_SC("xx")), @@ -181,16 +187,16 @@ static const SQRegFunction _blob_methods[] = { static SQInteger _g_blob_swap2(HSQUIRRELVM v) { - SQInteger i = 0; + SQInteger i; sq_getinteger(v,2,&i); - short s=(short)i; - sq_pushinteger(v,(s<<8)|((s>>8)&0x00FF)); + unsigned short s = (unsigned short)i; + sq_pushinteger(v, ((s << 8) | ((s >> 8) & 0x00FFu)) & 0xFFFFu); return 1; } static SQInteger _g_blob_swap4(HSQUIRRELVM v) { - SQInteger i = 0; + SQInteger i; sq_getinteger(v,2,&i); unsigned int t4 = (unsigned int)i; __swap_dword(&t4); diff --git a/squirrel/sqstdlib/sqstdblobimpl.h b/squirrel/sqstdlib/sqstdblobimpl.h index 5de92d81d7f..07cbd3b0110 100644 --- a/squirrel/sqstdlib/sqstdblobimpl.h +++ b/squirrel/sqstdlib/sqstdblobimpl.h @@ -88,7 +88,7 @@ struct SQBlob : public SQStream return 0; } bool IsValid() { - return _buf?true:false; + return _size == 0 || _buf?true:false; } bool EOS() { return _ptr == _size; diff --git a/squirrel/sqstdlib/sqstdio.cc b/squirrel/sqstdlib/sqstdio.cc index 719e4eb0902..ed2352c963c 100644 --- a/squirrel/sqstdlib/sqstdio.cc +++ b/squirrel/sqstdlib/sqstdio.cc @@ -5,7 +5,7 @@ #include "../sqstdio.h" #include "sqstdstream.h" -#define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001) +#define SQSTD_FILE_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001)) //basic API SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode) { @@ -372,7 +372,7 @@ SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror) } if(uc != 0xBF) { sqstd_fclose(file); - return sq_throwerror(v,_SC("Unrecognozed ecoding")); + return sq_throwerror(v,_SC("Unrecognized encoding")); } #ifdef SQUNICODE func = _io_file_lexfeed_UTF8; @@ -399,6 +399,10 @@ SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror) SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror) { + //at least one entry must exist in order for us to push it as the environment + if(sq_gettop(v) == 0) + return sq_throwerror(v,_SC("environment table expected")); + if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) { sq_push(v,-2); if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) { diff --git a/squirrel/sqstdlib/sqstdmath.cc b/squirrel/sqstdlib/sqstdmath.cc index 75b5ed4b28a..9ec80d77f5b 100644 --- a/squirrel/sqstdlib/sqstdmath.cc +++ b/squirrel/sqstdlib/sqstdmath.cc @@ -21,7 +21,7 @@ static SQInteger math_srand(HSQUIRRELVM v) { - SQInteger i = 0; + SQInteger i; if(SQ_FAILED(sq_getinteger(v,2,&i))) return sq_throwerror(v,_SC("invalid param")); srand((unsigned int)i); diff --git a/squirrel/sqstdlib/sqstdrex.cc b/squirrel/sqstdlib/sqstdrex.cc index 597988a7398..e5d06026309 100644 --- a/squirrel/sqstdlib/sqstdrex.cc +++ b/squirrel/sqstdlib/sqstdrex.cc @@ -153,6 +153,9 @@ static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass) exp->_nodes[node].right = ce; return node; } + case 0: + sqstd_rex_error(exp,_SC("letter expected for argument of escape sequence")); + break; case 'b': case 'B': if(!isclass) { @@ -575,9 +578,9 @@ SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error) scprintf(_SC("\n")); for(i = 0;i < nsize; i++) { if(exp->_nodes[i].type>MAX_CHAR) - scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); + scprintf(_SC("[%02d] %10s "), (SQInt32)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); else - scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type); + scprintf(_SC("[%02d] %10c "), (SQInt32)i,exp->_nodes[i].type); scprintf(_SC("left %02d right %02d next %02d\n"), (SQInt32)exp->_nodes[i].left, (SQInt32)exp->_nodes[i].right, (SQInt32)exp->_nodes[i].next); } scprintf(_SC("\n")); diff --git a/squirrel/sqstdlib/sqstdstream.cc b/squirrel/sqstdlib/sqstdstream.cc index a6c867ffa5b..902502e674a 100644 --- a/squirrel/sqstdlib/sqstdstream.cc +++ b/squirrel/sqstdlib/sqstdstream.cc @@ -11,7 +11,7 @@ #define SETUP_STREAM(v) \ SQStream *self = NULL; \ - if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_STREAM_TYPE_TAG))) \ + if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)))) \ return sq_throwerror(v,_SC("invalid type tag")); \ if(!self || !self->IsValid()) \ return sq_throwerror(v,_SC("the stream is invalid")); @@ -44,7 +44,7 @@ SQInteger _stream_readn(HSQUIRRELVM v) sq_getinteger(v, 2, &format); switch(format) { case 'l': { - SQInteger i = 0; + SQInteger i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } @@ -119,7 +119,7 @@ SQInteger _stream_writen(HSQUIRRELVM v) sq_getinteger(v, 3, &format); switch(format) { case 'l': { - SQInteger i = 0; + SQInteger i; sq_getinteger(v, 2, &ti); i = ti; self->Write(&i, sizeof(SQInteger)); @@ -259,7 +259,7 @@ void init_streamclass(HSQUIRRELVM v) if(SQ_FAILED(sq_get(v,-2))) { sq_pushstring(v,_SC("std_stream"),-1); sq_newclass(v,SQFalse); - sq_settypetag(v,-1,(SQUserPointer)SQSTD_STREAM_TYPE_TAG); + sq_settypetag(v,-1,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)); SQInteger i = 0; while(_stream_methods[i].name != 0) { const SQRegFunction &f = _stream_methods[i]; diff --git a/squirrel/sqstdlib/sqstdstream.h b/squirrel/sqstdlib/sqstdstream.h index 867c135fdee..c7113d89a38 100644 --- a/squirrel/sqstdlib/sqstdstream.h +++ b/squirrel/sqstdlib/sqstdstream.h @@ -15,4 +15,4 @@ SQInteger _stream_flush(HSQUIRRELVM v); #define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck} SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals); -#endif /*_SQSTD_STREAM_H_*/ +#endif diff --git a/squirrel/sqstdlib/sqstdstring.cc b/squirrel/sqstdlib/sqstdstring.cc index 300da8b9ee3..7bab858fab7 100644 --- a/squirrel/sqstdlib/sqstdstring.cc +++ b/squirrel/sqstdlib/sqstdstring.cc @@ -6,11 +6,14 @@ #include #include #include +#include #define MAX_FORMAT_LEN 20 #define MAX_WFORMAT_LEN 3 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar)) +static SQUserPointer rex_typetag = NULL; + static SQBool isfmtchr(SQChar ch) { switch(ch) { @@ -69,7 +72,10 @@ SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen const SQChar *format; SQChar *dest; SQChar fmt[MAX_FORMAT_LEN]; - sq_getstring(v,nformatstringidx,&format); + const SQRESULT res = sq_getstring(v,nformatstringidx,&format); + if (SQ_FAILED(res)) { + return res; // propagate the error + } SQInteger format_size = sq_getsize(v,nformatstringidx); SQInteger allocated = (format_size+2)*sizeof(SQChar); dest = sq_getscratchpad(v,allocated); @@ -150,6 +156,38 @@ SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen return SQ_OK; } +void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,s); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,s,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + sq_pushnull(v); + } else { + sq_pushstring(v,b,r); + } +} + +static SQInteger _string_printf(HSQUIRRELVM v) +{ + SQChar *dest = NULL; + SQInteger length = 0; + if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) + return -1; + + SQPRINTFUNCTION printfunc = sq_getprintfunc(v); + if(printfunc) printfunc(v,_SC("%s"),dest); + + return 0; +} + static SQInteger _string_format(HSQUIRRELVM v) { SQChar *dest = NULL; @@ -211,16 +249,16 @@ static SQInteger _string_rstrip(HSQUIRRELVM v) static SQInteger _string_split(HSQUIRRELVM v) { const SQChar *str,*seps; - SQChar *stemp; + SQInteger sepsize; + SQBool skipempty = SQFalse; sq_getstring(v,2,&str); - sq_getstring(v,3,&seps); - SQInteger sepsize = sq_getsize(v,3); + sq_getstringandsize(v,3,&seps,&sepsize); if(sepsize == 0) return sq_throwerror(v,_SC("empty separators string")); - SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar); - stemp = sq_getscratchpad(v,memsize); - memcpy(stemp,str,memsize); - SQChar *start = stemp; - SQChar *end = stemp; + if(sq_gettop(v)>3) { + sq_getbool(v,4,&skipempty); + } + const SQChar *start = str; + const SQChar *end = str; sq_newarray(v,0); while(*end != '\0') { @@ -229,9 +267,10 @@ static SQInteger _string_split(HSQUIRRELVM v) { if(cur == seps[i]) { - *end = 0; - sq_pushstring(v,start,-1); + if(!skipempty || (end != start)) { + sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); + } start = end + 1; break; } @@ -240,7 +279,7 @@ static SQInteger _string_split(HSQUIRRELVM v) } if(end != start) { - sq_pushstring(v,start,-1); + sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } return 1; @@ -348,7 +387,9 @@ static SQInteger _string_endswith(HSQUIRRELVM v) #define SETUP_REX(v) \ SQRex *self = NULL; \ - sq_getinstanceup(v,1,(SQUserPointer *)&self,0); + if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer *)&self,rex_typetag))) { \ + return sq_throwerror(v,_SC("invalid type tag")); \ + } static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { @@ -429,6 +470,13 @@ static SQInteger _regexp_subexpcount(HSQUIRRELVM v) static SQInteger _regexp_constructor(HSQUIRRELVM v) { + SQRex *self = NULL; + if (SQ_FAILED(sq_getinstanceup(v, 1, (SQUserPointer *)&self, rex_typetag))) { + return sq_throwerror(v, _SC("invalid type tag")); + } + if (self != NULL) { + return sq_throwerror(v, _SC("invalid regexp object")); + } const SQChar *error,*pattern; sq_getstring(v,2,&pattern); SQRex *rex = sqstd_rex_compile(pattern,&error); @@ -459,10 +507,11 @@ static const SQRegFunction rexobj_funcs[]={ #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask} static const SQRegFunction stringlib_funcs[]={ _DECL_FUNC(format,-2,_SC(".s")), + _DECL_FUNC(printf,-2,_SC(".s")), _DECL_FUNC(strip,2,_SC(".s")), _DECL_FUNC(lstrip,2,_SC(".s")), _DECL_FUNC(rstrip,2,_SC(".s")), - _DECL_FUNC(split,3,_SC(".ss")), + _DECL_FUNC(split,-3,_SC(".ssb")), _DECL_FUNC(escape,2,_SC(".s")), _DECL_FUNC(startswith,3,_SC(".ss")), _DECL_FUNC(endswith,3,_SC(".ss")), @@ -475,6 +524,8 @@ SQInteger sqstd_register_stringlib(HSQUIRRELVM v) { sq_pushstring(v,_SC("regexp"),-1); sq_newclass(v,SQFalse); + rex_typetag = (SQUserPointer)rexobj_funcs; + sq_settypetag(v, -1, rex_typetag); SQInteger i = 0; while(rexobj_funcs[i].name != 0) { const SQRegFunction &f = rexobj_funcs[i]; diff --git a/squirrel/sqstdlib/sqstdsystem.cc b/squirrel/sqstdlib/sqstdsystem.cc index d6117673b8e..04de012eea0 100644 --- a/squirrel/sqstdlib/sqstdsystem.cc +++ b/squirrel/sqstdlib/sqstdsystem.cc @@ -19,6 +19,10 @@ #define scremove remove #define screname rename #endif +#ifdef IOS + #include + extern char **environ; +#endif static SQInteger _system_getenv(HSQUIRRELVM v) { @@ -35,7 +39,13 @@ static SQInteger _system_system(HSQUIRRELVM v) { const SQChar *s; if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ + #ifdef IOS + pid_t pid; + posix_spawn(&pid, s, NULL, NULL, NULL, environ); + sq_pushinteger(v, 0); + #else sq_pushinteger(v,scsystem(s)); + #endif return 1; } return sq_throwerror(v,_SC("wrong param")); diff --git a/squirrel/sqstdstring.h b/squirrel/sqstdstring.h index 124a16985f4..366e8ed060a 100644 --- a/squirrel/sqstdstring.h +++ b/squirrel/sqstdstring.h @@ -24,6 +24,8 @@ SQUIRREL_API SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *sub SQUIRREL_API SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output); +SQUIRREL_API void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...); + SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v); #ifdef __cplusplus diff --git a/squirrel/squirrel.h b/squirrel/squirrel.h index 001142105a2..2fb20135693 100644 --- a/squirrel/squirrel.h +++ b/squirrel/squirrel.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2016 Alberto Demichelis +Copyright (c) 2003-2017 Alberto Demichelis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,10 @@ THE SOFTWARE. #ifndef _SQUIRREL_H_ #define _SQUIRREL_H_ +#ifdef _SQ_CONFIG_INCLUDE +#include _SQ_CONFIG_INCLUDE +#endif + #ifdef __cplusplus extern "C" { #endif @@ -62,7 +66,7 @@ struct SQOuter; #include "sqconfig.h" #define SQUIRREL_VERSION _SC("Squirrel 3.1 stable") -#define SQUIRREL_COPYRIGHT _SC("Copyright (C) 2003-2016 Alberto Demichelis") +#define SQUIRREL_COPYRIGHT _SC("Copyright (C) 2003-2017 Alberto Demichelis") #define SQUIRREL_AUTHOR _SC("Alberto Demichelis") #define SQUIRREL_VERSION_NUMBER 310 @@ -259,6 +263,7 @@ SQUIRREL_API SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQBool sq_instanceof(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b); +SQUIRREL_API SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size); SQUIRREL_API SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c); SQUIRREL_API SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); SQUIRREL_API SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); @@ -272,7 +277,7 @@ SQUIRREL_API void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK ho SQUIRREL_API SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize); SQUIRREL_API SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi); -SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars); +SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars); SQUIRREL_API SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name); SQUIRREL_API SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p); @@ -328,6 +333,7 @@ SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err); SQUIRREL_API SQRESULT sq_throwobject(HSQUIRRELVM v); SQUIRREL_API void sq_reseterror(HSQUIRRELVM v); SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v); +SQUIRREL_API SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams); /*raw object handling*/ SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po); diff --git a/squirrel/squirrel/sqapi.cc b/squirrel/squirrel/sqapi.cc index 05f638db979..a19cd0e39ea 100644 --- a/squirrel/squirrel/sqapi.cc +++ b/squirrel/squirrel/sqapi.cc @@ -16,7 +16,7 @@ static bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o) { *o = &stack_get(v,idx); - if(type(**o) != type){ + if(sq_type(**o) != type){ SQObjectPtr oval = v->PrintObjVal(**o); v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval)); return false; @@ -150,7 +150,7 @@ void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) { - if(!ISREFCOUNTED(type(*po))) return; + if(!ISREFCOUNTED(sq_type(*po))) return; #ifdef NO_GARBAGE_COLLECTOR __AddRef(po->_type,po->_unVal); #else @@ -160,7 +160,7 @@ void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) { - if(!ISREFCOUNTED(type(*po))) return 0; + if(!ISREFCOUNTED(sq_type(*po))) return 0; #ifdef NO_GARBAGE_COLLECTOR return po->_unVal.pRefCounted->_uiRef; #else @@ -170,7 +170,7 @@ SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) { - if(!ISREFCOUNTED(type(*po))) return SQTrue; + if(!ISREFCOUNTED(sq_type(*po))) return SQTrue; #ifdef NO_GARBAGE_COLLECTOR bool ret = (po->_unVal.pRefCounted->_uiRef <= 1) ? SQTrue : SQFalse; __Release(po->_type,po->_unVal); @@ -182,7 +182,7 @@ SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM SQ_UNUSED_ARG(v), const HSQOBJECT *po) { - if (!ISREFCOUNTED(type(*po))) return 0; + if (!ISREFCOUNTED(sq_type(*po))) return 0; return po->_unVal.pRefCounted->_uiRef; } @@ -290,7 +290,7 @@ SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase) SQClass *baseclass = NULL; if(hasbase) { SQObjectPtr &base = stack_get(v,-1); - if(type(base) != OT_CLASS) + if(sq_type(base) != OT_CLASS) return sq_throwerror(v,_SC("invalid base type")); baseclass = _class(base); } @@ -304,7 +304,7 @@ SQBool sq_instanceof(HSQUIRRELVM v) { SQObjectPtr &inst = stack_get(v,-1); SQObjectPtr &cl = stack_get(v,-2); - if(type(inst) != OT_INSTANCE || type(cl) != OT_CLASS) + if(sq_type(inst) != OT_INSTANCE || sq_type(cl) != OT_CLASS) return sq_throwerror(v,_SC("invalid param type")); return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse; } @@ -394,21 +394,21 @@ void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars) v->Push(SQObjectPtr(nc)); } -SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars) +SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars) { SQObject o = stack_get(v, idx); - if(type(o) == OT_CLOSURE) { + if(sq_type(o) == OT_CLOSURE) { SQClosure *c = _closure(o); SQFunctionProto *proto = c->_function; - *nparams = (SQUnsignedInteger)proto->_nparameters; - *nfreevars = (SQUnsignedInteger)proto->_noutervalues; + *nparams = proto->_nparameters; + *nfreevars = proto->_noutervalues; return SQ_OK; } - else if(type(o) == OT_NATIVECLOSURE) + else if(sq_type(o) == OT_NATIVECLOSURE) { SQNativeClosure *c = _nativeclosure(o); - *nparams = (SQUnsignedInteger)c->_nparamscheck; - *nfreevars = c->_noutervalues; + *nparams = c->_nparamscheck; + *nfreevars = (SQInteger)c->_noutervalues; return SQ_OK; } return sq_throwerror(v,_SC("the object is not a closure")); @@ -459,7 +459,7 @@ SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx) !sq_isclass(env) && !sq_isinstance(env)) return sq_throwerror(v,_SC("invalid environment")); - SQWeakRef *w = _refcounted(env)->GetWeakRef(type(env)); + SQWeakRef *w = _refcounted(env)->GetWeakRef(sq_type(env)); SQObjectPtr ret; if(sq_isclosure(o)) { SQClosure *c = _closure(o)->Clone(); @@ -510,7 +510,7 @@ SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx) v->Pop(); return SQ_OK; } - return sq_throwerror(v, _SC("ivalid type")); + return sq_throwerror(v, _SC("invalid type")); } SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx) @@ -524,7 +524,7 @@ SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx) SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx) { SQObject &o=stack_get(v,idx); - switch(type(o)) { + switch(sq_type(o)) { case OT_TABLE: _table(o)->Clear(); break; case OT_ARRAY: _array(o)->Resize(0); break; default: @@ -558,7 +558,7 @@ SQRESULT sq_setroottable(HSQUIRRELVM v) v->Pop(); return SQ_OK; } - return sq_throwerror(v, _SC("ivalid type")); + return sq_throwerror(v, _SC("invalid type")); } SQRESULT sq_setconsttable(HSQUIRRELVM v) @@ -569,7 +569,7 @@ SQRESULT sq_setconsttable(HSQUIRRELVM v) v->Pop(); return SQ_OK; } - return sq_throwerror(v, _SC("ivalid type, expected table")); + return sq_throwerror(v, _SC("invalid type, expected table")); } void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p) @@ -619,7 +619,7 @@ void sq_push(HSQUIRRELVM v,SQInteger idx) SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx) { - return type(stack_get(v, idx)); + return sq_type(stack_get(v, idx)); } SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx) @@ -657,6 +657,10 @@ SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i) *i = tointeger(o); return SQ_OK; } + if(sq_isbool(o)) { + *i = SQVM::IsFalse(o)?SQFalse:SQTrue; + return SQ_OK; + } return SQ_ERROR; } @@ -680,6 +684,15 @@ SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b) return SQ_ERROR; } +SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_STRING,o); + *c = _stringval(*o); + *size = _string(*o)->_len; + return SQ_OK; +} + SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c) { SQObjectPtr *o = NULL; @@ -710,7 +723,7 @@ SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx) SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); - SQObjectType type = type(o); + SQObjectType type = sq_type(o); switch(type) { case OT_STRING: return _string(o)->_len; case OT_TABLE: return _table(o)->CountUsed(); @@ -741,7 +754,7 @@ SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPoint SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) { SQObjectPtr &o = stack_get(v,idx); - switch(type(o)) { + switch(sq_type(o)) { case OT_USERDATA: _userdata(o)->_typetag = typetag; break; case OT_CLASS: _class(o)->_typetag = typetag; break; default: return sq_throwerror(v,_SC("invalid object type")); @@ -751,7 +764,7 @@ SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag) { - switch(type(*o)) { + switch(sq_type(*o)) { case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break; case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break; case OT_CLASS: *typetag = _class(*o)->_typetag; break; @@ -764,7 +777,7 @@ SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag) { SQObjectPtr &o = stack_get(v,idx); if(SQ_FAILED(sq_getobjtypetag(&o,typetag))) - return sq_throwerror(v,_SC("invalid object type")); + return SQ_ERROR;// this is not an error it should be a bool but would break backward compatibility return SQ_OK; } @@ -779,7 +792,7 @@ SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p) SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) { SQObjectPtr &o = stack_get(v,idx); - if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); _instance(o)->_userpointer = p; return SQ_OK; } @@ -787,7 +800,7 @@ SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) { SQObjectPtr &o = stack_get(v,idx); - if(type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class")); + if(sq_type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class")); if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked")); _class(o)->_udsize = udsize; return SQ_OK; @@ -797,7 +810,7 @@ SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag) { SQObjectPtr &o = stack_get(v,idx); - if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); (*p) = _instance(o)->_userpointer; if(typetag != 0) { SQClass *cl = _instance(o)->_class; @@ -854,9 +867,9 @@ SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) { sq_aux_paramscheck(v, 3); SQObjectPtr &self = stack_get(v, idx); - if(type(self) == OT_TABLE || type(self) == OT_CLASS) { + if(sq_type(self) == OT_TABLE || sq_type(self) == OT_CLASS) { SQObjectPtr &key = v->GetUp(-2); - if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false); v->Pop(2); } @@ -869,7 +882,7 @@ SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) SQObjectPtr *self; _GETSAFE_OBJ(v, idx, OT_TABLE,self); SQObjectPtr &key = v->GetUp(-1); - if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); SQObjectPtr res; if(!v->DeleteSlot(*self, key, res)){ v->Pop(); @@ -894,11 +907,11 @@ SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &key = v->GetUp(-2); - if(type(key) == OT_NULL) { + if(sq_type(key) == OT_NULL) { v->Pop(2); return sq_throwerror(v, _SC("null key")); } - switch(type(self)) { + switch(sq_type(self)) { case OT_TABLE: _table(self)->NewSlot(key, v->GetUp(-1)); v->Pop(2); @@ -931,22 +944,28 @@ SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) { SQObjectPtr &self = stack_get(v, idx); - if(type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); SQObjectPtr &key = v->GetUp(-3); - if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); - if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,false)) + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,false)) { + v->Pop(3); return SQ_ERROR; + } + v->Pop(3); return SQ_OK; } SQRESULT sq_rawnewmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) { SQObjectPtr &self = stack_get(v, idx); - if(type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); SQObjectPtr &key = v->GetUp(-3); - if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); - if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,true)) + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,true)) { + v->Pop(3); return SQ_ERROR; + } + v->Pop(3); return SQ_OK; } @@ -954,19 +973,23 @@ SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &mt = v->GetUp(-1); - SQObjectType type = type(self); + SQObjectType type = sq_type(self); switch(type) { case OT_TABLE: - if(type(mt) == OT_TABLE) { - if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, _SC("delagate cycle")); v->Pop();} - else if(type(mt)==OT_NULL) { + if(sq_type(mt) == OT_TABLE) { + if(!_table(self)->SetDelegate(_table(mt))) { + return sq_throwerror(v, _SC("delegate cycle")); + } + v->Pop(); + } + else if(sq_type(mt)==OT_NULL) { _table(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v,type); break; case OT_USERDATA: - if(type(mt)==OT_TABLE) { + if(sq_type(mt)==OT_TABLE) { _userdata(self)->SetDelegate(_table(mt)); v->Pop(); } - else if(type(mt)==OT_NULL) { + else if(sq_type(mt)==OT_NULL) { _userdata(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v, type); break; @@ -997,7 +1020,7 @@ SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self=stack_get(v,idx); - switch(type(self)){ + switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: if(!_delegable(self)->_delegate){ @@ -1026,7 +1049,7 @@ SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self=stack_get(v,idx); SQObjectPtr &obj = v->GetUp(-1); - switch(type(self)) { + switch(sq_type(self)) { case OT_TABLE: if(_table(self)->Get(obj,obj)) return SQ_OK; @@ -1055,6 +1078,7 @@ SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) v->Pop(); return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); } + // modified: call Raise_IdxError v->Raise_IdxError(v->GetUp(-1)); v->Pop(); return SQ_ERROR; @@ -1077,7 +1101,7 @@ const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedIntege stackbase-=ci._prevstkbase; } SQVM::CallInfo &ci=v->_callsstack[lvl]; - if(type(ci._closure)!=OT_CLOSURE) + if(sq_type(ci._closure)!=OT_CLOSURE) return NULL; SQClosure *c=_closure(ci._closure); SQFunctionProto *func=c->_function; @@ -1138,7 +1162,7 @@ SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize) SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) { - if (type(v->GetUp(-1)) == OT_GENERATOR) + if (sq_type(v->GetUp(-1)) == OT_GENERATOR) { v->PushNull(); //retval if (!v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), raiseerror, SQVM::ET_RESUME_GENERATOR)) @@ -1150,6 +1174,7 @@ SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) return sq_throwerror(v,_SC("only generators can be resumed")); } +/* modified for our purposes to handle suspended scripts */ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) { SQObjectPtr res; @@ -1175,6 +1200,27 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) } } +/* TODO check whether this works with our suspend logic; wont be missed +SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) +{ + SQObjectPtr &res = v->GetUp(-(nparams + 1)); + if (sq_type(res) != OT_CLOSURE) { + return sq_throwerror(v, _SC("only closure can be tail called")); + } + SQClosure *clo = _closure(res); + if (clo->_function->_bgenerator) + { + return sq_throwerror(v, _SC("generators cannot be tail called")); + } + + SQInteger stackbase = (v->_top - nparams) - v->_stackbase; + if (!v->TailCall(clo, stackbase, nparams)) { + return SQ_ERROR; + } + return SQ_TAILCALL_FLAG; +} +*/ + SQRESULT sq_suspendvm(HSQUIRRELVM v) { return v->Suspend(); @@ -1203,29 +1249,24 @@ SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseer void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook) { - if(sq_gettop(v) >= 1){ - SQObjectPtr &ud=stack_get(v,idx); - switch( type(ud) ) { - case OT_USERDATA: _userdata(ud)->_hook = hook; break; - case OT_INSTANCE: _instance(ud)->_hook = hook; break; - case OT_CLASS: _class(ud)->_hook = hook; break; - default: break; //shutup compiler - } + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: _userdata(ud)->_hook = hook; break; + case OT_INSTANCE: _instance(ud)->_hook = hook; break; + case OT_CLASS: _class(ud)->_hook = hook; break; + default: return; } } SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx) { - if(sq_gettop(v) >= 1){ - SQObjectPtr &ud=stack_get(v,idx); - switch( type(ud) ) { - case OT_USERDATA: return _userdata(ud)->_hook; break; - case OT_INSTANCE: return _instance(ud)->_hook; break; - case OT_CLASS: return _class(ud)->_hook; break; - default: break; //shutup compiler - } + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: return _userdata(ud)->_hook; break; + case OT_INSTANCE: return _instance(ud)->_hook; break; + case OT_CLASS: return _class(ud)->_hook; break; + default: return NULL; } - return NULL; } void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f) @@ -1239,7 +1280,7 @@ SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up) _GETSAFE_OBJ(v, -1, OT_CLOSURE,o); unsigned short tag = SQ_BYTECODE_STREAM_TAG; if(_closure(*o)->_function->_noutervalues) - return sq_throwerror(v,_SC("a closure with free valiables bound it cannot be serialized")); + return sq_throwerror(v,_SC("a closure with free variables bound cannot be serialized")); if(w(up,&tag,2) != 2) return sq_throwerror(v,_SC("io error")); if(!_closure(*o)->Save(v,up,w)) @@ -1300,7 +1341,7 @@ const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger n { SQObjectPtr &self=stack_get(v,idx); const SQChar *name = NULL; - switch(type(self)) + switch(sq_type(self)) { case OT_CLOSURE:{ SQClosure *clo = _closure(self); @@ -1328,7 +1369,7 @@ const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger n SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) { SQObjectPtr &self=stack_get(v,idx); - switch(type(self)) + switch(sq_type(self)) { case OT_CLOSURE:{ SQFunctionProto *fp = _closure(self)->_function; @@ -1345,7 +1386,7 @@ SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) else return sq_throwerror(v,_SC("invalid free var index")); break; default: - return sq_aux_invalidtype(v,type(self)); + return sq_aux_invalidtype(v, sq_type(self)); } v->Pop(); return SQ_OK; @@ -1358,7 +1399,7 @@ SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx) SQObjectPtr &key = stack_get(v,-2); SQObjectPtr &val = stack_get(v,-1); SQObjectPtr attrs; - if(type(key) == OT_NULL) { + if(sq_type(key) == OT_NULL) { attrs = _class(*o)->_attributes; _class(*o)->_attributes = val; v->Pop(2); @@ -1379,7 +1420,7 @@ SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx) _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQObjectPtr &key = stack_get(v,-1); SQObjectPtr attrs; - if(type(key) == OT_NULL) { + if(sq_type(key) == OT_NULL) { attrs = _class(*o)->_attributes; v->Pop(); v->Push(attrs); @@ -1411,7 +1452,7 @@ SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle) SQRESULT _getmemberbyhandle(HSQUIRRELVM v,SQObjectPtr &self,const HSQMEMBERHANDLE *handle,SQObjectPtr *&val) { - switch(type(self)) { + switch(sq_type(self)) { case OT_INSTANCE: { SQInstance *i = _instance(self); if(handle->_static) { @@ -1494,8 +1535,8 @@ SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx) void sq_weakref(HSQUIRRELVM v,SQInteger idx) { SQObject &o=stack_get(v,idx); - if(ISREFCOUNTED(type(o))) { - v->Push(_refcounted(o)->GetWeakRef(type(o))); + if(ISREFCOUNTED(sq_type(o))) { + v->Push(_refcounted(o)->GetWeakRef(sq_type(o))); return; } v->Push(o); @@ -1504,7 +1545,7 @@ void sq_weakref(HSQUIRRELVM v,SQInteger idx) SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); - if(type(o) != OT_WEAKREF) { + if(sq_type(o) != OT_WEAKREF) { return sq_throwerror(v,_SC("the object must be a weakref")); } v->Push(_weakref(o)->_obj); @@ -1533,7 +1574,7 @@ SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t) SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val; - if(type(o) == OT_GENERATOR) { + if(sq_type(o) == OT_GENERATOR) { return sq_throwerror(v,_SC("cannot iterate a generator")); } int faketojump; diff --git a/squirrel/squirrel/sqbaselib.cc b/squirrel/squirrel/sqbaselib.cc index b50184eb7be..130f2a191d2 100644 --- a/squirrel/squirrel/sqbaselib.cc +++ b/squirrel/squirrel/sqbaselib.cc @@ -156,7 +156,14 @@ static SQInteger base_getstackinfos(HSQUIRRELVM v) static SQInteger base_assert(HSQUIRRELVM v) { if(SQVM::IsFalse(stack_get(v,2))){ - return sq_throwerror(v,_SC("assertion failed")); + SQInteger top = sq_gettop(v); + if (top>2 && SQ_SUCCEEDED(sq_tostring(v,3))) { + const SQChar *str = 0; + if (SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + return sq_throwerror(v, str); + } + } + return sq_throwerror(v, _SC("assertion failed")); } return 0; } @@ -169,7 +176,7 @@ static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx, o=stack_get(v,1); if(top>1){ SQObjectPtr &start=stack_get(v,2); - if(type(start)!=OT_NULL && sq_isnumeric(start)){ + if(sq_type(start)!=OT_NULL && sq_isnumeric(start)){ sidx=tointeger(start); } } @@ -283,7 +290,7 @@ static const SQRegFunction base_funcs[]={ {_SC("setroottable"),base_setroottable,2, NULL}, {_SC("getconsttable"),base_getconsttable,1, NULL}, {_SC("setconsttable"),base_setconsttable,2, NULL}, - {_SC("assert"),base_assert,2, NULL}, + {_SC("assert"),base_assert,-2, NULL}, {_SC("print"),base_print,2, NULL}, {_SC("error"),base_error,2, NULL}, {_SC("compilestring"),base_compilestring,-2, _SC(".ss")}, @@ -340,7 +347,7 @@ static SQInteger default_delegate_len(HSQUIRRELVM v) static SQInteger default_delegate_tofloat(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,1); - switch(type(o)){ + switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(str2num(_stringval(o),res,10)){ @@ -369,7 +376,7 @@ static SQInteger default_delegate_tointeger(HSQUIRRELVM v) if(sq_gettop(v) > 1) { sq_getinteger(v,2,&base); } - switch(type(o)){ + switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(str2num(_stringval(o),res,base)){ @@ -406,7 +413,7 @@ static SQInteger obj_delegate_weakref(HSQUIRRELVM v) static SQInteger obj_clear(HSQUIRRELVM v) { - return sq_clear(v,-1); + return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR; } @@ -443,7 +450,7 @@ static SQInteger container_rawexists(HSQUIRRELVM v) static SQInteger container_rawset(HSQUIRRELVM v) { - return sq_rawset(v,-3); + return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR; } @@ -465,6 +472,58 @@ static SQInteger table_getdelegate(HSQUIRRELVM v) return SQ_SUCCEEDED(sq_getdelegate(v,-1))?1:SQ_ERROR; } +static SQInteger table_filter(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQTable *tbl = _table(o); + SQObjectPtr ret = SQTable::Create(_ss(v),0); + + SQObjectPtr itr, key, val; + SQInteger nitr; + while((nitr = tbl->Next(false, itr, key, val)) != -1) { + itr = (SQInteger)nitr; + + v->Push(o); + v->Push(key); + v->Push(val); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + if(!SQVM::IsFalse(v->GetUp(-1))) { + _table(ret)->NewSlot(key, val); + } + v->Pop(); + } + + v->Push(ret); + return 1; +} + +#define TABLE_TO_ARRAY_FUNC(_funcname_,_valname_) static SQInteger _funcname_(HSQUIRRELVM v) \ +{ \ + SQObject &o = stack_get(v, 1); \ + SQTable *t = _table(o); \ + SQObjectPtr itr, key, val; \ + SQObjectPtr _null; \ + SQInteger nitr, n = 0; \ + SQInteger nitems = t->CountUsed(); \ + SQArray *a = SQArray::Create(_ss(v), nitems); \ + a->Resize(nitems, _null); \ + if (nitems) { \ + while ((nitr = t->Next(false, itr, key, val)) != -1) { \ + itr = (SQInteger)nitr; \ + a->Set(n, _valname_); \ + n++; \ + } \ + } \ + v->Push(a); \ + return 1; \ +} + +TABLE_TO_ARRAY_FUNC(table_keys, key) +TABLE_TO_ARRAY_FUNC(table_values, val) + + const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("t")}, {_SC("rawget"),container_rawget,2, _SC("t")}, @@ -476,6 +535,9 @@ const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ {_SC("clear"),obj_clear,1, _SC(".")}, {_SC("setdelegate"),table_setdelegate,2, _SC(".t|o")}, {_SC("getdelegate"),table_getdelegate,1, _SC(".")}, + {_SC("filter"),table_filter,2, _SC("tc")}, + {_SC("keys"),table_keys,1, _SC("t") }, + {_SC("values"),table_values,1, _SC("t") }, {NULL,(SQFUNCTION)0,0,NULL} }; @@ -483,18 +545,19 @@ const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ static SQInteger array_append(HSQUIRRELVM v) { - return sq_arrayappend(v,-2); + return SQ_SUCCEEDED(sq_arrayappend(v,-2)) ? 1 : SQ_ERROR; } static SQInteger array_extend(HSQUIRRELVM v) { _array(stack_get(v,1))->Extend(_array(stack_get(v,2))); - return 0; + sq_pop(v,1); + return 1; } static SQInteger array_reverse(HSQUIRRELVM v) { - return sq_arrayreverse(v,-1); + return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR; } static SQInteger array_pop(HSQUIRRELVM v) @@ -519,7 +582,8 @@ static SQInteger array_insert(HSQUIRRELVM v) SQObject &val=stack_get(v,3); if(!_array(o)->Insert(tointeger(idx),val)) return sq_throwerror(v,_SC("index out of range")); - return 0; + sq_pop(v,2); + return 1; } static SQInteger array_remove(HSQUIRRELVM v) @@ -542,10 +606,15 @@ static SQInteger array_resize(HSQUIRRELVM v) SQObject &nsize = stack_get(v, 2); SQObjectPtr fill; if(sq_isnumeric(nsize)) { + SQInteger sz = tointeger(nsize); + if (sz<0) + return sq_throwerror(v, _SC("resizing to negative length")); + if(sq_gettop(v) > 2) fill = stack_get(v, 3); - _array(o)->Resize(tointeger(nsize),fill); - return 0; + _array(o)->Resize(sz,fill); + sq_settop(v, 1); + return 1; } return sq_throwerror(v, _SC("size must be a number")); } @@ -553,16 +622,36 @@ static SQInteger array_resize(HSQUIRRELVM v) static SQInteger __map_array(SQArray *dest,SQArray *src,HSQUIRRELVM v) { SQObjectPtr temp; SQInteger size = src->Size(); + SQObject &closure = stack_get(v, 2); + v->Push(closure); + + SQInteger nArgs = 0; + if(sq_type(closure) == OT_CLOSURE) { + nArgs = _closure(closure)->_function->_nparameters; + } + else if (sq_type(closure) == OT_NATIVECLOSURE) { + SQInteger nParamsCheck = _nativeclosure(closure)->_nparamscheck; + if (nParamsCheck > 0) + nArgs = nParamsCheck; + else // push all params when there is no check or only minimal count set + nArgs = 4; + } + for(SQInteger n = 0; n < size; n++) { src->Get(n,temp); v->Push(src); v->Push(temp); - if(SQ_FAILED(sq_call(v,2,SQTrue,SQFalse))) { + if (nArgs >= 3) + v->Push(SQObjectPtr(n)); + if (nArgs >= 4) + v->Push(src); + if(SQ_FAILED(sq_call(v,nArgs,SQTrue,SQFalse))) { return SQ_ERROR; } dest->Set(n,v->GetUp(-1)); v->Pop(); } + v->Pop(); return 0; } @@ -582,7 +671,8 @@ static SQInteger array_apply(HSQUIRRELVM v) SQObject &o = stack_get(v,1); if(SQ_FAILED(__map_array(_array(o),_array(o),v))) return SQ_ERROR; - return 0; + sq_pop(v,1); + return 1; } static SQInteger array_reduce(HSQUIRRELVM v) @@ -590,14 +680,21 @@ static SQInteger array_reduce(HSQUIRRELVM v) SQObject &o = stack_get(v,1); SQArray *a = _array(o); SQInteger size = a->Size(); - if(size == 0) { - return 0; - } SQObjectPtr res; + SQInteger iterStart; + if (sq_gettop(v)>2) { + res = stack_get(v,3); + iterStart = 0; + } else if (size==0) { + return 0; + } else { a->Get(0,res); - if(size > 1) { + iterStart = 1; + } + if (size > iterStart) { SQObjectPtr other; - for(SQInteger n = 1; n < size; n++) { + v->Push(stack_get(v,2)); + for (SQInteger n = iterStart; n < size; n++) { a->Get(n,other); v->Push(o); v->Push(res); @@ -608,6 +705,7 @@ static SQInteger array_reduce(HSQUIRRELVM v) res = v->GetUp(-1); v->Pop(); } + v->Pop(); } v->Push(res); return 1; @@ -656,7 +754,7 @@ static SQInteger array_find(HSQUIRRELVM v) } -static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) +static bool _sort_compare(HSQUIRRELVM v, SQArray *arr, SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) { if(func < 0) { if(!v->ObjCmp(a,b,ret)) return false; @@ -667,6 +765,8 @@ static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger sq_pushroottable(v); v->Push(a); v->Push(b); + SQObjectPtr *valptr = arr->_values._vals; + SQUnsignedInteger precallsize = arr->_values.size(); if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) { if(!sq_isstring( v->_lasterror)) v->Raise_Error(_SC("compare func failed")); @@ -676,6 +776,10 @@ static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger v->Raise_Error(_SC("numeric value expected as return value of the compare function")); return false; } + if (precallsize != arr->_values.size() || valptr != arr->_values._vals) { + v->Raise_Error(_SC("array resized during sort operation")); + return false; + } sq_settop(v, top); return true; } @@ -694,7 +798,7 @@ static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteg maxChild = root2; } else { - if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret)) + if(!_sort_compare(v,arr,arr->_values[root2],arr->_values[root2 + 1],func,ret)) return false; if (ret > 0) { maxChild = root2; @@ -704,7 +808,7 @@ static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteg } } - if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret)) + if(!_sort_compare(v,arr,arr->_values[root],arr->_values[maxChild],func,ret)) return false; if (ret < 0) { if (root == maxChild) { @@ -725,7 +829,7 @@ static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteg static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),SQInteger func) { SQArray *a = _array(arr); - SQInteger i = 0; + SQInteger i; SQInteger array_size = a->Size(); for (i = (array_size / 2); i >= 0; i--) { if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; @@ -749,7 +853,8 @@ static SQInteger array_sort(HSQUIRRELVM v) return SQ_ERROR; } - return 0; + sq_settop(v,1); + return 1; } static SQInteger array_slice(HSQUIRRELVM v) @@ -792,7 +897,7 @@ const SQRegFunction SQSharedState::_array_default_delegate_funcz[]={ {_SC("clear"),obj_clear,1, _SC(".")}, {_SC("map"),array_map,2, _SC("ac")}, {_SC("apply"),array_apply,2, _SC("ac")}, - {_SC("reduce"),array_reduce,2, _SC("ac")}, + {_SC("reduce"),array_reduce,-2, _SC("ac.")}, {_SC("filter"),array_filter,2, _SC("ac")}, {_SC("find"),array_find,2, _SC("a.")}, {NULL,(SQFUNCTION)0,0,NULL} @@ -850,9 +955,14 @@ static SQInteger string_find(HSQUIRRELVM v) return 1; \ } +static char toalnum(char c) +{ + return isalnum(c) && c >=0 ? c : '_'; +} STRING_TOFUNCZ(tolower) STRING_TOFUNCZ(toupper) +STRING_TOFUNCZ(toalnum) const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ {_SC("len"),default_delegate_len,1, _SC("s")}, @@ -863,6 +973,7 @@ const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ {_SC("find"),string_find,-2, _SC("s s n")}, {_SC("tolower"),string_tolower,-1, _SC("s n n")}, {_SC("toupper"),string_toupper,-1, _SC("s n n")}, + {_SC("toalnum"),string_toalnum,1, _SC("s")}, {_SC("weakref"),obj_delegate_weakref,1, NULL }, {NULL,(SQFUNCTION)0,0,NULL} }; @@ -885,7 +996,14 @@ static SQInteger closure_pcall(HSQUIRRELVM v) static SQInteger closure_call(HSQUIRRELVM v) { - return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQTrue))?1:SQ_ERROR; + /* has to wait until sq_tailcall is enabled + SQObjectPtr &c = stack_get(v, -1); + if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) + { + return sq_tailcall(v, sq_gettop(v) - 1); + } + */ + return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; } static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror) @@ -931,7 +1049,7 @@ static SQInteger closure_setroot(HSQUIRRELVM v) static SQInteger closure_getinfos(HSQUIRRELVM v) { SQObject o = stack_get(v,1); SQTable *res = SQTable::Create(_ss(v),4); - if(type(o) == OT_CLOSURE) { + if(sq_type(o) == OT_CLOSURE) { SQFunctionProto *f = _closure(o)->_function; SQInteger nparams = f->_nparameters + (f->_varparams?1:0); SQObjectPtr params = SQArray::Create(_ss(v),nparams); @@ -1010,7 +1128,7 @@ const SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={ static SQInteger thread_call(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); - if(type(o) == OT_THREAD) { + if(sq_type(o) == OT_THREAD) { SQInteger nparams = sq_gettop(v); _thread(o)->Push(_thread(o)->_roottable); for(SQInteger i = 2; i<(nparams+1); i++) @@ -1029,7 +1147,7 @@ static SQInteger thread_call(HSQUIRRELVM v) static SQInteger thread_wakeup(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); - if(type(o) == OT_THREAD) { + if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { @@ -1065,7 +1183,7 @@ static SQInteger thread_wakeup(HSQUIRRELVM v) static SQInteger thread_wakeupthrow(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); - if(type(o) == OT_THREAD) { + if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { @@ -1125,7 +1243,7 @@ static SQInteger thread_getstatus(HSQUIRRELVM v) static SQInteger thread_getstackinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); - if(type(o) == OT_THREAD) { + if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger threadtop = sq_gettop(thread); SQInteger level; @@ -1134,7 +1252,7 @@ static SQInteger thread_getstackinfos(HSQUIRRELVM v) if(SQ_FAILED(res)) { sq_settop(thread,threadtop); - if(type(thread->_lasterror) == OT_STRING) { + if(sq_type(thread->_lasterror) == OT_STRING) { sq_throwerror(v,_stringval(thread->_lasterror)); } else { diff --git a/squirrel/squirrel/sqclass.cc b/squirrel/squirrel/sqclass.cc index 7a7dec2346d..b5ab34bb421 100644 --- a/squirrel/squirrel/sqclass.cc +++ b/squirrel/squirrel/sqclass.cc @@ -53,7 +53,7 @@ SQClass::~SQClass() bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { SQObjectPtr temp; - bool belongs_to_static_table = type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE || bstatic; + bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic; if(_locked && !belongs_to_static_table) return false; //the class already has an instance so cannot be modified if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value @@ -61,20 +61,23 @@ bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr _defaultvalues[_member_idx(temp)].val = val; return true; } + if (_members->CountUsed() >= MEMBER_MAX_COUNT) { + return false; + } if(belongs_to_static_table) { SQInteger mmidx; - if((type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE) && + if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) { _metamethods[mmidx] = val; } else { SQObjectPtr theval = val; - if(_base && type(val) == OT_CLOSURE) { + if(_base && sq_type(val) == OT_CLOSURE) { theval = _closure(val)->Clone(); _closure(theval)->_base = _base; __ObjAddRef(_base); //ref for the closure } - if(type(temp) == OT_NULL) { + if(sq_type(temp) == OT_NULL) { bool isconstructor; SQVM::IsEqual(ss->_constructoridx, key, isconstructor); if(isconstructor) { @@ -191,7 +194,7 @@ SQInstance::~SQInstance() bool SQInstance::GetMetaMethod(SQVM* SQ_UNUSED_ARG(v),SQMetaMethod mm,SQObjectPtr &res) { - if(type(_class->_metamethods[mm]) != OT_NULL) { + if(sq_type(_class->_metamethods[mm]) != OT_NULL) { res = _class->_metamethods[mm]; return true; } diff --git a/squirrel/squirrel/sqclass.h b/squirrel/squirrel/sqclass.h index d998daf96d1..0083d6e41b8 100644 --- a/squirrel/squirrel/sqclass.h +++ b/squirrel/squirrel/sqclass.h @@ -17,6 +17,7 @@ typedef sqvector SQClassMemberVec; #define MEMBER_TYPE_METHOD 0x01000000 #define MEMBER_TYPE_FIELD 0x02000000 +#define MEMBER_MAX_COUNT 0x00FFFFFF #define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD) #define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD) diff --git a/squirrel/squirrel/sqcompiler.cc b/squirrel/squirrel/sqcompiler.cc index d17522ca5af..774d9cfbe20 100644 --- a/squirrel/squirrel/sqcompiler.cc +++ b/squirrel/squirrel/sqcompiler.cc @@ -5,7 +5,6 @@ #ifndef NO_COMPILER #include #include -#include #include "sqopcodes.h" #include "sqstring.h" #include "sqfuncproto.h" @@ -192,7 +191,7 @@ class SQCompiler } else { if(_raiseerror && _ss(_vm)->_compilererrorhandler) { - _ss(_vm)->_compilererrorhandler(_vm, _compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), + _ss(_vm)->_compilererrorhandler(_vm, _compilererror, sq_type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), _lex._currentline, _lex._currentcolumn); } _vm->_lasterror = SQString::Create(_ss(_vm), _compilererror, -1); @@ -444,8 +443,8 @@ class SQCompiler Expression(); SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); - _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); - _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + _fs->SetInstructionParam(jzpos, 1, endfirstexp - jzpos + 1); _fs->SnoozeOpt(); } break; @@ -467,6 +466,7 @@ class SQCompiler INVOKE_EXP(f); SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); + _es.etype = EXPR; } void LogicalOrExp() { @@ -482,7 +482,8 @@ class SQCompiler SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SnoozeOpt(); - _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; break; }else return; } @@ -501,7 +502,8 @@ class SQCompiler SQInteger second_exp = _fs->PopTarget(); if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SnoozeOpt(); - _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; break; } @@ -633,7 +635,7 @@ class SQCompiler } break; case _SC('['): - if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration")); + if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot break deref/or comma needed after [exp]=exp slot declaration")); Lex(); Expression(); Expect(_SC(']')); pos = -1; if(_es.etype==BASE) { @@ -763,7 +765,7 @@ class SQCompiler /* Handle named constant */ SQObjectPtr constval; SQObject constid; - if(type(constant) == OT_TABLE) { + if(sq_type(constant) == OT_TABLE) { Expect('.'); constid = Expect(TK_IDENTIFIER); if(!_table(constant)->Get(constid, constval)) { @@ -777,7 +779,7 @@ class SQCompiler _es.epos = _fs->PushTarget(); /* generate direct or literal function depending on size */ - SQObjectType ctype = type(constval); + SQObjectType ctype = sq_type(constval); switch(ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break; case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break; @@ -832,7 +834,7 @@ class SQCompiler _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK); key++; } - _fs->SetIntructionParam(apos, 1, key); + _fs->SetInstructionParam(apos, 1, key); Lex(); } break; @@ -860,6 +862,7 @@ class SQCompiler case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break; case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break; case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break; + case TK_RAWCALL: Lex(); Expect('('); FunctionCallArgs(true); break; case TK_MINUSMINUS : case TK_PLUSPLUS :PrefixIncDec(_token); break; case TK_DELETE : DeleteExpr(); break; @@ -890,9 +893,7 @@ class SQCompiler target = _fs->PushTarget(); } if(sizeof(SQFloat) == sizeof(SQInt32)) { - SQInt32 v; - memcpy(&v, &value, sizeof(SQInt32)); - _fs->AddInstruction(_OP_LOADFLOAT, target, v); + _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value)); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); @@ -918,7 +919,7 @@ class SQCompiler } return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('[')))); } - void FunctionCallArgs() + void FunctionCallArgs(bool rawcall = false) { SQInteger nargs = 1;//this while(_token != _SC(')')) { @@ -931,10 +932,52 @@ class SQCompiler } } Lex(); + if (rawcall) { + if (nargs < 3) Error(_SC("rawcall requires at least 2 parameters (callee and this)")); + nargs -= 2; //removes callee and this from count + } for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); SQInteger stackbase = _fs->PopTarget(); SQInteger closure = _fs->PopTarget(); _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); +#if 0 + /* + * disabled as it breaks existing scripts like + * + * local a = [] + * local b = a.len() + * // open some new block + * { + * local c = 0 + * } + * + * see https://github.com/albertodemichelis/squirrel/issues/177 + */ + if (_token == '{') + { + SQInteger retval = _fs->TopTarget(); + SQInteger nkeys = 0; + Lex(); + while (_token != '}') { + switch (_token) { + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + default: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + break; + } + if (_token == ',') Lex(); + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + _fs->AddInstruction(_OP_SET, 0xFF, retval, key, val); + } + Lex(); + } +#endif } void ParseTableOrClass(SQInteger separator,SQInteger terminator) { @@ -997,7 +1040,7 @@ class SQCompiler } } if(separator == _SC(',')) //hack recognizes a table from the separator - _fs->SetIntructionParam(tpos, 1, nkeys); + _fs->SetInstructionParam(tpos, 1, nkeys); Lex(); } void LocalDeclStatement() @@ -1047,8 +1090,10 @@ class SQCompiler } } else { + //BEGIN_SCOPE(); Statement(); if (_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + //END_SCOPE(); } } void IfStatement() @@ -1059,18 +1104,35 @@ class SQCompiler _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jnepos = _fs->GetCurrentPos(); + + IfBlock(); + // + /*static int n = 0; + if (_token != _SC('}') && _token != TK_ELSE) { + printf("IF %d-----------------------!!!!!!!!!\n", n); + if (n == 5) + { + printf("asd"); + } + n++; + //OptionalSemicolon(); + }*/ + SQInteger endifblock = _fs->GetCurrentPos(); if(_token == TK_ELSE){ haselse = true; + //BEGIN_SCOPE(); _fs->AddInstruction(_OP_JMP); jmppos = _fs->GetCurrentPos(); Lex(); + //Statement(); if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); IfBlock(); - _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + //END_SCOPE(); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); } - _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); + _fs->SetInstructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); } void WhileStatement() { @@ -1087,7 +1149,7 @@ class SQCompiler END_SCOPE(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); - _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKBLE_BLOCK(jmppos); } @@ -1146,10 +1208,11 @@ class SQCompiler _fs->AddInstruction(exp[i]); } _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); - if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); - END_SCOPE(); + if(jzpos> 0) _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKBLE_BLOCK(continuetrg); + + END_SCOPE(); } void ForEachStatement() { @@ -1186,8 +1249,8 @@ class SQCompiler BEGIN_BREAKBLE_BLOCK() Statement(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); - _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); - _fs->SetIntructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); + _fs->SetInstructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); + _fs->SetInstructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); END_BREAKBLE_BLOCK(foreachpos - 1); //restore the local variable stack(remove index,val and ref idx) _fs->PopTarget(); @@ -1207,7 +1270,7 @@ class SQCompiler if(!bfirst) { _fs->AddInstruction(_OP_JMP, 0, 0); skipcondjmp = _fs->GetCurrentPos(); - _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); } //condition Lex(); Expression(); Expect(_SC(':')); @@ -1225,7 +1288,7 @@ class SQCompiler //end condition if(skipcondjmp != -1) { - _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); + _fs->SetInstructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); } tonextcondjmp = _fs->GetCurrentPos(); BEGIN_SCOPE(); @@ -1234,7 +1297,7 @@ class SQCompiler bfirst = false; } if(tonextcondjmp != -1) - _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); if(_token == TK_DEFAULT) { Lex(); Expect(_SC(':')); BEGIN_SCOPE(); @@ -1325,7 +1388,7 @@ class SQCompiler } break; default: - Error(_SC("scalar expected : integer,float or string")); + Error(_SC("scalar expected : integer, float, or string")); } Lex(); return val; @@ -1378,14 +1441,14 @@ class SQCompiler if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; _fs->AddInstruction(_OP_JMP, 0, 0); SQInteger jmppos = _fs->GetCurrentPos(); - _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); + _fs->SetInstructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); { BEGIN_SCOPE(); SQInteger ex_target = _fs->PushLocalVariable(exid); - _fs->SetIntructionParam(trappos, 0, ex_target); + _fs->SetInstructionParam(trappos, 0, ex_target); Statement(); - _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); + _fs->SetInstructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); END_SCOPE(); } } @@ -1523,7 +1586,7 @@ class SQCompiler SQInteger pos = funcstate->_unresolvedbreaks.back(); funcstate->_unresolvedbreaks.pop_back(); //set the jmp instruction - funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); + funcstate->SetInstructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); ntoresolve--; } } @@ -1533,7 +1596,7 @@ class SQCompiler SQInteger pos = funcstate->_unresolvedcontinues.back(); funcstate->_unresolvedcontinues.pop_back(); //set the jmp instruction - funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0); + funcstate->SetInstructionParams(pos, 0, targetpos - pos, 0); ntoresolve--; } } diff --git a/squirrel/squirrel/sqcompiler.h b/squirrel/squirrel/sqcompiler.h index 94bbd006cc8..b0c4190320b 100644 --- a/squirrel/squirrel/sqcompiler.h +++ b/squirrel/squirrel/sqcompiler.h @@ -70,6 +70,7 @@ struct SQVM; #define TK_STATIC 322 #define TK_ENUM 323 #define TK_CONST 324 +#define TK_RAWCALL 325 diff --git a/squirrel/squirrel/sqdebug.cc b/squirrel/squirrel/sqdebug.cc index 4f8796a174f..dcf35c09012 100644 --- a/squirrel/squirrel/sqdebug.cc +++ b/squirrel/squirrel/sqdebug.cc @@ -17,8 +17,8 @@ SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi) SQClosure *c = _closure(ci._closure); SQFunctionProto *proto = c->_function; fi->funcid = proto; - fi->name = type(proto->_name) == OT_STRING?_stringval(proto->_name):_SC("unknown"); - fi->source = type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):_SC("unknown"); + fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):_SC("unknown"); + fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):_SC("unknown"); fi->line = proto->_lineinfos[0]._line; return SQ_OK; } @@ -32,12 +32,12 @@ SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) if (cssize > level) { memset(si, 0, sizeof(SQStackInfos)); SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; - switch (type(ci._closure)) { + switch (sq_type(ci._closure)) { case OT_CLOSURE:{ SQFunctionProto *func = _closure(ci._closure)->_function; - if (type(func->_name) == OT_STRING) + if (sq_type(func->_name) == OT_STRING) si->funcname = _stringval(func->_name); - if (type(func->_sourcename) == OT_STRING) + if (sq_type(func->_sourcename) == OT_STRING) si->source = _stringval(func->_sourcename); si->line = func->GetLine(ci._ip); } @@ -45,7 +45,7 @@ SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) case OT_NATIVECLOSURE: si->source = _SC("NATIVE"); si->funcname = _SC("unknown"); - if(type(_nativeclosure(ci._closure)->_name) == OT_STRING) + if(sq_type(_nativeclosure(ci._closure)->_name) == OT_STRING) si->funcname = _stringval(_nativeclosure(ci._closure)->_name); si->line = -1; break; @@ -78,7 +78,7 @@ void SQVM::Raise_Error(const SQObjectPtr &desc) SQString *SQVM::PrintObjVal(const SQObjectPtr &o) { - switch(type(o)) { + switch(sq_type(o)) { case OT_STRING: return _string(o); case OT_INTEGER: scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR), _PRINT_INT_FMT, _integer(o)); @@ -112,7 +112,7 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty SQInteger found = 0; for(SQInteger i=0; i<16; i++) { - SQInteger mask = 0x00000001 << i; + SQInteger mask = ((SQInteger)1) << i; if(typemask & (mask)) { if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes); found ++; diff --git a/squirrel/squirrel/sqfuncproto.h b/squirrel/squirrel/sqfuncproto.h index 267c83e9733..1ac4c9ead4a 100644 --- a/squirrel/squirrel/sqfuncproto.h +++ b/squirrel/squirrel/sqfuncproto.h @@ -19,12 +19,6 @@ struct SQOuterVar _src=src; _type=t; } - SQOuterVar(const SQOuterVar &ov) - { - _type=ov._type; - _src=ov._src; - _name=ov._name; - } SQOuterType _type; SQObjectPtr _name; SQObjectPtr _src; @@ -33,13 +27,6 @@ struct SQOuterVar struct SQLocalVarInfo { SQLocalVarInfo():_start_op(0),_end_op(0),_pos(0){} - SQLocalVarInfo(const SQLocalVarInfo &lvi) - { - _name=lvi._name; - _start_op=lvi._start_op; - _end_op=lvi._end_op; - _pos=lvi._pos; - } SQObjectPtr _name; SQUnsignedInteger _start_op; SQUnsignedInteger _end_op; diff --git a/squirrel/squirrel/sqfuncstate.cc b/squirrel/squirrel/sqfuncstate.cc index 90f4b64059d..e74ad7cbd65 100644 --- a/squirrel/squirrel/sqfuncstate.cc +++ b/squirrel/squirrel/sqfuncstate.cc @@ -77,7 +77,7 @@ SQInstructionDesc g_InstrDesc[]={ #endif void DumpLiteral(SQObjectPtr &o) { - switch(type(o)){ + switch(sq_type(o)){ case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break; case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break; case OT_INTEGER: scprintf(_SC("{") _PRINT_INT_FMT _SC("}"),_integer(o));break; @@ -117,10 +117,10 @@ void SQFuncState::Dump(SQFunctionProto *func) { SQUnsignedInteger n=0,i; SQInteger si; - scprintf(_SC("SQInstruction sizeof %d\n"),sizeof(SQInstruction)); - scprintf(_SC("SQObject sizeof %d\n"),sizeof(SQObject)); + scprintf(_SC("SQInstruction sizeof %d\n"),(SQInt32)sizeof(SQInstruction)); + scprintf(_SC("SQObject sizeof %d\n"), (SQInt32)sizeof(SQObject)); scprintf(_SC("--------------------------------------------------------------------\n")); - scprintf(_SC("*****FUNCTION [%s]\n"),type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown")); + scprintf(_SC("*****FUNCTION [%s]\n"),sq_type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown")); scprintf(_SC("-----LITERALS\n")); SQObjectPtr refidx,key,val; SQInteger idx; @@ -131,7 +131,7 @@ void SQFuncState::Dump(SQFunctionProto *func) templiterals[_integer(val)]=key; } for(i=0;i>\n")); n=0; for(i=0;i<_parameters.size();i++){ - scprintf(_SC("[%d] "),n); + scprintf(_SC("[%d] "), (SQInt32)n); DumpLiteral(_parameters[i]); scprintf(_SC("\n")); n++; @@ -149,13 +149,13 @@ void SQFuncState::Dump(SQFunctionProto *func) scprintf(_SC("-----LOCALS\n")); for(si=0;si_nlocalvarinfos;si++){ SQLocalVarInfo lvi=func->_localvarinfos[si]; - scprintf(_SC("[%d] %s \t%d %d\n"),lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op); + scprintf(_SC("[%d] %s \t%d %d\n"), (SQInt32)lvi._pos,_stringval(lvi._name), (SQInt32)lvi._start_op, (SQInt32)lvi._end_op); n++; } scprintf(_SC("-----LINE INFO\n")); for(i=0;i<_lineinfos.size();i++){ SQLineInfo li=_lineinfos[i]; - scprintf(_SC("op [%d] line [%d] \n"),li._op,li._line); + scprintf(_SC("op [%d] line [%d] \n"), (SQInt32)li._op, (SQInt32)li._line); n++; } scprintf(_SC("-----dump\n")); @@ -165,7 +165,7 @@ void SQFuncState::Dump(SQFunctionProto *func) if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){ SQInteger lidx = inst._arg1; - scprintf(_SC("[%03d] %15s %d "),n,g_InstrDesc[inst.op].name,inst._arg0); + scprintf(_SC("[%03d] %15s %d "), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0); if(lidx >= 0xFFFFFFFF) scprintf(_SC("null")); else { @@ -196,18 +196,18 @@ void SQFuncState::Dump(SQFunctionProto *func) } } else if(inst.op==_OP_LOADFLOAT) { - scprintf(_SC("[%03d] %15s %d %f %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); + scprintf(_SC("[%03d] %15s %d %f %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); } /* else if(inst.op==_OP_ARITH){ scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); }*/ else { - scprintf(_SC("[%03d] %15s %d %d %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); + scprintf(_SC("[%03d] %15s %d %d %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); } n++; } scprintf(_SC("-----\n")); - scprintf(_SC("stack size[%d]\n"),func->_stacksize); + scprintf(_SC("stack size[%d]\n"), (SQInt32)func->_stacksize); scprintf(_SC("--------------------------------------------------------------------\n\n")); } #endif @@ -238,7 +238,7 @@ SQInteger SQFuncState::GetConstant(const SQObject &cons) return _integer(val); } -void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) +void SQFuncState::SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) { _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); @@ -246,7 +246,7 @@ void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); } -void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val) +void SQFuncState::SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val) { switch(arg){ case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; @@ -290,7 +290,7 @@ SQInteger SQFuncState::PopTarget() SQUnsignedInteger npos=_targetstack.back(); assert(npos < _vlocals.size()); SQLocalVarInfo &t = _vlocals[npos]; - if(type(t._name)==OT_NULL){ + if(sq_type(t._name)==OT_NULL){ _vlocals.pop_back(); } _targetstack.pop_back(); @@ -322,7 +322,7 @@ void SQFuncState::SetStackSize(SQInteger n) while(size>n){ size--; SQLocalVarInfo lvi = _vlocals.back(); - if(type(lvi._name)!=OT_NULL){ + if(sq_type(lvi._name)!=OT_NULL){ if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer _outers--; } @@ -346,7 +346,7 @@ bool SQFuncState::IsConstant(const SQObject &name,SQObject &e) bool SQFuncState::IsLocal(SQUnsignedInteger stkpos) { if(stkpos>=_vlocals.size())return false; - else if(type(_vlocals[stkpos]._name)!=OT_NULL)return true; + else if(sq_type(_vlocals[stkpos]._name)!=OT_NULL)return true; return false; } @@ -369,7 +369,7 @@ SQInteger SQFuncState::GetLocalVariable(const SQObject &name) SQInteger locals=_vlocals.size(); while(locals>=1){ SQLocalVarInfo &lvi = _vlocals[locals-1]; - if(type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ + if(sq_type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ return locals-1; } locals--; @@ -481,7 +481,6 @@ void SQFuncState::AddInstruction(SQInstruction &i) break; case _OP_GET: if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){ - pi._arg1 = pi._arg1; pi._arg2 = (unsigned char)i._arg1; pi.op = _OP_GETK; pi._arg0 = i._arg0; @@ -493,7 +492,6 @@ void SQFuncState::AddInstruction(SQInstruction &i) if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_PREPCALLK; pi._arg0 = i._arg0; - pi._arg1 = pi._arg1; pi._arg2 = i._arg2; pi._arg3 = i._arg3; return; @@ -511,7 +509,6 @@ void SQFuncState::AddInstruction(SQInstruction &i) if(aat != -1 && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_APPENDARRAY; pi._arg0 = i._arg0; - pi._arg1 = pi._arg1; pi._arg2 = (unsigned char)aat; pi._arg3 = MAX_FUNC_STACKSIZE; return; @@ -553,7 +550,6 @@ void SQFuncState::AddInstruction(SQInstruction &i) { pi.op = i.op; pi._arg0 = i._arg0; - pi._arg1 = pi._arg1; pi._arg2 = i._arg2; pi._arg3 = MAX_FUNC_STACKSIZE; return; diff --git a/squirrel/squirrel/sqfuncstate.h b/squirrel/squirrel/sqfuncstate.h index 9621705124c..8bd19938ca6 100644 --- a/squirrel/squirrel/sqfuncstate.h +++ b/squirrel/squirrel/sqfuncstate.h @@ -16,8 +16,8 @@ struct SQFuncState void PopChildState(); void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} void AddInstruction(SQInstruction &i); - void SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); - void SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val); + void SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); + void SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val); SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];} void PopInstructions(SQInteger size){for(SQInteger i=0;i_stack[v->_stackbase]; - _stack._vals[0] = ISREFCOUNTED(type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(type(_this))) : _this; + _stack._vals[0] = ISREFCOUNTED(sq_type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(sq_type(_this))) : _this; for(SQInteger n =1; n_stack[v->_stackbase+n]; } @@ -191,7 +191,7 @@ bool SQGenerator::Resume(SQVM *v,SQObjectPtr &dest) et._stacksize += newbase; } SQObject _this = _stack._vals[0]; - v->_stack[v->_stackbase] = type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; + v->_stack[v->_stackbase] = sq_type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; for(SQInteger n = 1; n_stack[v->_stackbase+n] = _stack._vals[n]; @@ -285,7 +285,7 @@ bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer de return true; } -bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) +bool SafeRead(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) { if(size && read(up,dest,size) != size) { v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated")); @@ -299,7 +299,7 @@ bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUnsignedInteger return SafeWrite(v,write,up,&tag,sizeof(tag)); } -bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) +bool CheckTag(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) { SQUnsignedInteger32 t; _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t))); @@ -312,9 +312,9 @@ bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUnsignedInteger3 bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o) { - SQUnsignedInteger32 _type = (SQUnsignedInteger32)type(o); + SQUnsignedInteger32 _type = (SQUnsignedInteger32)sq_type(o); _CHECK_IO(SafeWrite(v,write,up,&_type,sizeof(_type))); - switch(type(o)){ + switch(sq_type(o)){ case OT_STRING: _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); _CHECK_IO(SafeWrite(v,write,up,_stringval(o),sq_rsl(_string(o)->_len))); @@ -347,7 +347,7 @@ bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o) } break; case OT_INTEGER:{ - SQInteger i = 0; + SQInteger i; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; } case OT_BOOL:{ diff --git a/squirrel/squirrel/sqobject.h b/squirrel/squirrel/sqobject.h index 4faf5e5456e..70ef4814393 100644 --- a/squirrel/squirrel/sqobject.h +++ b/squirrel/squirrel/sqobject.h @@ -101,7 +101,7 @@ struct SQWeakRef : SQRefCounted SQObject _obj; }; -#define _realval(o) (type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) +#define _realval(o) (sq_type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) struct SQObjectPtr; @@ -128,8 +128,7 @@ struct SQObjectPtr; (obj)->_uiRef++; \ } -#define type(obj) ((obj)._type) -#define is_delegable(t) (type(t)&SQOBJECT_DELEGABLE) +#define is_delegable(t) (sq_type(t)&SQOBJECT_DELEGABLE) #define raw_type(obj) _RAW_TYPE((obj)._type) #define _integer(obj) ((obj)._unVal.nInteger) @@ -155,8 +154,8 @@ struct SQObjectPtr; #define _stringval(obj) (obj)._unVal.pString->_val #define _userdataval(obj) ((SQUserPointer)sq_aligning((obj)._unVal.pUserData + 1)) -#define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) -#define tointeger(num) ((type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) +#define tofloat(num) ((sq_type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) +#define tointeger(num) ((sq_type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// #if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) diff --git a/squirrel/squirrel/sqpcheader.h b/squirrel/squirrel/sqpcheader.h index 935329d5a82..afa4bcaa909 100644 --- a/squirrel/squirrel/sqpcheader.h +++ b/squirrel/squirrel/sqpcheader.h @@ -6,11 +6,11 @@ #include #endif +#include #include #include #include #include -#include #include //squirrel stuff #include "../squirrel.h" diff --git a/squirrel/squirrel/sqstate.cc b/squirrel/squirrel/sqstate.cc index cf8bb9905ee..fcdd710b29b 100644 --- a/squirrel/squirrel/sqstate.cc +++ b/squirrel/squirrel/sqstate.cc @@ -12,12 +12,6 @@ #include "squserdata.h" #include "sqclass.h" -//SQObjectPtr _null_; -//SQObjectPtr _true_(true); -//SQObjectPtr _false_(false); -//SQObjectPtr _one_((SQInteger)1); -//SQObjectPtr _minusone_((SQInteger)-1); - SQSharedState::SQSharedState() { _compilererrorhandler = NULL; @@ -221,7 +215,7 @@ SQSharedState::~SQSharedState() SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) { - if(type(name) != OT_STRING) + if(sq_type(name) != OT_STRING) return -1; SQObjectPtr ret; if(_table(_metamethodsmap)->Get(name,ret)) { @@ -234,7 +228,7 @@ SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) { - switch(type(o)){ + switch(sq_type(o)){ case OT_TABLE:_table(o)->Mark(chain);break; case OT_ARRAY:_array(o)->Mark(chain);break; case OT_USERDATA:_userdata(o)->Mark(chain);break; @@ -282,7 +276,6 @@ SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) SQCollectable *resurrected = _gc_chain; SQCollectable *t = resurrected; - //SQCollectable *nx = NULL; _gc_chain = tchain; @@ -423,7 +416,7 @@ void RefTable::Mark(SQCollectable **chain) { RefNode *nodes = (RefNode *)_nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { - if(type(nodes->obj) != OT_NULL) { + if(sq_type(nodes->obj) != OT_NULL) { SQSharedState::MarkObject(nodes->obj,chain); } nodes++; @@ -485,7 +478,7 @@ void RefTable::Resize(SQUnsignedInteger size) //rehash SQUnsignedInteger nfound = 0; for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { - if(type(t->obj) != OT_NULL) { + if(sq_type(t->obj) != OT_NULL) { //add back; assert(t->refs != 0); RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); @@ -518,7 +511,7 @@ RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bo mainpos = ::HashObj(obj)&(_numofslots-1); *prev = NULL; for (ref = _buckets[mainpos]; ref; ) { - if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj)) + if(_rawval(ref->obj) == _rawval(obj) && sq_type(ref->obj) == sq_type(obj)) break; *prev = ref; ref = ref->next; diff --git a/squirrel/squirrel/sqtable.cc b/squirrel/squirrel/sqtable.cc index b11e8e6b60b..15f1780db11 100644 --- a/squirrel/squirrel/sqtable.cc +++ b/squirrel/squirrel/sqtable.cc @@ -62,7 +62,7 @@ void SQTable::Rehash(bool force) _usednodes = 0; for (SQInteger i=0; ikey) != OT_NULL) + if (sq_type(old->key) != OT_NULL) NewSlot(old->key,old->val); } for(SQInteger k=0;kkey) != OT_NULL) { + if(sq_type(mp->key) != OT_NULL) { n = _firstfree; /* get a free place */ SQHash mph = HashObj(mp->key) & (_numofnodes - 1); _HashNode *othern; /* main position of colliding node */ @@ -161,7 +161,7 @@ bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val) mp->key = key; for (;;) { /* correct `firstfree' */ - if (type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) { + if (sq_type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) { mp->val = val; _usednodes++; return true; /* OK; table still has a free place */ @@ -177,7 +177,7 @@ SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr { SQInteger idx = (SQInteger)TranslateIndex(refpos); while (idx < _numofnodes) { - if(type(_nodes[idx].key) != OT_NULL) { + if(sq_type(_nodes[idx].key) != OT_NULL) { //first found _HashNode &n = _nodes[idx]; outkey = n.key; diff --git a/squirrel/squirrel/sqtable.h b/squirrel/squirrel/sqtable.h index fca0a383f76..ff526b73d5b 100644 --- a/squirrel/squirrel/sqtable.h +++ b/squirrel/squirrel/sqtable.h @@ -12,9 +12,9 @@ #define hashptr(p) ((SQHash)(((SQInteger)p) >> 3)) -inline SQHash HashObj(const SQObjectPtr &key) +inline SQHash HashObj(const SQObject &key) { - switch(type(key)) { + switch(sq_type(key)) { case OT_STRING: return _string(key)->_hash; case OT_FLOAT: return (SQHash)((SQInteger)_float(key)); case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key)); @@ -67,7 +67,7 @@ struct SQTable : public SQDelegable { _HashNode *n = &_nodes[hash]; do{ - if(_rawval(n->key) == _rawval(key) && type(n->key) == type(key)){ + if(_rawval(n->key) == _rawval(key) && sq_type(n->key) == sq_type(key)){ return n; } }while((n = n->next)); @@ -80,7 +80,7 @@ struct SQTable : public SQDelegable _HashNode *n = &_nodes[hash & (_numofnodes - 1)]; _HashNode *res = NULL; do{ - if(type(n->key) == OT_STRING && (scstrcmp(_stringval(n->key),key) == 0)){ + if(sq_type(n->key) == OT_STRING && (scstrcmp(_stringval(n->key),key) == 0)){ res = n; break; } diff --git a/squirrel/squirrel/sqvm.cc b/squirrel/squirrel/sqvm.cc index 680405be0f7..82085aad265 100644 --- a/squirrel/squirrel/sqvm.cc +++ b/squirrel/squirrel/sqvm.cc @@ -2,8 +2,8 @@ see copyright notice in squirrel.h */ #include "sqpcheader.h" +#include #include -#include #include "sqopcodes.h" #include "sqvm.h" #include "sqfuncproto.h" @@ -15,11 +15,13 @@ #include "sqclass.h" #define TOP() (_stack._vals[_top-1]) +#define TARGET _stack._vals[_stackbase+arg0] +#define STK(a) _stack._vals[_stackbase+(a)] bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { SQInteger res; - if((type(o1)|type(o2)) == OT_INTEGER) + if((sq_type(o1)| sq_type(o2)) == OT_INTEGER) { SQInteger i1 = _integer(o1), i2 = _integer(o2); switch(op) { @@ -39,7 +41,7 @@ bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,con #define _ARITH_(op,trg,o1,o2) \ { \ - SQInteger tmask = type(o1)|type(o2); \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \ case (OT_FLOAT|OT_INTEGER): \ @@ -50,7 +52,7 @@ bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,con #define _ARITH_NOZERO(op,trg,o1,o2,err) \ { \ - SQInteger tmask = type(o1)|type(o2); \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: { SQInteger i2 = _integer(o2); if(i2 == 0) { Raise_Error(err); SQ_THROW(); } trg = _integer(o1) op i2; } break;\ case (OT_FLOAT|OT_INTEGER): \ @@ -61,7 +63,7 @@ bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,con bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { - SQInteger tmask = type(o1)|type(o2); + SQInteger tmask = sq_type(o1)| sq_type(o2); switch(tmask) { case OT_INTEGER:{ SQInteger res, i1 = _integer(o1), i2 = _integer(o2); @@ -125,6 +127,7 @@ SQVM::SQVM(SQSharedState *ss) _releasehook = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _ops_remaining = 0; + _ops_total = 0; _ops_grace_amount = 500; _throw_if_no_ops = true; } @@ -178,7 +181,7 @@ bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) { - switch(type(o)) { + switch(sq_type(o)) { case OT_INTEGER: trg = -_integer(o); return true; @@ -207,7 +210,7 @@ bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) #define _RET_SUCCEED(exp) { result = (exp); return true; } bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) { - SQObjectType t1 = type(o1), t2 = type(o2); + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); if(t1 == t2) { if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); SQObjectPtr res; @@ -226,7 +229,7 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { Push(o1);Push(o2); if(CallMetaMethod(closure,MT_CMP,2,res)) { - if(type(res) != OT_INTEGER) { + if(sq_type(res) != OT_INTEGER) { Raise_Error(_SC("_cmp must return an integer")); return false; } @@ -284,7 +287,7 @@ bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObject bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) { - switch(type(o)) { + switch(sq_type(o)) { case OT_STRING: res = o; return true; @@ -297,6 +300,9 @@ bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) case OT_BOOL: scsprintf(_sp(sq_rsl(6)),sq_rsl(6),_integer(o)?_SC("true"):_SC("false")); break; + case OT_NULL: + scsprintf(_sp(sq_rsl(5)),sq_rsl(5),_SC("null")); + break; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: @@ -304,8 +310,8 @@ bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) SQObjectPtr closure; if(_delegable(o)->GetMetaMethod(this, MT_TOSTRING, closure)) { Push(o); - if(CallMetaMethod(closure,MT_TOSTRING,1,res)) {; - if(type(res) == OT_STRING) + if(CallMetaMethod(closure,MT_TOSTRING,1,res)) { + if(sq_type(res) == OT_STRING) return true; } else { @@ -382,8 +388,8 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege { paramssize--; if (nargs < paramssize) { - const SQChar *src = type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; - const SQChar *name = type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } @@ -410,8 +416,8 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege } } else { - const SQChar *src = type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; - const SQChar *name = type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); return false; } @@ -522,7 +528,7 @@ bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger SQ_UNUSED_ARG(arg_2),int exitpos,int &jump) { SQInteger nrefidx; - switch(type(o1)) { + switch(sq_type(o1)) { case OT_TABLE: if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos); o4 = (SQInteger)nrefidx; _FINISH(1); @@ -545,7 +551,7 @@ bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr Push(o4); if(CallMetaMethod(closure, MT_NEXTI, 2, itr)) { o4 = o2 = itr; - if(type(itr) == OT_NULL) _FINISH(exitpos); + if(sq_type(itr) == OT_NULL) _FINISH(exitpos); if(!Get(o1, itr, o3, 0, DONT_FALL_BACK)) { Raise_Error(_SC("_nexti returned an invalid idx")); // cloud be changed return false; @@ -564,7 +570,7 @@ bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos); if(_generator(o1)->_state == SQGenerator::eSuspended) { SQInteger idx = 0; - if(type(o4) == OT_INTEGER) { + if(sq_type(o4) == OT_INTEGER) { idx = _integer(o4) + 1; } o2 = idx; @@ -619,14 +625,14 @@ bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes SQClass *base = NULL; SQObjectPtr attrs; if(baseclass != -1) { - if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } + if(sq_type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } base = _class(_stack._vals[_stackbase + baseclass]); } if(attributes != MAX_FUNC_STACKSIZE) { attrs = _stack._vals[_stackbase+attributes]; } target = SQClass::Create(_ss(this),base); - if(type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) { + if(sq_type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) { int nparams = 2; SQObjectPtr ret; Push(target); Push(attrs); @@ -642,9 +648,15 @@ bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes bool SQVM::IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res) { - if(type(o1) == type(o2)) { + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + if(t1 == t2) { + if (t1 == OT_FLOAT) { + res = (_float(o1) == _float(o2)); + } + else { res = (_rawval(o1) == _rawval(o2)); } + } else { if(sq_isnumeric(o1) && sq_isnumeric(o2)) { res = (tofloat(o1) == tofloat(o2)); @@ -658,19 +670,18 @@ bool SQVM::IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res) bool SQVM::IsFalse(SQObjectPtr &o) { - if(((type(o) & SQOBJECT_CANBEFALSE) - && ( ((type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0))) )) + if(((sq_type(o) & SQOBJECT_CANBEFALSE) + && ( ((sq_type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0))) )) #if !defined(SQUSEDOUBLE) || (defined(SQUSEDOUBLE) && defined(_SQ64)) || (_integer(o) == 0) ) //OT_NULL|OT_INTEGER|OT_BOOL #else - || (((type(o) != OT_FLOAT) && (_integer(o) == 0))) ) //OT_NULL|OT_INTEGER|OT_BOOL + || (((sq_type(o) != OT_FLOAT) && (_integer(o) == 0))) ) //OT_NULL|OT_INTEGER|OT_BOOL #endif { return true; } return false; } - extern SQInstructionDesc g_InstrDesc[]; bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et, SQBool can_suspend) @@ -711,6 +722,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ { for(;;) { + _ops_total++; _ops_remaining --; if (_ops_remaining < 0) { // suspend vm @@ -732,7 +744,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ const SQInstruction &_i_ = *ci->_ip++; //dumpstack(_stackbase); - //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); + //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); switch(_i_.op) { case _OP_LINE: if (_debughook) CallDebugHook(_SC('l'),arg1); continue; @@ -751,24 +763,29 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; case _OP_TAILCALL:{ SQObjectPtr &t = STK(arg1); - if (type(t) == OT_CLOSURE + if (sq_type(t) == OT_CLOSURE && (!_closure(t)->_function->_bgenerator)){ SQObjectPtr clo = t; + SQInteger last_top = _top; if(_openouters) CloseOuters(&(_stack._vals[_stackbase])); for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i); _GUARD(StartCall(_closure(clo), ci->_target, arg3, _stackbase, true)); + if (last_top >= _top) { + _top = last_top; + } continue; } } case _OP_CALL: { SQObjectPtr clo = STK(arg1); - switch (type(clo)) { + switch (sq_type(clo)) { case OT_CLOSURE: _GUARD(StartCall(_closure(clo), sarg0, arg3, _stackbase+arg2, false)); continue; case OT_NATIVECLOSURE: { bool suspend; - _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo,suspend)); + bool tailcall; + _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, (SQInt32)sarg0, suspend, tailcall)); if(suspend){ _suspended = SQTrue; _suspended_target = sarg0; @@ -777,7 +794,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ outres = clo; return true; } - if(sarg0 != -1) { + if(sarg0 != -1 && !tailcall) { STK(arg0) = clo; } } @@ -789,17 +806,17 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ STK(arg0) = inst; } SQInteger stkbase; - switch(type(clo)) { + switch(sq_type(clo)) { case OT_CLOSURE: stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; _GUARD(StartCall(_closure(clo), -1, arg3, stkbase, false)); break; case OT_NATIVECLOSURE: - bool suspend; + bool dummy; stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; - _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo,suspend)); + _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo, -1, dummy, dummy)); break; default: break; //shutup GCC 4.x } @@ -887,7 +904,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n).Null(); }continue; case _OP_LOADROOT: { SQWeakRef *w = _closure(ci->_closure)->_root; - if(type(w->_obj) != OT_NULL) { + if(sq_type(w->_obj) != OT_NULL) { TARGET = w->_obj; } else { TARGET = _roottable; //shoud this be like this? or null @@ -963,7 +980,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false, arg1));} continue; case _OP_INCL: { SQObjectPtr &a = STK(arg1); - if(type(a) == OT_INTEGER) { + if(sq_type(a) == OT_INTEGER) { a._unVal.nInteger = _integer(a) + sarg3; } else { @@ -974,7 +991,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true, arg1));} continue; case _OP_PINCL: { SQObjectPtr &a = STK(arg1); - if(type(a) == OT_INTEGER) { + if(sq_type(a) == OT_INTEGER) { TARGET = a; a._unVal.nInteger = _integer(a) + sarg3; } @@ -986,9 +1003,9 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue; case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW, DONT_FALL_BACK) ? true : false; continue; case _OP_INSTANCEOF: - if(type(STK(arg1)) != OT_CLASS) + if(sq_type(STK(arg1)) != OT_CLASS) {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();} - TARGET = (type(STK(arg2)) == OT_INSTANCE) ? (_instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?true:false) : false; + TARGET = (sq_type(STK(arg2)) == OT_INSTANCE) ? (_instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?true:false) : false; continue; case _OP_AND: if(IsFalse(STK(arg2))) { @@ -1005,7 +1022,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue; case _OP_NOT: TARGET = IsFalse(STK(arg1)); continue; case _OP_BWNOT: - if(type(STK(arg1)) == OT_INTEGER) { + if(sq_type(STK(arg1)) == OT_INTEGER) { SQInteger t = _integer(STK(arg1)); TARGET = SQInteger(~t); continue; @@ -1021,6 +1038,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ case _OP_YIELD:{ if(ci->_generator) { if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1); + if (_openouters) CloseOuters(&_stack._vals[_stackbase]); _GUARD(ci->_generator->Yield(this,arg2)); traps -= ci->_etraps; if(sarg1 != MAX_FUNC_STACKSIZE) _Swap(STK(arg1),temp_reg);//STK(arg1) = temp_reg; @@ -1035,7 +1053,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ } continue; case _OP_RESUME: - if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();} + if(sq_type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();} _GUARD(_generator(STK(arg1))->Resume(this, TARGET)); traps += ci->_etraps; continue; @@ -1044,7 +1062,7 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQ ci->_ip += tojump; } continue; case _OP_POSTFOREACH: - assert(type(STK(arg0)) == OT_GENERATOR); + assert(sq_type(STK(arg0)) == OT_GENERATOR); if(_generator(STK(arg0))->_state == SQGenerator::eDead) ci->_ip += (sarg1 - 1); continue; @@ -1134,7 +1152,7 @@ bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr void SQVM::CallErrorHandler(SQObjectPtr &error) { - if(type(_errorhandler) != OT_NULL) { + if(sq_type(_errorhandler) != OT_NULL) { SQObjectPtr out; Push(_roottable); Push(error); Call(_errorhandler, 2, _top-2, out,SQFalse); @@ -1148,8 +1166,8 @@ void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) _debughook = false; SQFunctionProto *func=_closure(ci->_closure)->_function; if(_debughook_native) { - const SQChar *src = type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; - const SQChar *fname = type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *fname = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; SQInteger line = forcedline?forcedline:func->GetLine(ci->_ip); _debughook_native(this,type,src,line,fname); } @@ -1163,7 +1181,7 @@ void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) _debughook = true; } -bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, bool &suspend) +bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) { SQInteger nparamscheck = nclosure->_nparamscheck; SQInteger newtop = newbase + nargs + nclosure->_noutervalues; @@ -1176,7 +1194,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || ((nparamscheck < 0) && (nargs < (-nparamscheck))))) { - const SQChar *src = type(nclosure->_name) == OT_STRING?_stringval(nclosure->_name):NULL; + const SQChar *src = sq_type(nclosure->_name) == OT_STRING?_stringval(nclosure->_name):NULL; Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s"), nargs, nparamscheck, src); return false; } @@ -1185,8 +1203,8 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb SQIntVec &tc = nclosure->_typecheck; if((tcs = tc.size())) { for(SQInteger i = 0; i < nargs && i < tcs; i++) { - if((tc._vals[i] != -1) && !(type(_stack._vals[newbase+i]) & tc._vals[i])) { - Raise_ParamTypeError(i,tc._vals[i],type(_stack._vals[newbase+i])); + if((tc._vals[i] != -1) && !(sq_type(_stack._vals[newbase+i]) & tc._vals[i])) { + Raise_ParamTypeError(i,tc._vals[i], sq_type(_stack._vals[newbase+i])); return false; } } @@ -1194,6 +1212,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb if(!EnterFrame(newbase, newtop, false)) return false; ci->_closure = nclosure; + ci->_target = target; SQInteger outers = nclosure->_noutervalues; for (SQInteger i = 0; i < outers; i++) { @@ -1208,7 +1227,12 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb _nnativecalls--; suspend = false; - if (ret == SQ_SUSPEND_FLAG) { + tailcall = false; + if (ret == SQ_TAILCALL_FLAG) { + tailcall = true; + return true; + } + else if (ret == SQ_SUSPEND_FLAG) { suspend = true; } else if (ret < 0) { @@ -1227,13 +1251,30 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb return true; } +bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) +{ + SQInteger last_top = _top; + SQObjectPtr clo = closure; + if (ci->_root) + { + Raise_Error("root calls cannot invoke tailcalls"); + return false; + } + for (SQInteger i = 0; i < nparams; i++) STK(i) = STK(parambase + i); + bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); + if (last_top >= _top) { + _top = last_top; + } + return ret; +} + #define FALLBACK_OK 0 #define FALLBACK_NO_MATCH 1 #define FALLBACK_ERROR 2 bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx) { - switch(type(self)){ + switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Get(key,dest))return true; break; @@ -1274,7 +1315,7 @@ bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &des //#ifdef ROOT_FALLBACK if(selfidx == 0) { SQWeakRef *w = _closure(ci->_closure)->_root; - if(type(w->_obj) != OT_NULL) + if(sq_type(w->_obj) != OT_NULL) { if(Get(*((const SQObjectPtr *)&w->_obj),key,dest,0,DONT_FALL_BACK)) return true; } @@ -1288,7 +1329,7 @@ bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &des bool SQVM::InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { SQTable *ddel = NULL; - switch(type(self)) { + switch(sq_type(self)) { case OT_CLASS: ddel = _class_ddel; break; case OT_TABLE: ddel = _table_ddel; break; case OT_ARRAY: ddel = _array_ddel; break; @@ -1307,7 +1348,7 @@ bool SQVM::InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key, SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { - switch(type(self)){ + switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: //delegation @@ -1330,7 +1371,7 @@ SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObj } else { Pop(2); - if(type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) return FALLBACK_ERROR; } } @@ -1345,7 +1386,7 @@ SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObj bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,SQInteger selfidx) { - switch(type(self)){ + switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Set(key,val)) return true; break; @@ -1359,6 +1400,7 @@ bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr return false; } return true; + case OT_USERDATA: break; // must fall back default: Raise_Error(_SC("trying to set '%s'"),GetTypeName(self)); return false; @@ -1379,7 +1421,7 @@ bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) { - switch(type(self)) { + switch(sq_type(self)) { case OT_TABLE: if(_table(self)->_delegate) { if(Set(_table(self)->_delegate,key,val,DONT_FALL_BACK)) return FALLBACK_OK; @@ -1398,9 +1440,8 @@ SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const return FALLBACK_OK; } else { - if(type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) - //error Pop(3); + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) return FALLBACK_ERROR; } } @@ -1417,7 +1458,7 @@ bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) { SQObjectPtr temp_reg; SQObjectPtr newobj; - switch(type(self)){ + switch(sq_type(self)){ case OT_TABLE: newobj = _table(self)->Clone(); goto cloned_mt; @@ -1445,14 +1486,14 @@ bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw) { - if(type(self) != OT_CLASS) { + if(sq_type(self) != OT_CLASS) { Raise_Error(_SC("object must be a class")); return false; } SQClass *c = _class(self); if(!raw) { SQObjectPtr &mm = c->_metamethods[MT_NEWMEMBER]; - if(type(mm) != OT_NULL ) { + if(sq_type(mm) != OT_NULL ) { Push(self); Push(key); Push(val); Push(attrs); Push(bstatic); @@ -1461,7 +1502,7 @@ bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjec } if(!NewSlot(self, key, val,bstatic)) return false; - if(type(attrs) != OT_NULL) { + if(sq_type(attrs) != OT_NULL) { c->SetAttributes(key,attrs); } return true; @@ -1469,8 +1510,8 @@ bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjec bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { - if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; } - switch(type(self)) { + if(sq_type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; } + switch(sq_type(self)) { case OT_TABLE: { bool rawcall = true; if(_table(self)->_delegate) { @@ -1530,7 +1571,7 @@ bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObject bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res) { - switch(type(self)) { + switch(sq_type(self)) { case OT_TABLE: case OT_INSTANCE: case OT_USERDATA: { @@ -1542,7 +1583,7 @@ bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr return CallMetaMethod(closure,MT_DELSLOT,2,res); } else { - if(type(self) == OT_TABLE) { + if(sq_type(self) == OT_TABLE) { if(_table(self)->Get(key,t)) { _table(self)->Remove(key); } @@ -1571,13 +1612,13 @@ bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObj #ifdef _DEBUG SQInteger prevstackbase = _stackbase; #endif - switch(type(closure)) { + switch(sq_type(closure)) { case OT_CLOSURE: return Execute(closure, nparams, stackbase, outres, raiseerror, ET_CALL, can_suspend); break; case OT_NATIVECLOSURE:{ - bool suspend; - return CallNative(_nativeclosure(closure), nparams, stackbase, outres,suspend); + bool dummy; + return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); } break; @@ -1585,7 +1626,7 @@ SQInteger prevstackbase = _stackbase; SQObjectPtr constr; SQObjectPtr temp; CreateClassInstance(_class(closure),outres,constr); - SQObjectType ctype = type(constr); + SQObjectType ctype = sq_type(constr); if (ctype == OT_NATIVECLOSURE || ctype == OT_CLOSURE) { _stack[stackbase] = outres; return Call(constr,nparams,stackbase,temp,raiseerror); @@ -1594,6 +1635,7 @@ SQInteger prevstackbase = _stackbase; } break; default: + Raise_Error(_SC("attempt to call '%s'"), GetTypeName(closure)); return false; } #ifdef _DEBUG @@ -1747,10 +1789,10 @@ void SQVM::dumpstack(SQInteger stackbase,bool dumpall) for(SQInteger i=0;i"));else scprintf(_SC(" ")); - scprintf(_SC("[%d]:"),n); - switch(type(obj)){ + scprintf(_SC("[" _PRINT_INT_FMT "]:"),n); + switch(sq_type(obj)){ case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break; - case OT_INTEGER: scprintf(_SC("INTEGER %d"),_integer(obj));break; + case OT_INTEGER: scprintf(_SC("INTEGER " _PRINT_INT_FMT),_integer(obj));break; case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break; case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break; case OT_NULL: scprintf(_SC("NULL")); break; @@ -1764,7 +1806,7 @@ void SQVM::dumpstack(SQInteger stackbase,bool dumpall) case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break; case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break; case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break; - case OT_WEAKREF: scprintf(_SC("WEAKERF %p"),_weakref(obj));break; + case OT_WEAKREF: scprintf(_SC("WEAKREF %p"),_weakref(obj));break; default: assert(0); break; diff --git a/squirrel/squirrel/sqvm.h b/squirrel/squirrel/sqvm.h index 714ba599559..b6ab4fcfb19 100644 --- a/squirrel/squirrel/sqvm.h +++ b/squirrel/squirrel/sqvm.h @@ -2,14 +2,15 @@ #ifndef _SQVM_H_ #define _SQVM_H_ -#include #include "sqopcodes.h" #include "sqobject.h" #define MAX_NATIVE_CALLS 100 #define MIN_STACK_OVERHEAD 15 #define SQ_SUSPEND_FLAG -666 +#define SQ_TAILCALL_FLAG -777 #define DONT_FALL_BACK 666 +//#define EXISTS_FALL_BACK -1 #define GET_FLAG_RAW 0x00000001 #define GET_FLAG_DO_NOT_RAISE_ERROR 0x00000002 @@ -19,7 +20,6 @@ void sq_base_register(HSQUIRRELVM v); struct SQExceptionTrap{ SQExceptionTrap() {} SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;} - SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; } SQInteger _stackbase; SQInteger _stacksize; SQInstruction *_ip; @@ -28,9 +28,6 @@ struct SQExceptionTrap{ #define _INLINE -#define STK(a) _stack._vals[_stackbase+(a)] -#define TARGET _stack._vals[_stackbase+arg0] - typedef sqvector ExceptionsTraps; struct SQVM : public CHAINABLE_OBJ @@ -59,7 +56,8 @@ typedef sqvector CallInfoVec; bool Init(SQVM *friendvm, SQInteger stacksize); bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL, SQBool can_suspend = false); //starts a native call return when the NATIVE closure returns - bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval,bool &suspend); + bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); + bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); //starts a SQUIRREL call in the same "Execution loop" bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); @@ -183,6 +181,7 @@ typedef sqvector CallInfoVec; SQInteger _ops_remaining; /// number of ops the vm can do till break SQInteger _ops_grace_amount; /// raise error if _ops_remaining is less than -_ops_grace_amount for pure native calls + SQInteger _ops_total; /// total number of ops performed bool _throw_if_no_ops; /// is no-ops an error or can call suspended? default: true }; diff --git a/squirrel/update_squirrel.awk b/squirrel/update_squirrel.awk new file mode 100644 index 00000000000..61fe00779ad --- /dev/null +++ b/squirrel/update_squirrel.awk @@ -0,0 +1,30 @@ +# gawk file to minimize patch size + +# strip trailing whitespace +{ + sub(/[[:space:]]+$/, "") +} + +# replace include by include "../sq*.h" +/include.*\/, "\"../\\1\"", "g"); + print a + next +} + +# replace four spaces by tabs +/ / { + a = gensub(/ /, "\t", "g"); + print a + next +} + +# replace stupid endif comments +/endif.*_H_/ { + print "#endif" + next +} + +{ + print $0 +} diff --git a/squirrel/update_squirrel.sh b/squirrel/update_squirrel.sh new file mode 100755 index 00000000000..a23dc59be0a --- /dev/null +++ b/squirrel/update_squirrel.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Script to update squirrel source code from SQUIRREL3 +# +# Call with ``update_squirrel PATH_TO_SQUIRREL'', where +# PATH_TO_SQUIRREL is path of local clone of https://github.com/albertodemichelis/squirrel +# +# Call it within a git repo of simutrans. +# +# Still needs manual review, to make sure none of our modifications are reverted. +SQPATH=$1 +# copy and rename files +echo $SQPATH/COPYRIGHT ../simutrans/license_squirrel.txt +cp $SQPATH/COPYRIGHT ../simutrans/license_squirrel.txt +cp $SQPATH/include/* . +cp $SQPATH/squirrel/*.h squirrel/ + +cp $SQPATH/squirrel/*.h squirrel/ +cp $SQPATH/squirrel/*.cpp squirrel/ + + +for i in *.h; do gawk -f update_squirrel.awk "$i" > temp; mv temp "$i"; done + +cd squirrel +# rename +for i in *.cpp; do mv "$i" "${i/.cpp}".cc; done +# replace include by include "../sq*.h" +for i in *.cc; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done +for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done +cd .. + +cp $SQPATH/sqstdlib/*.h sqstdlib/ +cp $SQPATH/sqstdlib/*.cpp sqstdlib/ + +cd sqstdlib +# rename +for i in *.cpp; do mv "$i" "${i/.cpp}".cc; done +# replace include by include "../sq*.h" +for i in *.cc; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done +for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done +cd .. + +cd .. +git diff -w --ignore-blank-lines --ignore-space-at-eol -b > update_squirrel.diff + +git checkout -- squirrel/squirrel +git checkout -- squirrel/sqstdlib +git checkout -- squirrel/sq*.h +git checkout -- simutrans/license_squirrel.txt + +patch --ignore-whitespace -p3 < update_squirrel.diff diff --git a/sys/clipboard_internal.cc b/sys/clipboard_internal.cc index df5802a33ce..62fd0dd9c02 100644 --- a/sys/clipboard_internal.cc +++ b/sys/clipboard_internal.cc @@ -4,10 +4,10 @@ */ #include +#include #include "simsys.h" -#include "../display/simgraph.h" -#include "../simdebug.h" +#include "../unicode.h" #define MAX_SIZE (4096) @@ -48,7 +48,7 @@ size_t dr_paste(char *target, size_t max_length) // ensure that max_length aligns with logical character boundaries of clipboard content size_t tmp_length = 0; size_t byte_count; - while( tmp_length+(byte_count=get_next_char(content+tmp_length,0u))<=max_length ) { + while( tmp_length+(byte_count=utf8_get_next_char(content+tmp_length,0u))<=max_length ) { tmp_length += byte_count; } max_length = tmp_length; diff --git a/sys/clipboard_s2.cc b/sys/clipboard_s2.cc index fbc6afb4abe..da5b043791c 100644 --- a/sys/clipboard_s2.cc +++ b/sys/clipboard_s2.cc @@ -9,6 +9,7 @@ #include "../display/simgraph.h" #include "../simdebug.h" #include "../dataobj/translator.h" +#include "../unicode.h" #ifndef __APPLE__ #ifdef ALT_SDL_DIR diff --git a/sys/clipboard_w32.cc b/sys/clipboard_w32.cc index e6cb98b4c6e..e45681efa1a 100644 --- a/sys/clipboard_w32.cc +++ b/sys/clipboard_w32.cc @@ -11,6 +11,7 @@ #include "../display/simgraph.h" #include "../simdebug.h" #include "../dataobj/translator.h" +#include "../unicode.h" /** diff --git a/sys/simsys_s2.cc b/sys/simsys_s2.cc index 6050f741d9b..0af315a052f 100644 --- a/sys/simsys_s2.cc +++ b/sys/simsys_s2.cc @@ -34,6 +34,7 @@ extern char **__argv; #include "../gui/components/gui_textinput.h" #include "../simintr.h" #include "../simworld.h" +#include "../unicode.h" // Maybe Linux is not fine too, had critical bugs... diff --git a/sys/simsys_w.cc b/sys/simsys_w.cc index 6876733c290..f096aaebd76 100644 --- a/sys/simsys_w.cc +++ b/sys/simsys_w.cc @@ -41,6 +41,7 @@ extern char **__argv; #include "../gui/gui_frame.h" #include "../gui/components/gui_component.h" #include "../gui/components/gui_textinput.h" +#include "../unicode.h" // needed for wheel #ifndef WM_MOUSEWHEEL diff --git a/tests/all_tests.nut b/tests/all_tests.nut new file mode 100644 index 00000000000..39b460835f4 --- /dev/null +++ b/tests/all_tests.nut @@ -0,0 +1,176 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// list containing all tests +// + +include("tests/test_building") +include("tests/test_climate") +include("tests/test_depot") +include("tests/test_dir") +include("tests/test_factory") +include("tests/test_good") +include("tests/test_halt") +include("tests/test_headquarters") +include("tests/test_label") +include("tests/test_player") +include("tests/test_powerline") +include("tests/test_reservation") +include("tests/test_scenario") +include("tests/test_sign") +include("tests/test_slope") +include("tests/test_terraform") +include("tests/test_trees") +include("tests/test_way_bridge") +include("tests/test_way_road") +include("tests/test_way_runway") +include("tests/test_way_tram") +include("tests/test_way_tunnel") +include("tests/test_wayobj") + + +all_tests <- [ + test_building_build_house, + test_building_build_multi_tile_sloped, + test_building_rotate_house, + test_building_rotate_harbour, + test_building_rotate_station, + test_building_rotate_factory, + test_climate_invalid, + test_climate_flat, + test_climate_cliff, + test_depot_build_invalid_params, + test_depot_build_invalid_pos_outside_map, + test_depot_build_invalid_shipyard_on_land, + test_depot_build_invalid_in_water, + test_depot_build_invalid_on_runway, + test_depot_build_invalid_on_non_end_road, + test_depot_build_invalid_on_stop, + test_depot_build_invalid_on_depot, + test_depot_build_invalid_on_rail_crossing, + test_depot_build_as_public_player, + test_depot_build_road, +// test_depot_build_road_on_tram_crossing, + test_depot_build_water, +// test_depot_build_monorail, +// test_depot_build_tram, + test_depot_build_sloped, +// test_depot_build_on_tunnel_entrance, +// test_depot_build_on_bridge_end, + test_depot_build_on_halt, +// test_depot_convoy_add_normal, +// test_depot_convoy_add_nonelectrified, + test_dir_is_single, + test_dir_is_twoway, + test_dir_is_threeway, + test_dir_is_curve, + test_dir_is_straight, + test_dir_double, + test_dir_backward, + test_dir_to_slope + test_dir_to_coord, +// test_factory_build_pp, +// test_factory_build_with_fields, +// test_factory_build_climate, +// test_factory_link, + test_good_is_interchangeable, +// test_good_speed_bonus, +// test_halt_build_rail_single_tile, +// test_halt_build_harbour, +// test_halt_build_air, +// test_halt_build_multi_tile, +// test_halt_build_multi_mode, +// test_halt_build_multi_player, + test_halt_build_separate, +// test_halt_build_near_factory, +// test_halt_build_near_factories, +// test_halt_build_on_tunnel_entrance, +// test_halt_build_on_bridge_end, + test_halt_build_on_depot, +// test_halt_build_station_extension, +// test_halt_upgrade_downgrade, +// test_halt_make_public_single, +// test_halt_make_public_multi_tile, +// test_halt_make_public_underground, +// test_headquarters_build_flat, + test_label, +// test_player_cash, + test_player_isactive, +// test_player_headquarters, + test_player_name, + test_player_lines, +// test_powerline_connect, +// test_powerline_bridge, +// test_powerline_build_transformer, +// test_powerline_build_over_transformer +// test_powerline_build_transformer_multiple, +// test_powerline_ways, + test_reservation_clear_ground, + test_reservation_clear_road, + test_reservation_clear_rail, + test_scenario_rules_allow_forbid_tool, + test_scenario_rules_allow_forbid_way_tool_rect, + test_scenario_rules_allow_forbid_way_tool_cube, + test_scenario_rules_allow_forbid_tool_stacked_rect, + test_scenario_rules_allow_forbid_tool_stacked_cube, +// test_sign_build_oneway, +// test_sign_build_trafficlight, +// test_sign_build_private_way, +// test_sign_build_signal, +// test_sign_build_signal_multiple, +// test_sign_replace_signal, +// test_sign_signal_when_player_removed, + test_slope_to_dir, +// test_slope_can_set, + test_slope_set_and_restore, + test_slope_get_price, + test_slope_set_near_map_border, + test_slope_max_height_diff, + test_terraform_raise_lower_land, + test_terraform_raise_lower_land_at_map_border, + test_terraform_raise_lower_land_at_water_center, +// test_terraform_raise_lower_land_at_water_corner, +// test_terraform_raise_lower_land_at_water_edge, + test_terraform_raise_lower_land_below_way, + test_terraform_raise_lower_water_level, +// test_trees_plant_single, +// test_trees_plant_forest, +// test_way_bridge_build_ground, +// test_way_bridge_build_at_slope, +// test_way_bridge_build_at_slope_stacked, +// test_way_bridge_build_above_way, +// test_way_bridge_build_above_runway, +// test_way_bridge_planner, + test_way_road_build_single_tile, + test_way_road_build_remove_straight, +// test_way_road_build_straight, + test_way_road_build_bend, + test_way_road_build_parallel, + test_way_road_build_below_powerline, +// test_way_road_upgrade_downgrade, +// test_way_road_upgrade_downgrade_across_bridge, + test_way_road_build_cityroad, + test_way_road_has_double_slopes, +// test_way_road_make_public, +// test_way_runway_build_rw_flat, +// test_way_runway_build_tw_flat, +// test_way_runway_build_mixed_flat, +// test_way_tram_build_flat, +// test_way_tram_build_parallel, +// test_way_tram_build_on_road, +// test_way_tram_build_across_road_bridge, +// test_way_tram_build_across_crossing, +// test_way_tram_build_in_tunel, +// test_way_tram_has_double_slopes, +// test_way_tunnel_build_straight, +// test_way_tunnel_build_up_down, +// test_way_tunnel_make_public, +// test_wayobj_build_straight, +// test_wayobj_build_disconnected, +// test_wayobj_upgrade_downgrade, +// test_wayobj_electrify_depot +] diff --git a/tests/empty-16x16.sve b/tests/empty-16x16.sve new file mode 100644 index 00000000000..15855adb577 Binary files /dev/null and b/tests/empty-16x16.sve differ diff --git a/tests/scenario.nut b/tests/scenario.nut new file mode 100644 index 00000000000..68313606564 --- /dev/null +++ b/tests/scenario.nut @@ -0,0 +1,115 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +map.file = "empty-16x16.sve" + +scenario.short_description = "Automated tests" +scenario.author = "ceeac" +scenario.version = "0.1" + + +// +// Includes +// + +include("test_helpers") +include("all_tests") + + +// +// test runner stuff +// + +local num_tests_done = 0 +local num_tests = all_tests.len() +local error_msg = null + + +function run_all_tests() +{ + num_tests_done = 0 + error_msg = null + + print("============================================================") + print("== Running tests ... =======================================") + print("============================================================") + + foreach (i,test_func in all_tests) { + local func_name = test_func.getinfos().name + print("[" + (num_tests_done+1) + "/" + num_tests + "] " + func_name) + test_func() // run the test + ++num_tests_done + } + + print("Tests completed successfully.") +} + + +// +// Required for scenario +// + +function get_rule_text(pl) +{ + return ttext("Don't touch.") +} + + +function get_goal_text(pl) +{ + return ttext("Wait for all tests to pass.") +} + + +function get_info_text(pl) +{ + if (error_msg != null) { + // an error ocurred + return error_msg + } + + local msg = ttext("{ndone}/{ntests} completed.") + msg.ndone = num_tests_done + msg.ntests = num_tests + return msg +} + + +function get_result_text(pl) +{ + return get_info_text(pl) +} + + +function is_tool_allowed(pl, tool_id, wt) +{ + return true +} + + +function start() +{ + run_all_tests() +} + + +function resume_game() +{ + run_all_tests() +} + + +function is_scenario_completed(pl) +{ + if (error_msg != null) { + return -1 + } + else if (num_tests == 0) { + return 100 + } + + return (100*num_tests_done) / num_tests +} diff --git a/tests/test_helpers.nut b/tests/test_helpers.nut new file mode 100644 index 00000000000..832a6b4426f --- /dev/null +++ b/tests/test_helpers.nut @@ -0,0 +1,189 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Test helpers +// +function make_assertion_str(val) +{ + if (typeof val == "string") { + return "\"" + val + "\"" + } + else { + return "" + val + } +} + + +function ASSERT_EQUAL(act, exp) +{ + if (!(act == exp)) { + local err = ttext("Assertion failed, '{act} == {exp}' was not true") + err.act = make_assertion_str(act) + err.exp = make_assertion_str(exp) + throw err.tostring() + } +} + +function ASSERT_LESS(lhs, rhs) +{ + if (!(lhs < rhs)) { + local err = ttext("Assertion failed, '{lhs} < {rhs}' was not true") + err.lhs = make_assertion_str(lhs) + err.rhs = make_assertion_str(rhs) + throw err.tostring() + } +} + +function ASSERT_GREATER(lhs, rhs) +{ + if (!(lhs > rhs)) { + local err = ttext("Assertion failed, '{lhs} > {rhs}' was not true") + err.lhs = make_assertion_str(lhs) + err.rhs = make_assertion_str(rhs) + throw err.tostring() + } +} + +function ASSERT_TRUE(a) +{ + if (!(a == true)) { + local err = ttext("Assertion failed, '{a}' was not true") + err.a = make_assertion_str(a) + throw err.tostring() + } +} + + +function ASSERT_FALSE(a) +{ + if (!(a == false)) { + local err = ttext("Assertion failed, '{a}' was not false") + err.a = make_assertion_str(a) + throw err.tostring() + } +} + + +// Set player funds (incl. non-cash assets) to some specific amount (in credit-cents) +function SET_PLAYER_FUNDS(pl, amount) +{ + pl.book_cash(amount - pl.get_current_net_wealth()) +} + + +function RESET_ALL_PLAYER_FUNDS() +{ + local default_cash = 33*1000*1000 * 100 + + for (local i = 0; i < 8; ++i) { + local pl = player_x(i) + if (pl.is_valid()) { + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + SET_PLAYER_FUNDS(pl, default_cash) + } + } +} + + +function char_to_dir(ch) +{ + switch (ch) { + case '.': return dir.none // '.' == dontcare + case '0': return dir.none + case '1': return dir.north + case '2': return dir.east + case '3': return dir.northeast + case '4': return dir.south + case '5': return dir.northsouth + case '6': return dir.southeast + case '7': return dir.northsoutheast + case '8': return dir.west + case '9': return dir.northwest + case 'A': return dir.eastwest + case 'B': return dir.northeastwest + case 'C': return dir.southwest + case 'D': return dir.northsouthwest + case 'E': return dir.southeastwest + case 'F': return dir.all + } + + ASSERT_TRUE(false) // should not reach here +} + + +function ASSERT_WAY_PATTERN(waytype, lefttop, pattern) +{ + local z = lefttop.z + + for (local y = 0; y < pattern.len(); ++y) { + for (local x = 0; x < pattern[y].len(); ++x) { + local tile = square_x(lefttop.x + x, lefttop.y + y).get_tile_at_height(z) + local expected_dir = char_to_dir(pattern[y][x]) + + if (tile != null) { + local actual_dir = 0 + if (waytype != wt_power) { + actual_dir = tile.get_way_dirs(waytype) + } + else { + // powerlines connect to other powerlines automatically. + local powerline = tile.find_object(mo_powerline) + actual_dir = 0 + + if (powerline) { + for (local i = 0; i < 4; ++i) { + local offset = dir.to_coord(1< 0; --h) { + for (local y = 0; y < 3; ++y) { + for (local x = 0; x < 3; ++x) { + ASSERT_EQUAL(setslope.work(pl, pos + coord3d(x, y, h), "" + slope.all_down_slope), null) + } + } + } + + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(15, 15, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() +} + diff --git a/tests/tests/test_depot.nut b/tests/tests/test_depot.nut new file mode 100644 index 00000000000..1479360b431 --- /dev/null +++ b/tests/tests/test_depot.nut @@ -0,0 +1,670 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for depots +// + + +function test_depot_build_invalid_params() +{ + local pl = player_x(0) + local public_pl = player_x(1) + + // invalid default_param + { + local error_caught = false + try { + ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), null), "") + } + catch (e) { + error_caught = true; + ASSERT_EQUAL(e, "Error during initializing tool") + } + + local error_caught = false + try { + ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), ""), "") + } + catch (e) { + error_caught = true; + ASSERT_EQUAL(e, "Error during initializing tool") + } + + local error_caught = false + try { + ASSERT_EQUAL(command_x(tool_build_depot).work(pl, coord3d(0, 0, 0), "nonexistent"), "") + } + catch (e) { + error_caught = true; + ASSERT_EQUAL(e, "Error during initializing tool") + } + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_pos_outside_map() +{ + // Cannot build outside of map borders + ASSERT_EQUAL(command_x.build_depot(player_x(0), coord3d(-1, -1, 0), get_depot_by_wt(wt_road)), "") + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_shipyard_on_land() +{ + // no shipyards on land + ASSERT_EQUAL(command_x.build_depot(player_x(0), coord3d(4, 2, 0), get_depot_by_wt(wt_water)), + "Cannot built depot here!") + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_in_water() +{ + local pl = player_x(0) + + // no land depots on water + { + ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), get_depot_by_wt(wt_air)), "Cannot built depot here!") + ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_on_runway() +{ + // no hangars on runways // FIXME +// { +// ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), runway_desc, true), null) +// ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 5, 0), hangar), "Cannot built depot here!") +// ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), "" + wt_air), null) +// } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_on_non_end_road() +{ + local pl = player_x(0) + local road_depot = get_depot_by_wt(wt_road) + local road_descs = way_desc_x.get_available_ways(wt_road, st_flat) + road_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local road_desc = road_descs[1] + + ASSERT_TRUE(road_desc != null) + ASSERT_TRUE(road_depot != null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // no depot in the middle of a road + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Cannot built depot here!") + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), road_desc, true), null) + + // no depot on road-road crossings + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Cannot built depot here!") + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_on_stop() +{ + local pl = player_x(0) + local road_depot = get_depot_by_wt(wt_road) + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + + local road_descs = way_desc_x.get_available_ways(wt_road, st_flat) + road_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local road_desc = road_descs[1] + + ASSERT_TRUE(station_desc != null) + ASSERT_TRUE(road_desc != null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // no depot over a stop + { + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc, 0), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), "Cannot built depot here!") + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_on_depot() +{ + local pl = player_x(0) + local road_depot = get_depot_by_wt(wt_road) + + local road_descs = way_desc_x.get_available_ways(wt_road, st_flat) + road_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local road_desc = road_descs[1] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // do not replace existing depots + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), road_depot), "Cannot built depot here!") + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_invalid_on_rail_crossing() +{ + local pl = player_x(0) + local road_depot = get_depot_by_wt(wt_road) + + local road_descs = way_desc_x.get_available_ways(wt_road, st_flat) + road_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local road_desc = road_descs[1] + + local rail_descs = way_desc_x.get_available_ways(wt_rail, st_flat) + rail_descs.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local rail_desc = rail_descs[1] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), rail_desc, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // no depot on road-rail crossings + { +// ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 3, 0), road_depot), "Cannot built depot here!") + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_as_public_player() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_road) + local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + // preconditions + ASSERT_TRUE(default_depot != null) + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 0) + ASSERT_TRUE(way_desc != null) + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 3, 0), way_desc, true), null) + + // build as public player; this is impossible in Standard but allowed in Extended + { + command_x.build_depot(public_pl, coord3d(4, 2 , 0), default_depot) + + ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) + ASSERT_EQUAL(depot_x.get_depot_list(public_pl, wt_road).len(), 1) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 3, 0), "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_road() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_road) + local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + // preconditions + ASSERT_TRUE(default_depot != null) + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 0) + ASSERT_TRUE(way_desc != null) + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) + + // build depot as normal player, all 4 directions + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) + + ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_road).len(), 4) + + ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) + ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) + ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) + ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 3, 0)), null) + + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_road_on_tram_crossing() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_road) + local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local tramtrack = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) + + // FIXME building tram track directly across road does not work + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 2, 0), coord3d(5, 2, 0), tramtrack, true), null) +// ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(5, 2, 0), tramtrack, true), null) + + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), "Cannot built depot here!") + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_water() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_water) + + // preconditions + ASSERT_TRUE(default_depot != null) + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_water).len(), 0) + + ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_water), null) + + // build depot as normal player + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) + ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_water).len(), 1) + ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_set_climate).work(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_monorail() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_monorail) + local way_desc = way_desc_x.get_available_ways(wt_monorail, st_flat)[0] + local elevated_monorail = way_desc_x.get_available_ways(wt_monorail, st_elevated)[0] + + // preconditions + ASSERT_TRUE(default_depot != null) + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_monorail).len(), 0) + ASSERT_TRUE(way_desc != null) + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) + + // build depot as normal player, all 4 directions + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) + + ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_monorail).len(), 4) + + ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) + ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) + ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) + ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_monorail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_monorail), null) + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), elevated_monorail, true), null) + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), elevated_monorail, true), null) + + // build elevated monorail depot, with foundation, all 4 directions + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 1), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 1), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 1), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 1), default_depot), null) + + ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_monorail).len(), 4) + + ASSERT_TRUE(depot_x(4, 2, 1).is_valid()) + ASSERT_TRUE(depot_x(5, 3, 1).is_valid()) + ASSERT_TRUE(depot_x(4, 4, 1).is_valid()) + ASSERT_TRUE(depot_x(3, 3, 1).is_valid()) + + ASSERT_FALSE(square_x(4, 2).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(5, 3).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(4, 4).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(3, 3).get_tile_at_height(0).is_empty()) + + // This leaves behind the depot foundations + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 1)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 1)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 1)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 1)), null) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 1), coord3d(4, 4, 1), "" + wt_monorail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 1), coord3d(5, 3, 1), "" + wt_monorail), null) + + ASSERT_FALSE(square_x(4, 2).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(5, 3).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(4, 4).get_tile_at_height(0).is_empty()) + ASSERT_FALSE(square_x(3, 3).get_tile_at_height(0).is_empty()) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_tram() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local default_depot = get_depot_by_wt(wt_tram) + local way_desc = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + + // preconditions + ASSERT_TRUE(default_depot != null) + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_tram).len(), 0) + ASSERT_TRUE(way_desc != null) + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 3, 0), coord3d(5, 3, 0), way_desc, true), null) + + // build depot as normal player, all 4 directions + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), default_depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), default_depot), null) + + ASSERT_EQUAL(depot_x.get_depot_list(player_x(0), wt_tram).len(), 4) + + ASSERT_TRUE(depot_x(4, 2, 0).is_valid()) + ASSERT_TRUE(depot_x(5, 3, 0).is_valid()) + ASSERT_TRUE(depot_x(4, 4, 0).is_valid()) + ASSERT_TRUE(depot_x(3, 3, 0).is_valid()) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + + // note: wt_tram does not work + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), "" + wt_rail), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_sloped() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local wayremover = command_x(tool_remove_way) + + local pos = coord3d(4, 3, 0) + + { + for (local sl = slope.flat+1; sl < slope.raised; ++sl) { + ASSERT_EQUAL(setslope.work(pl, pos, "" + sl), null) + + local d = slope.to_dir(sl) + if (d != dir.none) { // only consider slopes we can build roads on + RESET_ALL_PLAYER_FUNDS() + + local c = dir.to_coord(dir.backward(d)) + local adjacent = pos + coord3d(c.x, c.y, 0) + ASSERT_EQUAL(command_x.build_way(pl, adjacent, pos, road, true), null) + local old_maintenance = pl.get_current_maintenance() + + ASSERT_EQUAL(command_x.build_depot(pl, pos, get_depot_by_wt(wt_road)), "Cannot built depot here!") // sic! + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) + + ASSERT_EQUAL(wayremover.work(pl, pos, adjacent, "" + wt_road), null) + } + } + } + + // clean up + ASSERT_EQUAL(setslope.work(pl, pos, "" + slope.flat), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_on_tunnel_entrance() +{ + local pl = player_x(0) + local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + + ASSERT_EQUAL(raise.work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 3, 0)), null) + + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 1, 0), rail_tunnel.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 2, 0), rail_tunnel.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(5, 2, 0), rail_tunnel.get_name()), null) + + // Building depots on tunnel entrance works (contrary to stations) + { + for (local d = dir.north; d < dir.all; d = d*2) { + local p = coord3d(4, 2, 0) + dir.to_coord(d) + + ASSERT_EQUAL(command_x.build_depot(pl, p, get_depot_by_wt(wt_rail)), null) + } + } + + local remover = command_x(tool_remover) + remover.set_flags(2) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) + ASSERT_EQUAL(remover.work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0)), null) + + ASSERT_EQUAL(lower.work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 3, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_on_bridge_end() +{ + local pl = player_x(0) + local rail_bridge = bridge_desc_x.get_available_bridges(wt_rail)[0] + local setslope = command_x(tool_setslope) + + // north-south direction + { + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.north), null) + + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(4, 2, 0), rail_bridge.get_name()), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 4, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.flat), null) + } + + // east-west direction + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.east), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 0), "" + slope.west), null) + + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(3, 3, 0), rail_bridge.get_name()), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 0), "" + slope.flat), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_build_on_halt() +{ + local pl = player_x(0) + local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] + local depot = get_depot_by_wt(wt_rail) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail_desc, true), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), null) + + { + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), depot), "Cannot built depot here!") + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_convoy_add_normal() +{ + // build depot + local pl = player_x(0) + local public_pl = player_x(1) + local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(0, 0, 0), coord3d(0, 1, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(0, 0, 0), get_depot_by_wt(wt_road)), null) + + ASSERT_EQUAL(depot_x.get_depot_list(pl, wt_road).len(), 1) + local the_depot = depot_x.get_depot_list(pl, wt_road)[0] + ASSERT_EQUAL(the_depot.get_convoy_list().len(), 0) + + local vehicle_desc = vehicle_desc_x.get_available_vehicles(wt_road)[0] + ASSERT_TRUE(vehicle_desc != null) + + { + ASSERT_TRUE(the_depot.append_vehicle(pl, convoy_x(0), vehicle_desc)) + + ASSERT_EQUAL(the_depot.get_convoy_list().len(), 1) + local cnv = the_depot.get_convoy_list()[0] + + ASSERT_EQUAL(cnv.get_vehicles().len(), 1) + ASSERT_EQUAL(cnv.get_pos().tostring(), coord3d(0, 0, 0).tostring()) + ASSERT_EQUAL(cnv.get_pos().tostring(), cnv.get_home_depot().tostring()) + ASSERT_EQUAL(cnv.get_speed(), 0) + ASSERT_EQUAL(cnv.get_traveled_distance()[0], 0) + ASSERT_EQUAL(cnv.get_distance_traveled_total(), 0) + ASSERT_TRUE(cnv.is_in_depot()) + ASSERT_FALSE(cnv.is_waiting()) + ASSERT_EQUAL(cnv.get_line(), null) + + ASSERT_TRUE(cnv.destroy(player_x(0))) + ASSERT_EQUAL(the_depot.get_convoy_list().len(), 0) + } + + // clean up + local remover = command_x(tool_remover) + ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 0, 0)), null) + ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 0, 0)), null) + ASSERT_EQUAL(remover.work(player_x(1), tile_x(0, 1, 0)), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_depot_convoy_add_nonelectrified() +{ + local pl = player_x(0) + local way_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local electro_loco = vehicle_desc_x.get_available_vehicles(wt_rail).filter(@(idx, v) ( v.needs_electrification() && v.can_be_first() ))[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), get_depot_by_wt(wt_rail)), null) + + local the_depot = depot_x(4, 2, 0) + + { + ASSERT_TRUE(the_depot.append_vehicle(pl, convoy_x(0), electro_loco)) // can add loco via script even if depot is not electrified + + ASSERT_EQUAL(the_depot.get_convoy_list().len(), 1) + local cnv = the_depot.get_convoy_list()[0] + ASSERT_EQUAL(cnv.get_vehicles().len(), 1) + ASSERT_EQUAL(cnv.get_pos().tostring(), coord3d(4, 2, 0).tostring()) + + ASSERT_TRUE(cnv.destroy(pl)) + } + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_dir.nut b/tests/tests/test_dir.nut new file mode 100644 index 00000000000..15cdf01093f --- /dev/null +++ b/tests/tests/test_dir.nut @@ -0,0 +1,256 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for dir/coord +// + + +function test_dir_is_single() +{ + ASSERT_FALSE(dir.is_single(dir.none)) + + ASSERT_TRUE(dir.is_single(dir.north)) + ASSERT_TRUE(dir.is_single(dir.east)) + ASSERT_TRUE(dir.is_single(dir.south)) + ASSERT_TRUE(dir.is_single(dir.west)) + + ASSERT_FALSE(dir.is_single(dir.northeast)) + ASSERT_FALSE(dir.is_single(dir.northsouth)) + ASSERT_FALSE(dir.is_single(dir.northwest)) + ASSERT_FALSE(dir.is_single(dir.southeast)) + ASSERT_FALSE(dir.is_single(dir.eastwest)) + ASSERT_FALSE(dir.is_single(dir.southwest)) + + ASSERT_FALSE(dir.is_single(dir.northsoutheast)) + ASSERT_FALSE(dir.is_single(dir.northeastwest)) + ASSERT_FALSE(dir.is_single(dir.northsouthwest)) + ASSERT_FALSE(dir.is_single(dir.southeastwest)) + + ASSERT_FALSE(dir.is_single(dir.all)) +} + + +function test_dir_is_twoway() +{ + ASSERT_FALSE(dir.is_twoway(dir.none)) + + ASSERT_FALSE(dir.is_twoway(dir.north)) + ASSERT_FALSE(dir.is_twoway(dir.east)) + ASSERT_FALSE(dir.is_twoway(dir.south)) + ASSERT_FALSE(dir.is_twoway(dir.west)) + + ASSERT_TRUE(dir.is_twoway(dir.northeast)) + ASSERT_TRUE(dir.is_twoway(dir.northsouth)) + ASSERT_TRUE(dir.is_twoway(dir.northwest)) + ASSERT_TRUE(dir.is_twoway(dir.southeast)) + ASSERT_TRUE(dir.is_twoway(dir.eastwest)) + ASSERT_TRUE(dir.is_twoway(dir.southwest)) + + ASSERT_FALSE(dir.is_twoway(dir.northsoutheast)) + ASSERT_FALSE(dir.is_twoway(dir.northeastwest)) + ASSERT_FALSE(dir.is_twoway(dir.northsouthwest)) + ASSERT_FALSE(dir.is_twoway(dir.southeastwest)) + + ASSERT_FALSE(dir.is_twoway(dir.all)) +} + + +function test_dir_is_threeway() +{ + ASSERT_FALSE(dir.is_threeway(dir.none)) + + ASSERT_FALSE(dir.is_threeway(dir.north)) + ASSERT_FALSE(dir.is_threeway(dir.east)) + ASSERT_FALSE(dir.is_threeway(dir.south)) + ASSERT_FALSE(dir.is_threeway(dir.west)) + + ASSERT_FALSE(dir.is_threeway(dir.northeast)) + ASSERT_FALSE(dir.is_threeway(dir.northsouth)) + ASSERT_FALSE(dir.is_threeway(dir.northwest)) + ASSERT_FALSE(dir.is_threeway(dir.southeast)) + ASSERT_FALSE(dir.is_threeway(dir.eastwest)) + ASSERT_FALSE(dir.is_threeway(dir.southwest)) + + ASSERT_TRUE(dir.is_threeway(dir.northsoutheast)) + ASSERT_TRUE(dir.is_threeway(dir.northeastwest)) + ASSERT_TRUE(dir.is_threeway(dir.northsouthwest)) + ASSERT_TRUE(dir.is_threeway(dir.southeastwest)) + + ASSERT_TRUE(dir.is_threeway(dir.all)) +} + + +function test_dir_is_curve() +{ + ASSERT_FALSE(dir.is_curve(dir.none)) + + ASSERT_FALSE(dir.is_curve(dir.north)) + ASSERT_FALSE(dir.is_curve(dir.east)) + ASSERT_FALSE(dir.is_curve(dir.south)) + ASSERT_FALSE(dir.is_curve(dir.west)) + + ASSERT_TRUE(dir.is_curve(dir.northeast)) + ASSERT_FALSE(dir.is_curve(dir.northsouth)) + ASSERT_TRUE(dir.is_curve(dir.northwest)) + ASSERT_TRUE(dir.is_curve(dir.southeast)) + ASSERT_FALSE(dir.is_curve(dir.eastwest)) + ASSERT_TRUE(dir.is_curve(dir.southwest)) + + ASSERT_FALSE(dir.is_curve(dir.northsoutheast)) + ASSERT_FALSE(dir.is_curve(dir.northeastwest)) + ASSERT_FALSE(dir.is_curve(dir.northsouthwest)) + ASSERT_FALSE(dir.is_curve(dir.southeastwest)) + + ASSERT_FALSE(dir.is_curve(dir.all)) +} + + +function test_dir_is_straight() +{ + ASSERT_FALSE(dir.is_straight(dir.none)) + + ASSERT_TRUE(dir.is_straight(dir.north)) + ASSERT_TRUE(dir.is_straight(dir.east)) + ASSERT_TRUE(dir.is_straight(dir.south)) + ASSERT_TRUE(dir.is_straight(dir.west)) + + ASSERT_FALSE(dir.is_straight(dir.northeast)) + ASSERT_TRUE(dir.is_straight(dir.northsouth)) + ASSERT_FALSE(dir.is_straight(dir.northwest)) + ASSERT_FALSE(dir.is_straight(dir.southeast)) + ASSERT_TRUE(dir.is_straight(dir.eastwest)) + ASSERT_FALSE(dir.is_straight(dir.southwest)) + + ASSERT_FALSE(dir.is_straight(dir.northsoutheast)) + ASSERT_FALSE(dir.is_straight(dir.northeastwest)) + ASSERT_FALSE(dir.is_straight(dir.northsouthwest)) + ASSERT_FALSE(dir.is_straight(dir.southeastwest)) + + ASSERT_FALSE(dir.is_straight(dir.all)) +} + + +function test_dir_double() +{ + ASSERT_EQUAL(dir.double(dir.none), dir.none) + + ASSERT_EQUAL(dir.double(dir.north), dir.northsouth) + ASSERT_EQUAL(dir.double(dir.east), dir.eastwest) + ASSERT_EQUAL(dir.double(dir.south), dir.northsouth) + ASSERT_EQUAL(dir.double(dir.west), dir.eastwest) + + ASSERT_EQUAL(dir.double(dir.northeast), dir.none) + ASSERT_EQUAL(dir.double(dir.northsouth), dir.northsouth) + ASSERT_EQUAL(dir.double(dir.northwest), dir.none) + ASSERT_EQUAL(dir.double(dir.southeast), dir.none) + ASSERT_EQUAL(dir.double(dir.eastwest), dir.eastwest) + ASSERT_EQUAL(dir.double(dir.southwest), dir.none) + + ASSERT_EQUAL(dir.double(dir.northsoutheast), dir.none) + ASSERT_EQUAL(dir.double(dir.northeastwest), dir.none) + ASSERT_EQUAL(dir.double(dir.northsouthwest), dir.none) + ASSERT_EQUAL(dir.double(dir.southeastwest), dir.none) + + ASSERT_EQUAL(dir.double(dir.all), dir.none) +} + + +function test_dir_backward() +{ + ASSERT_EQUAL(dir.backward(dir.none), dir.all) + + ASSERT_EQUAL(dir.backward(dir.north), dir.south) + ASSERT_EQUAL(dir.backward(dir.east), dir.west) + ASSERT_EQUAL(dir.backward(dir.south), dir.north) + ASSERT_EQUAL(dir.backward(dir.west), dir.east) + + ASSERT_EQUAL(dir.backward(dir.northeast), dir.southwest) + ASSERT_EQUAL(dir.backward(dir.northsouth), dir.northsouth) + ASSERT_EQUAL(dir.backward(dir.northwest), dir.southeast) + ASSERT_EQUAL(dir.backward(dir.southeast), dir.northwest) + ASSERT_EQUAL(dir.backward(dir.eastwest), dir.eastwest) + ASSERT_EQUAL(dir.backward(dir.southwest), dir.northeast) + + ASSERT_EQUAL(dir.backward(dir.northsoutheast), dir.west) + ASSERT_EQUAL(dir.backward(dir.northeastwest), dir.south) + ASSERT_EQUAL(dir.backward(dir.northsouthwest), dir.east) + ASSERT_EQUAL(dir.backward(dir.southeastwest), dir.north) + + ASSERT_EQUAL(dir.backward(dir.all), dir.none) +} + + +function test_dir_to_slope() +{ + ASSERT_EQUAL(dir.to_slope(dir.none), slope.flat) + + ASSERT_EQUAL(dir.to_slope(dir.north), slope.south) + ASSERT_EQUAL(dir.to_slope(dir.east), slope.west) + ASSERT_EQUAL(dir.to_slope(dir.south), slope.north) + ASSERT_EQUAL(dir.to_slope(dir.west), slope.east) + + ASSERT_EQUAL(dir.to_slope(dir.northeast), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.northsouth), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.northwest), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.southeast), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.eastwest), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.southwest), slope.flat) + + ASSERT_EQUAL(dir.to_slope(dir.northsouthwest), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.northeastwest), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.northsouthwest), slope.flat) + ASSERT_EQUAL(dir.to_slope(dir.southeastwest), slope.flat) + + ASSERT_EQUAL(dir.to_slope(dir.all), slope.flat) + + local error_raised = false + try { + // out-of-range dir: ANDed with dir.all + ASSERT_EQUAL(dir.to_slope(0x2A), dir.to_slope(0x0A)) + } + catch (e) { + error_raised = true; + } + + ASSERT_FALSE(error_raised) +} + + +function test_dir_to_coord() +{ + ASSERT_EQUAL(dir.to_coord(dir.none).tostring(), coord( 0, 0).tostring()) + + ASSERT_EQUAL(dir.to_coord(dir.north).tostring(), coord( 0, -1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.east).tostring(), coord( 1, 0).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.south).tostring(), coord( 0, 1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.west).tostring(), coord(-1, 0).tostring()) + + ASSERT_EQUAL(dir.to_coord(dir.northeast).tostring(), coord( 1, -1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.northsouth).tostring(), coord( 0, 0).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.southeast).tostring(), coord( 1, 1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.northwest).tostring(), coord(-1, -1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.eastwest).tostring(), coord( 0, 0).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.southwest).tostring(), coord(-1, 1).tostring()) + + ASSERT_EQUAL(dir.to_coord(dir.northsouthwest).tostring(), coord(-1, 0).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.southeastwest).tostring(), coord( 0, 1).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.northsoutheast).tostring(), coord( 1, 0).tostring()) + ASSERT_EQUAL(dir.to_coord(dir.northeastwest).tostring(), coord( 0, -1).tostring()) + + ASSERT_EQUAL(dir.to_coord(dir.all).tostring(), coord( 0, 0).tostring()) + + local error_raised = false + try { + dir.to_coord(42) // out-of-range dir + } + catch (e) { + ASSERT_EQUAL(e, "Invalid dir 42 (valid values are 0..15)") + error_raised = true + } + + ASSERT_TRUE(error_raised) +} diff --git a/tests/tests/test_factory.nut b/tests/tests/test_factory.nut new file mode 100644 index 00000000000..998f7520471 --- /dev/null +++ b/tests/tests/test_factory.nut @@ -0,0 +1,336 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for factories / factory links +// + +function build_factory(pl, pos, ignore_climates, rotation, base_prod, name) +{ + local factory_builder = command_x(tool_build_factory) + local ignore_regions = "1" + return factory_builder.work(pl, pos, "" + ignore_climates.tointeger() + ignore_regions + rotation + base_prod + "," + name) +} + + +function test_factory_build_pp() +{ + local pl = player_x(0) + local public_pl = player_x(1) + + { + // FIXME Causes signed integer overflow even though < 2^30 + // local production = 1024 * 1000 * 1000 + local production = 1024 // 1/s + + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, production, "Aufwindkraftwerk"), null) + local factory = factory_x(3, 4) + ASSERT_TRUE(factory_x(5, 6) != null) + ASSERT_EQUAL(factory_list_x().get_count(), 1) + + // FIXME Max electricity production for PPs not queryable via script? + + ASSERT_EQUAL(factory.get_consumers().len(), 0) + ASSERT_EQUAL(factory.get_suppliers().len(), 0) + ASSERT_EQUAL(factory.get_production()[0], 0) + ASSERT_EQUAL(factory.get_power()[0], 0) + ASSERT_EQUAL(factory.get_boost_electric()[0], 0) + ASSERT_EQUAL(factory.get_boost_pax()[0], 0) + ASSERT_EQUAL(factory.get_boost_mail()[0], 0) + ASSERT_EQUAL(factory.get_pax_generated()[0], 0) + ASSERT_EQUAL(factory.get_pax_departed()[0], 0) + ASSERT_EQUAL(factory.get_pax_arrived()[0], 0) + ASSERT_EQUAL(factory.get_mail_generated()[0], 0) + ASSERT_EQUAL(factory.get_tile_list().len(), 3 * 3) + ASSERT_EQUAL(factory.get_halt_list().len(), 0) + ASSERT_FALSE(factory.is_transformer_connected()) + ASSERT_EQUAL(factory.get_transformer(), null) + ASSERT_EQUAL(factory.get_field_count(), 0) + ASSERT_EQUAL(factory.get_min_field_count(), 0) + + ASSERT_TRUE(factory.input.len() == 0) + ASSERT_TRUE(factory.output.len() == 0) + } + + { + // and remove factory; only public player can remove them + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), "Der Besitzer erlaubt das Entfernen nicht") + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_factory_build_climate() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local setclimate = command_x(tool_set_climate) + + local allowed_climates = [ cl_temperate, cl_tundra, cl_rocky, cl_arctic ] + local all_climates = [ cl_water, cl_desert, cl_tropic, cl_mediterran, cl_temperate, cl_tundra, cl_rocky, cl_arctic ] + + { + foreach (cl in all_climates) { + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl), null) + + local err = build_factory(pl, coord3d(3, 4, 0), false /*don't ignore climates*/, 1, 1024, "Kohlegrube") + + if (allowed_climates.find(cl) != null) { + ASSERT_EQUAL(err, null) + ASSERT_EQUAL(factory_list_x().get_count(), 1) + + // and remove it + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + else { + ASSERT_EQUAL(err, "No suitable ground!") + ASSERT_EQUAL(factory_list_x().get_count(), 0) + } + + RESET_ALL_PLAYER_FUNDS() + } + } + + // and ignore climates + { + foreach (cl in all_climates) { + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl), null) + + local err = build_factory(pl, coord3d(3, 4, 0), 1 /*ignore climates*/, 1, 1024, "Kohlegrube") + + if (cl != cl_water) { + ASSERT_EQUAL(err, null) + ASSERT_EQUAL(factory_list_x().get_count(), 1) + + // and remove it + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + else { + ASSERT_EQUAL(err, "No suitable ground!") + ASSERT_EQUAL(factory_list_x().get_count(), 0) + + } + + RESET_ALL_PLAYER_FUNDS() + } + + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() + } + + // partially on disallowed climate + { + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(3, 7, 0), "" + cl_temperate), null) + ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) + + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), false /*don't ignore climates*/, 1, 1024, "Kohlegrube"), "No suitable ground!") + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), true /*ignore climates*/, 1, 1024, "Kohlegrube"), null) + + ASSERT_EQUAL(factory_list_x().get_count(), 1) + + // and remove it + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + + // clean up + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_factory_build_with_fields() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local setclimate = command_x(tool_set_climate) + + // build normal + { + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, 1024, "PVkraftwerk"), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + + // build in non-suitable climate + { + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_arctic), null) + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 0, 1, 1024, "PVkraftwerk"), "No suitable ground!") + } + + // Force building in non-suitable climate, factory should have no fields + { + ASSERT_EQUAL(build_factory(pl, coord3d(3, 4, 0), 1, 1, 1024, "PVkraftwerk"), null) + + for (local y = 0; y < 8; ++y) { + for (local x = 0; x < 8; ++x) { + ASSERT_EQUAL(tile_x(x, y, 0).find_object(mo_field), null) + } + } + + ASSERT_EQUAL(factory_list_x().get_count(), 1) + + // and remove it + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) + } + + // clean up + ASSERT_EQUAL(setclimate.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_factory_link() +{ + local pl = player_x(0) + local public_pl = player_x(1) + + local production = 1024 // 1/s + + // build coal mine + coal power plant + ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) + ASSERT_EQUAL(build_factory(public_pl, coord3d(6, 6, 0), 1, 1, production, "Kohlekraftwerk"), null) + + local mine = factory_x(0, 0) + local pp = factory_x(6, 6) + + ASSERT_TRUE(mine != null) + ASSERT_TRUE(pp != null) + + // test pre-conditions + { + ASSERT_EQUAL(mine.get_consumers().len(), 0) + ASSERT_EQUAL(mine.get_suppliers().len(), 0) +// ASSERT_EQUAL(mine.get_production()[0], 0) // FIXME this is 1024 when using the -scenario switch + ASSERT_EQUAL(mine.get_power()[0], 0) + ASSERT_TRUE(mine.input.len() == 0) + ASSERT_TRUE(mine.output.len() == 1) + + local out = mine.output.Kohle + ASSERT_TRUE(out != null) +// ASSERT_EQUAL(out.get_storage()[0], 0) // FIXME this is 122 when using the -scenario switch + ASSERT_EQUAL(out.get_received()[0], 0) + ASSERT_EQUAL(out.get_consumed()[0], 0) + ASSERT_EQUAL(out.get_in_transit()[0], 0) + ASSERT_EQUAL(out.get_delivered()[0], 0) + ASSERT_EQUAL(out.get_produced()[0], 0) + ASSERT_EQUAL(out.get_base_production(), production) + ASSERT_EQUAL(out.get_base_consumption(), production) + ASSERT_EQUAL(out.get_production_factor(), 100) + ASSERT_EQUAL(out.max_storage, 122) + + { + local error_caught = false + try { + ASSERT_EQUAL(out.get_consumption_factor(), 0) + } + catch (e) { + ASSERT_EQUAL(e, "No input slot [0] in factory at (0,0,0)") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + + ASSERT_EQUAL(pp.get_consumers().len(), 0) + ASSERT_EQUAL(pp.get_suppliers().len(), 0) + ASSERT_EQUAL(pp.get_production()[0], 0) + ASSERT_EQUAL(pp.get_power()[0], 0) + ASSERT_TRUE(pp.input.len() == 1) + ASSERT_TRUE(pp.output.len() == 0) + + local inp = pp.input.Kohle + ASSERT_TRUE(inp != null) + ASSERT_EQUAL(inp.get_storage()[0], 0) + ASSERT_EQUAL(inp.get_received()[0], 0) + ASSERT_EQUAL(inp.get_consumed()[0], 0) + ASSERT_EQUAL(inp.get_in_transit()[0], 0) + ASSERT_EQUAL(inp.get_delivered()[0], 0) + ASSERT_EQUAL(inp.get_produced()[0], 0) + ASSERT_EQUAL(inp.get_base_production(), production) + ASSERT_EQUAL(inp.get_base_consumption(), production) + ASSERT_EQUAL(inp.get_consumption_factor(), 100) + ASSERT_EQUAL(inp.max_storage, 1153) + + { + local error_caught = false + try { + ASSERT_EQUAL(inp.get_production_factor(), 0) + } + catch (e) { + ASSERT_EQUAL(e, "No output slot [-1] in factory at (6,6,0)") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + } + + // link factories + { + // default_param is necessary even though it is not used + ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) + + ASSERT_EQUAL(mine.get_consumers().len(), 1) + ASSERT_EQUAL(mine.get_suppliers().len(), 0) + + ASSERT_EQUAL(mine.get_consumers()[0].x, pp.x) + ASSERT_EQUAL(mine.get_consumers()[0].y, pp.y) + + ASSERT_EQUAL(pp.get_consumers().len(), 0) + ASSERT_EQUAL(pp.get_suppliers().len(), 1) + + ASSERT_EQUAL(pp.get_suppliers()[0].x, mine.x) + ASSERT_EQUAL(pp.get_suppliers()[0].y, mine.y) + } + + // second link should have no effect + { + ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) + + ASSERT_EQUAL(mine.get_consumers().len(), 1) + ASSERT_EQUAL(mine.get_suppliers().len(), 0) + + ASSERT_EQUAL(mine.get_consumers()[0].x, pp.x) + ASSERT_EQUAL(mine.get_consumers()[0].y, pp.y) + + ASSERT_EQUAL(pp.get_consumers().len(), 0) + ASSERT_EQUAL(pp.get_suppliers().len(), 1) + + ASSERT_EQUAL(pp.get_suppliers()[0].x, mine.x) + ASSERT_EQUAL(pp.get_suppliers()[0].y, mine.y) + } + + // unlink factories + { + local tool = command_x(tool_link_factory) + tool.set_flags(2) // ctrl == unlink + ASSERT_EQUAL(tool.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), ""), null) + + ASSERT_EQUAL(mine.get_consumers().len(), 0) + ASSERT_EQUAL(mine.get_suppliers().len(), 0) + + ASSERT_EQUAL(pp.get_consumers().len(), 0) + ASSERT_EQUAL(pp.get_suppliers().len(), 0) + } + + // second unlink should have no effect + { + local tool = command_x(tool_link_factory) + tool.set_flags(2) // ctrl == unlink + ASSERT_EQUAL(tool.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), ""), null) + + ASSERT_EQUAL(mine.get_consumers().len(), 0) + ASSERT_EQUAL(mine.get_suppliers().len(), 0) + + ASSERT_EQUAL(pp.get_consumers().len(), 0) + ASSERT_EQUAL(pp.get_suppliers().len(), 0) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(7, 7, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_good.nut b/tests/tests/test_good.nut new file mode 100644 index 00000000000..cec63092bda --- /dev/null +++ b/tests/tests/test_good.nut @@ -0,0 +1,53 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for goods / freight +// + +function test_good_is_interchangeable() +{ + local pax = good_desc_x.passenger + local mail = good_desc_x.mail + + local coal = good_desc_x("Kohle") + local ore = good_desc_x("Eisenerz") + + ASSERT_FALSE(pax.is_interchangeable(mail)) + ASSERT_FALSE(mail.is_interchangeable(pax)) + ASSERT_TRUE(pax.get_catg_index() != mail.get_catg_index()) + ASSERT_EQUAL(pax.get_catg_index(), 0) + ASSERT_EQUAL(mail.get_catg_index(), 1) + + ASSERT_TRUE(coal.is_interchangeable(ore)) + ASSERT_TRUE(ore.is_interchangeable(coal)) + ASSERT_EQUAL(coal.get_catg_index(), ore.get_catg_index()) + ASSERT_TRUE(coal.get_catg_index() >= 3) + ASSERT_TRUE(ore.get_catg_index() >= 3) +} + + +function test_good_speed_bonus() +{ + local pax = good_desc_x.passenger + local concrete = good_desc_x("Concrete") + + ASSERT_TRUE(concrete.calc_revenue(wt_road, 0) == concrete.calc_revenue(wt_road, 999)) // this has a speed bonus of 0 + + local wtypes = [ wt_road, wt_rail, wt_water, wt_monorail, wt_maglev, wt_narrowgauge ] + local goods = [ pax, concrete ] + + foreach (g in goods) { + foreach (wt in wtypes) { + local last = 0 + for (local speed = 0; speed <= 999; ++speed) { + local current = g.calc_revenue(wt, speed) + ASSERT_TRUE(current >= last) + last = current + } + } + } +} diff --git a/tests/tests/test_halt.nut b/tests/tests/test_halt.nut new file mode 100644 index 00000000000..b7d49848fc5 --- /dev/null +++ b/tests/tests/test_halt.nut @@ -0,0 +1,971 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// test for halts/stops +// + + +function test_halt_build_rail_single_tile() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + local stationbuilder = command_x(tool_build_station) + local wayremover = command_x(tool_remove_way) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] // road because it has double slopes available + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] // FIXME: null instead of pax fails + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + // preconditions + ASSERT_TRUE(road_desc != null) + ASSERT_TRUE(station_desc != null) + ASSERT_TRUE(bridge_desc != null) + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + local pos = coord3d(4, 2, 0) + + { + for (local sl = slope.flat; sl < slope.raised; ++sl) { + ASSERT_EQUAL(setslope.work(pl, pos, "" + sl), sl != slope.flat ? null : "") + ASSERT_EQUAL(stationbuilder.work(pl, pos, station_desc.get_name()), "No suitable way on the ground!") + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) + } + } + + ASSERT_EQUAL(setslope.work(pl, pos, "" + slope.flat), null) + + // cannot build on non-flat tile + { + for (local sl = slope.flat+1; sl < slope.raised; ++sl) { + ASSERT_EQUAL(setslope.work(pl, pos, "" + sl), null) + + local d = slope.to_dir(sl) + if (d != dir.none) { // only consider slopes we can build roads on + RESET_ALL_PLAYER_FUNDS() + + local c = dir.to_coord(dir.backward(d)) + local adjacent = pos + coord3d(c.x, c.y, 0) + ASSERT_EQUAL(command_x.build_way(pl, adjacent, pos, road_desc, true), null) + local old_maintenance = pl.get_current_maintenance() + + ASSERT_EQUAL(stationbuilder.work(pl, pos, station_desc.get_name()), "No suitable way on the ground!") + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + ASSERT_EQUAL(tile_x(pos.x, pos.y, pos.z).find_object(mo_building), null) + + ASSERT_EQUAL(wayremover.work(pl, pos, adjacent, "" + wt_road), null) + } + } + } + + ASSERT_EQUAL(setslope.work(pl, pos, "" + slope.flat), null) + + // build on bridge + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 4, 0), "" + slope.north), null) + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(3, 2, 0), bridge_desc.get_name()), null) + + local old_maintenance = pl.get_current_maintenance() + + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 1), station_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 1)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + // note z = 0 instead of 1 + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 2, 0), station_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + // note z = 0 instead of 1 + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 4, 0), station_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 4, 0)), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 4, 0), "" + slope.flat), null) + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_harbour() +{ + local pl = player_x(0) + local lower = command_x(tool_lower_land) + local raise = command_x(tool_raise_land) + local stationbuilder = command_x(tool_build_station) + local station_desc = building_desc_x.get_available_stations(building_desc_x.harbour, wt_water, good_desc_x.passenger)[0] // FIXME: null instead of pax fails + local setclimate = command_x(tool_set_climate) + + // build harbour on flat land: should fail + { + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "Dock must be built on single slope!") + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) + } + + // build harbour on sloped land: should fail + { + ASSERT_EQUAL(lower.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 3, 0)), null) + + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "") + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) + + ASSERT_EQUAL(lower.work(pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 4, 0)), null) + + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), "") + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) + } + + // build harbour on sloped land adjacent to water: Should succeed + { + ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 3, -1), coord3d(4, 3, -1), "" + cl_water), null) + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 2, -1), station_desc.get_name()), null) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 3, -1)), null) + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) + + ASSERT_EQUAL(setclimate.work(pl, coord3d(4, 3, -1), coord3d(4, 3, -1), "" + cl_mediterran), null) + } + + // clean up + ASSERT_EQUAL(raise.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 4, 0)), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_air() +{ + local pl = player_x(0) + local runway = way_desc_x.get_available_ways(wt_air, st_runway)[0] + local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] + local airhalt = building_desc_x("AirStop") + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), runway, true), null) + + // air halts must be on taxiways (contrary to the untranslated error message) + { + ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 5, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") + ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 6, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") + ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 7, 0), airhalt), "Flugzeughalt muss auf\nRunway liegen!\n") + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 6, 0), coord3d(3, 6, 0), taxiway, true), null) + + // not in the middle of a taxiway + { + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 6, 0), airhalt), "No terminal station here!") + } + + // end of taxiway -> success + { + ASSERT_EQUAL(command_x.build_station(pl, coord3d(3, 6, 0), airhalt), null) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 6, 0)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 6, 0), coord3d(5, 6, 0), "" + wt_air), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 5, 0), coord3d(5, 7, 0), "" + wt_air), null) + RESET_ALL_PLAYER_FUNDS() +} + +function test_halt_build_multi_tile() +{ + local pl = player_x(0) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] // FIXME: null instead of pax fails + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + // 2 adjacent tiles + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), road, true), null) + + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(2, 2, 0), station_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 2, 0), station_desc.get_name()), null) + + local halt = halt_x.get_halt(coord3d(2, 2, 0), pl) + ASSERT_TRUE(halt != null) + ASSERT_EQUAL(tile_x(2, 2, 0).get_halt().get_name(), tile_x(3, 2, 0).get_halt().get_name()) // check that this is really the same halt + ASSERT_EQUAL(halt.get_owner().get_name(), pl.get_name()) + ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.passenger), 0) // FIXME this should be 1 + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_FALSE(halt.accepts_good(good_desc_x.mail)) + + ASSERT_EQUAL(halt.get_arrived()[0], 0) + ASSERT_EQUAL(halt.get_departed()[0], 0) + ASSERT_EQUAL(halt.get_waiting()[0], 0) + ASSERT_EQUAL(halt.get_happy()[0], 0) + ASSERT_EQUAL(halt.get_unhappy()[0], 0) + ASSERT_EQUAL(halt.get_noroute()[0], 0) + ASSERT_EQUAL(halt.get_convoys()[0], 0) + ASSERT_EQUAL(halt.get_walked()[0], 0) + ASSERT_EQUAL(halt.get_convoy_list().get_count(), 0) + ASSERT_EQUAL(halt.get_line_list().get_count(), 0) + ASSERT_EQUAL(halt.get_factory_list().len(), 0) + + local tile_list = halt.get_tile_list() + local expected_tiles = [ tile_x(2, 2, 0), tile_x(3, 2, 0) ] + + foreach (t in tile_list) { + ASSERT_TRUE(t.find_object(mo_building) != null) + + // expected_tiles.find(t) != null won't work + ASSERT_TRUE(expected_tiles.filter(@(idx, val) t.x == val.x && t.y == val.y && t.z == val.z).len() == 1) + } + + ASSERT_EQUAL(halt.get_freight_to_dest(good_desc_x.passenger, coord(2, 2)), 0) + ASSERT_EQUAL(halt.get_freight_to_halt(good_desc_x.passenger, halt), 0) + + // this also depends on the separate_halt_capacities setting + ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), 64) + ASSERT_EQUAL(halt.get_capacity(good_desc_x.mail), 0) + ASSERT_EQUAL(halt.get_capacity(good_desc_x("Kohle")), 0) + ASSERT_EQUAL(halt.get_capacity(good_desc_x("nonexistent")), 0) + + ASSERT_EQUAL(halt.get_connections(good_desc_x.passenger).len(), 0) + ASSERT_EQUAL(halt.get_connections(good_desc_x.mail).len(), 0) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() + } + + // 2 tiles on top of each other + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), road, true), null) + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), bridge_desc.get_name()), null) + + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 0), station_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 1), station_desc.get_name()), null) + + local lower_halt = halt_x.get_halt(coord3d(3, 4, 0), pl) + local upper_halt = halt_x.get_halt(coord3d(3, 4, 1), pl) + + ASSERT_EQUAL(lower_halt.get_name(), upper_halt.get_name()) + ASSERT_EQUAL(lower_halt.get_tile_list().len(), 2) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 6, 0), "" + wt_road), null) + } + + RESET_ALL_PLAYER_FUNDS() + + local old_cash = pl.get_cash()[0] + ASSERT_EQUAL(old_cash, 200*1000) + + // 16x16 station + { + for (local x = 0; x < 16; ++x) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(x, 0, 0), coord3d(x, 15, 0), road, true), null) + for (local y = 0; y < 16; ++y) { + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(x, y, 0), station_desc.get_name()), null) + } + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 16*16*(road.get_maintenance() + station_desc.get_maintenance())) + ASSERT_EQUAL(pl.get_cash()[0]*100, old_cash*100 - 16*16*(road.get_cost() + station_desc.get_cost())) + + local halt = halt_x.get_halt(coord3d(0, 0, 0), pl) + ASSERT_TRUE(halt != null) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_FALSE(halt.accepts_good(good_desc_x.mail)) + ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.passenger), 0) // FIXME this should be 1 + ASSERT_EQUAL(halt.is_connected(halt, good_desc_x.mail), 0) + + for (local x = 0; x < 16; ++x) { + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(x, 0, 0), coord3d(x, 15, 0), "" + wt_road), null) + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_multi_mode() +{ + local pl = player_x(0) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local pax_stop = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + local pax_station = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 4, 0), road_desc, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 3, 0), coord3d(5, 4, 0), rail_desc, true), null) + + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_stop.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 4, 0), pax_station.get_name()), null) + + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 4, 0), pl).get_name(), halt_x.get_halt(coord3d(5, 4, 0), pl).get_name()) + + local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) + ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), 64) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 4, 0), coord3d(5, 3, 0), "" + wt_rail), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_multi_player() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(3, 4, 0), road_desc, true), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(public_pl, coord3d(3, 4, 0), pax_halt.get_name()), null) + + ASSERT_TRUE(halt_x.get_halt(coord3d(3, 4, 0), pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 4, 0), public_pl), null) + + local my_halt = halt_x.get_halt(coord3d(4, 4, 0), pl) + local pub_halt = halt_x.get_halt(coord3d(3, 4, 0), public_pl) + ASSERT_TRUE(my_halt != null) + ASSERT_TRUE(pub_halt != null) + + ASSERT_TRUE(my_halt.get_name() != pub_halt.get_name()) + ASSERT_EQUAL(my_halt.get_tile_list().len(), 1) + ASSERT_EQUAL(pub_halt.get_tile_list().len(), 1) + + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 4, 0), coord3d(3, 4, 0), "" + wt_road), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_separate() +{ + local pl = player_x(0) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 4, 0), coord3d(3, 4, 0), road_desc, true), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(5, 4, 0), pax_halt.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(3, 4, 0), pax_halt.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) + + local halt3 = halt_x.get_halt(coord3d(3, 4, 0), pl) + local halt4 = halt_x.get_halt(coord3d(4, 4, 0), pl) + local halt5 = halt_x.get_halt(coord3d(5, 4, 0), pl) + + ASSERT_TRUE(halt3 != null) + ASSERT_TRUE(halt4 != null) + ASSERT_TRUE(halt5 != null) + + ASSERT_TRUE(halt3.get_name() != halt5.get_name()) + ASSERT_TRUE(halt4.get_name() == halt5.get_name()) + ASSERT_TRUE(halt4.get_name() != halt3.get_name()) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 4, 0), coord3d(3, 4, 0), "" + wt_road), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_near_factory() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + local freight_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x("Kohle"))[0] + local remover = command_x(tool_remover) + + // build coal mine + coal power plant, then link them + ASSERT_EQUAL(build_factory(pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) + + { + // also depends on station catchment area size + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), road_desc, true), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) + + local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) + ASSERT_TRUE(halt != null) + ASSERT_EQUAL(halt.get_factory_list().len(), 1) + ASSERT_EQUAL(halt.get_factory_list()[0].get_name(), "Coal mine") + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) + } + + ASSERT_EQUAL(remover.work(public_pl, coord3d(0, 0, 0)), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_near_factories() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + local freight_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x("Kohle"))[0] + local remover = command_x(tool_remover) + + // build coal mine + coal power plant, then link them + ASSERT_EQUAL(build_factory(pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) + ASSERT_EQUAL(build_factory(pl, coord3d(6, 6, 0), 1, 1, 1024, "Kohlekraftwerk"), null) + ASSERT_EQUAL(command_x(tool_link_factory).work(pl, coord3d(0, 0, 0), coord3d(6, 6, 0), ""), null) + + { + // also depends on station catchment area size + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), road_desc, true), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(4, 4, 0), pax_halt.get_name()), null) + + local halt = halt_x.get_halt(coord3d(4, 4, 0), pl) + ASSERT_TRUE(halt != null) + ASSERT_EQUAL(halt.get_factory_list().len(), 2) + ASSERT_EQUAL(halt.get_factory_list()[0].get_name(), "Coal power station") + ASSERT_EQUAL(halt.get_factory_list()[1].get_name(), "Coal mine") + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 4, 0), coord3d(4, 3, 0), "" + wt_road), null) + } + + // this only works with catchment area size of 4 + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 7, 0), coord3d(1, 7, 0), road_desc, true), null) + ASSERT_EQUAL(command_x(tool_build_station).work(pl, coord3d(1, 7, 0), pax_halt.get_name()), null) + + local halt = halt_x.get_halt(coord3d(1, 7, 0), pl) + ASSERT_TRUE(halt != null) + ASSERT_EQUAL(halt.get_factory_list().len(), 0) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 7, 0), coord3d(1, 7, 0), "" + wt_road), null) + } + + + ASSERT_EQUAL(remover.work(public_pl, coord3d(0, 0, 0)), null) + ASSERT_EQUAL(remover.work(public_pl, coord3d(6, 6, 0)), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_on_tunnel_entrance() +{ + local pl = player_x(0) + local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] + + ASSERT_EQUAL(raise.work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(5, 3, 0)), null) + + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 1, 0), rail_tunnel.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 2, 0), rail_tunnel.get_name()), null) + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(5, 2, 0), rail_tunnel.get_name()), null) + + // Building station on tunnel entrance fails (contrary to depots) + { + for (local d = dir.north; d < dir.all; d = d*2) { + local p = coord3d(4, 2, 0) + dir.to_coord(d) + + ASSERT_EQUAL(command_x.build_station(pl, p, station_desc), "No suitable way on the ground!") + } + } + + local remover = command_x(tool_remover) + remover.set_flags(2) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) + + ASSERT_EQUAL(lower.work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(5, 3, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_on_bridge_end() +{ + local pl = player_x(0) + local rail_bridge = bridge_desc_x.get_available_bridges(wt_rail)[0] + local setslope = command_x(tool_setslope) + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] + + // north-south direction + { + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.north), null) + + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(4, 2, 0), rail_bridge.get_name()), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), station_desc), null) + + // tool_remover removes halt first, then bridge, then depot (depot is automatically removed when destroying bridge) + // So here we have to remove things on the tile twice (contrary to test_depot_build_on_bridge_end) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.flat), null) + } + + // east-west direction + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.east), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 0), "" + slope.west), null) + + ASSERT_EQUAL(command_x(tool_build_bridge).work(pl, coord3d(3, 3, 0), rail_bridge.get_name()), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(3, 3, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(5, 3, 0), get_depot_by_wt(wt_rail)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 0), "" + slope.flat), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_on_depot() +{ + local pl = player_x(0) + local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_rail, good_desc_x.passenger)[0] + local depot = get_depot_by_wt(wt_rail) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail_desc, true), null) + ASSERT_EQUAL(command_x.build_depot(pl, coord3d(4, 2, 0), depot), null) + + { + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 2, 0), station_desc), "No suitable ground!") + } + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_build_station_extension() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + local stationbuilder = command_x(tool_build_station) + local wayremover = command_x(tool_remove_way) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] // road because it has double slopes available + local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] // FIXME: null instead of pax fails + local stext_desc = building_desc_x.get_available_stations(building_desc_x.station_extension, wt_rail, good_desc_x.passenger)[0] + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + ASSERT_TRUE(station_desc != null) + ASSERT_TRUE(stext_desc != null) + ASSERT_TRUE(bridge_desc != null) + ASSERT_TRUE(road_desc != null) + + // build station extension without station: should fail + { + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), stext_desc.get_name()), "Post muss neben\nHaltestelle\nliegen!\n") + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_building), null) + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), station_desc.get_name()), null) + local old_maintenance = pl.get_current_maintenance() + + // build station extension next to station: should succeed + { + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 3, 0), stext_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + // and diagonal + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 2, 0), stext_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + } + + // build station extension on raised tile: should succeed + { + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 1), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 3, 2), "" + slope.all_up_slope), null) + + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 3, 3), stext_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 3, 3)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + // up slope is automatically removed + ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(1), null) + ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(2), null) + } + + { + // diagonal too + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 2, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(5, 2, 1), "" + slope.all_up_slope), null) + + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(5, 2, 2), stext_desc.get_name()), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 2, 2)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + // up slope is automatically removed + ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(1), null) + ASSERT_EQUAL(square_x(5, 3).get_tile_at_height(2), null) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_upgrade_downgrade() +{ + local pl = player_x(0) + local stationbuilder = command_x(tool_build_station) + local wayremover = command_x(tool_remove_way) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pax_halts = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger) + pax_halts.sort(@(a, b) a.get_capacity() <=> b.get_capacity()) + + ASSERT_TRUE(pax_halts.len() >= 2) + local small_halt = pax_halts[0] + local big_halt = pax_halts[1] + local pos = coord3d(3, 3, 0) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), road_desc, true), null) + + // upgrade station + { + ASSERT_EQUAL(stationbuilder.work(pl, pos, small_halt.get_name()), null) + local halt = halt_x.get_halt(pos, pl) + ASSERT_TRUE(halt != null) + local old_capacity = halt.get_capacity(good_desc_x.passenger) + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(stationbuilder.work(pl, pos, big_halt.get_name()), null) + + ASSERT_TRUE(halt.is_valid()) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_TRUE(halt.get_capacity(good_desc_x.passenger) > old_capacity) + ASSERT_EQUAL(halt.get_tile_list().len(), 1) + ASSERT_TRUE(pl.get_current_maintenance() > old_maint) + ASSERT_EQUAL(pl.get_current_maintenance() - old_maint, big_halt.get_maintenance() - small_halt.get_maintenance()) + } + + // upgrade station to same level + { + local halt = halt_x.get_halt(pos, pl) + local old_capacity = halt.get_capacity(good_desc_x.passenger) + local old_maint = pl.get_current_maintenance() + local old_cash = pl.get_current_cash() + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), big_halt.get_name()), null) + + ASSERT_TRUE(halt.is_valid()) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), old_capacity) + ASSERT_EQUAL(halt.get_tile_list().len(), 1) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // downgrade without ctrl, should fail + { + local halt = halt_x.get_halt(pos, pl) + local old_capacity = halt.get_capacity(good_desc_x.passenger) + local old_maint = pl.get_current_maintenance() + local old_cash = pl.get_current_cash() + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), small_halt.get_name()), "Upgrade must have\na higher level") + + ASSERT_TRUE(halt.is_valid()) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_EQUAL(halt.get_capacity(good_desc_x.passenger), old_capacity) + ASSERT_EQUAL(halt.get_tile_list().len(), 1) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // downgrade with ctrl + { + local halt = halt_x.get_halt(pos, pl) + local old_capacity = halt.get_capacity(good_desc_x.passenger) + local old_maint = pl.get_current_maintenance() + local old_cash = pl.get_current_cash() + + stationbuilder.set_flags(2) // ctrl + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(3, 3, 0), small_halt.get_name()), null) + + ASSERT_TRUE(halt.is_valid()) + ASSERT_TRUE(halt.accepts_good(good_desc_x.passenger)) + ASSERT_TRUE(halt.get_capacity(good_desc_x.passenger) < old_capacity) + ASSERT_EQUAL(halt.get_tile_list().len(), 1) + ASSERT_TRUE(pl.get_current_maintenance() < old_maint) + ASSERT_EQUAL(pl.get_current_maintenance() - old_maint, small_halt.get_maintenance() - big_halt.get_maintenance()) + } + + // clean up + ASSERT_EQUAL(wayremover.work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_make_public_single() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local stationbuilder = command_x(tool_build_station) + local wayremover = command_x(tool_remove_way) + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local makepublic = command_x(tool_make_stop_public) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) + + // invalid pos + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(-1, -1, -1)), "No suitable ground!") + + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // no stop under cursor + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(3, 2, 0)), null) + + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // valid stop - Making the stop public also makes the road under it public + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 1*pax_halt.get_maintenance() - 1*road_desc.get_maintenance()) + ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (1*pax_halt.get_maintenance() + 1*road_desc.get_maintenance()) ) + ASSERT_EQUAL(public_pl.get_current_maintenance(), 1*pax_halt.get_maintenance() + 1*road_desc.get_maintenance()) + } + + // already public - should have no effect + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + } + + // same for public player + { + local old_cash = public_pl.get_current_cash() + local old_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint) + ASSERT_EQUAL(public_pl.get_current_cash(), old_cash) + } + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) + + // If public player uses this tool, he pays for it + { + local old_cash = public_pl.get_current_cash() + local old_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + // only halt maintenance because the road is already public + ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * pax_halt.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + pax_halt.get_maintenance()) + } + + ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) + + // Public player can make halts of other players public even if road underneath is not public + { + local old_cash = public_pl.get_current_cash() + local old_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + // halt + road maintenance + ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * pax_halt.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + pax_halt.get_maintenance() + road_desc.get_maintenance()) + } + + ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_make_public_multi_tile() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local stationbuilder = command_x(tool_build_station) + local wayremover = command_x(tool_remove_way) + local pax_halt = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0] + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local makepublic = command_x(tool_make_stop_public) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 5, 0), road_desc, true), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), pax_halt), null) + + // valid stop - Making the stop public also makes the road under it public + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 2*pax_halt.get_maintenance() - 2*road_desc.get_maintenance()) + ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance()) ) + ASSERT_EQUAL(public_pl.get_current_maintenance(), 2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance()) + } + + // already public - should have no effect + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + } + + // same for public player + { + local old_cash = public_pl.get_current_cash() + local old_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint) + ASSERT_EQUAL(public_pl.get_current_cash(), old_cash) + } + + ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 5, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 5, 0), road_desc, true), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 3, 0), pax_halt), null) + ASSERT_EQUAL(command_x.build_station(pl, coord3d(4, 4, 0), pax_halt), null) + + // If public player uses this tool, he pays for it + { + local old_cash = public_pl.get_current_cash() + local old_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(public_pl.get_current_cash()*100, 100*old_cash + 60 * 2*pax_halt.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_maint + (2*pax_halt.get_maintenance() + 2*road_desc.get_maintenance())) + } + + ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 5, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_halt_make_public_underground() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local tunnel_desc = tunnel_desc_x.get_available_tunnels(wt_road)[0] + local pax_halt = building_desc_x("LongBusStop") + local makepublic = command_x(tool_make_stop_public) + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local stationbuilder = command_x(tool_build_station) + + ASSERT_EQUAL(raise.work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(5, 4, 0)), null) + + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 2, 0), tunnel_desc.get_name()), null) + ASSERT_EQUAL(stationbuilder.work(pl, coord3d(4, 3, 0), pax_halt.get_name()), null) + + // valid underground stop + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + ASSERT_TRUE(halt_x.get_halt(coord3d(4, 3, 0), public_pl) != null) + ASSERT_EQUAL(halt_x.get_halt(coord3d(4, 3, 0), public_pl).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint - 1*pax_halt.get_maintenance() - 1*tunnel_desc.get_maintenance()) + ASSERT_EQUAL(pl.get_current_cash()*100, old_cash*100 - 60 * (1*pax_halt.get_maintenance() + 1*tunnel_desc.get_maintenance()) ) + ASSERT_EQUAL(public_pl.get_current_maintenance(), 1*pax_halt.get_maintenance() + 1*tunnel_desc.get_maintenance()) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + + ASSERT_EQUAL(lower.work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(5, 4, 0)), null) +} diff --git a/tests/tests/test_headquarters.nut b/tests/tests/test_headquarters.nut new file mode 100644 index 00000000000..7aebd2b3b07 --- /dev/null +++ b/tests/tests/test_headquarters.nut @@ -0,0 +1,76 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Test building/upgrading/removing headquarters +// + + +function get_hq_by_level(level) +{ + local list = building_desc_x.get_building_list(building_desc_x.headquarter) + list.sort(@(a, b) a.get_headquarter_level() <=> b.get_headquarter_level()) + return list[level] +} + + +function test_headquarters_build_flat() +{ + local pl = player_x(0) + local public_pl = player_x(1) + + local hq_builder = command_x(tool_headquarter) + local pos1 = coord3d(2, 3, 0) + local pos2 = coord3d(4, 1, 0) + local pos1_2d = coord(2, 3) + local pos2_2d = coord(4, 1) + + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) + + // place normal + { + ASSERT_EQUAL(hq_builder.work(pl, pos1, get_hq_by_level(0).get_name()), null) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) + } + + // replace my existing HQ + { + ASSERT_EQUAL(hq_builder.work(pl, pos1, get_hq_by_level(1).get_name()), null) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) + } + + { + // cannot downgrade headquarters FIXME this depends on whether timeline is enabled ?! + ASSERT_EQUAL(hq_builder.work(pl, pos2, get_hq_by_level(0).get_name()), "Insufficient funds!") + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) + + pl.book_cash(1*1000*1000*100) + ASSERT_EQUAL(hq_builder.work(pl, pos2, get_hq_by_level(0).get_name()), null) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) + } + + // cannot replace other people's headquarters (not even public player!) + { + ASSERT_EQUAL(hq_builder.work(public_pl, pos2, get_hq_by_level(1).get_name()), "No suitable ground!") + ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) + + ASSERT_EQUAL(hq_builder.work(public_pl, pos1, get_hq_by_level(1).get_name()), null) + ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), pos1_2d.tostring()) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), pos2_2d.tostring()) + } + + // remove hq + local remover = command_x(tool_remover) + ASSERT_EQUAL(remover.work(pl, tile_x(2, 3, 0)), "Der Besitzer erlaubt das Entfernen nicht") // cannot remove HQ of other players + ASSERT_EQUAL(remover.work(public_pl, tile_x(2, 3, 0)), null) + ASSERT_EQUAL(remover.work(public_pl, tile_x(4, 1, 0)), null) + + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) + ASSERT_EQUAL(public_pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) + + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_label.nut b/tests/tests/test_label.nut new file mode 100644 index 00000000000..dade6ab1911 --- /dev/null +++ b/tests/tests/test_label.nut @@ -0,0 +1,64 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for labels +// + + +function test_label() +{ + local remover = command_x(tool_remover) + local pl = player_x(0) + local public_pl = player_x(1) + + // build label on ground + { + ASSERT_EQUAL(label_x.create(coord3d(4, 1, 0), pl, "Foo2"), null) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0)), null) + } + + // Build label on invalid ground, same as building on map ground + { + ASSERT_EQUAL(label_x.create(coord3d(3, 1, -2), pl, "Foo1"), null) + + local label = tile_x(3, 1, 0).find_object(mo_label) + ASSERT_TRUE(label != null) + ASSERT_TRUE(label.is_valid()) + + ASSERT_EQUAL(label.get_owner().get_name(), pl.get_name()) // TODO More secure way to check for player equality + ASSERT_EQUAL(label.get_waytype(), wt_invalid) + + ASSERT_EQUAL(label.get_text(), "Foo1") + ASSERT_EQUAL(label.set_text("Bar"), true) + ASSERT_EQUAL(label.get_text(), "Bar") + + ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0)), null) + ASSERT_FALSE(label.is_valid()) + } + + // build label on existing label, should fail + { + ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo3"), null) + ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo4"), "There is already a marker here.\n") + ASSERT_EQUAL(remover.work(pl, coord3d(5, 1, 0)), null) + } + + // players cannot remove labels of other players (except public player) + { + ASSERT_EQUAL(label_x.create(coord3d(5, 1, 0), pl, "Foo1"), null) + ASSERT_EQUAL(label_x.create(coord3d(5, 2, 0), public_pl, "Foo2"), null) + + ASSERT_EQUAL(remover.work(pl, coord3d(5, 2, 0)), "Der Besitzer erlaubt das Entfernen nicht") + ASSERT_TRUE(tile_x(5, 2, 0).find_object(mo_label) != null) + + ASSERT_EQUAL(remover.work(public_pl, coord3d(5, 2, 0)), null) + ASSERT_EQUAL(remover.work(public_pl, coord3d(5, 1, 0)), null) + } + + // clean up + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_player.nut b/tests/tests/test_player.nut new file mode 100644 index 00000000000..8e6453899cd --- /dev/null +++ b/tests/tests/test_player.nut @@ -0,0 +1,131 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for player_x stuff +// + + +function test_player_cash() +{ + local pl = player_x(0) + + ASSERT_EQUAL(pl.get_current_cash(), 200000) // get_current_cash is in credits (returns float) + ASSERT_EQUAL(pl.get_current_net_wealth(), 200000*100) // get_current_net_wealth is in 1/100 credits + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + local ok = pl.book_cash(0) + ASSERT_EQUAL(ok, true) + ASSERT_EQUAL(pl.get_current_cash(), 200000) + ASSERT_EQUAL(pl.get_current_net_wealth(), 200000 * 100) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ok = pl.book_cash(-200000 * 100) // book_cash is in 1/100 credits + ASSERT_EQUAL(ok, true) + ASSERT_EQUAL(pl.get_current_cash(), 0) + ASSERT_EQUAL(pl.get_current_net_wealth(), 0) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ok = pl.book_cash(-1) + ASSERT_EQUAL(ok, true) + ASSERT_EQUAL(pl.get_current_cash(), -0.01) + ASSERT_EQUAL(pl.get_current_net_wealth(), -1) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // clean up + ok = pl.book_cash(200000 * 100 + 1) + ASSERT_EQUAL(ok, true) + ASSERT_EQUAL(pl.get_current_cash(), 200000) + ASSERT_EQUAL(pl.get_current_net_wealth(), 200000 * 100) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_player_isactive() +{ + local pl = player_x(0) + ASSERT_EQUAL(pl.is_valid(), true) + ASSERT_EQUAL(pl.is_active(), true) + + local public_pl = player_x(1) + ASSERT_EQUAL(public_pl.is_valid(), true) + ASSERT_EQUAL(public_pl.is_active(), true) + + local nonexist = player_x(2) + ASSERT_EQUAL(nonexist.is_valid(), false) + ASSERT_EQUAL(nonexist.is_active(), false) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_player_headquarters() +{ + local pl = player_x(0) + ASSERT_EQUAL(pl.get_headquarter_level(), 0) + ASSERT_EQUAL(pl.get_headquarter_pos().tostring(), coord(-1, -1).tostring()) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_player_name() +{ + local pl = player_x(0) + + ASSERT_EQUAL(pl.get_name(), "Default player") + ASSERT_TRUE(pl.set_name("Foo")) + ASSERT_EQUAL(pl.get_name(), "Foo") + ASSERT_TRUE(pl.set_name("Foo")) + ASSERT_EQUAL(pl.get_name(), "Foo") + + // clean up + ASSERT_TRUE(pl.set_name("Default player")) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_player_lines() +{ + local pl = player_x(0) + + // empty line list + { + local line_list = pl.get_line_list() + ASSERT_TRUE(line_list != null) + ASSERT_EQUAL(line_list.get_count(), 0) + } + + // non-empty line list + { + ASSERT_TRUE(pl.create_line(wt_road)) + + local line_list = pl.get_line_list() + ASSERT_TRUE(line_list != null) + ASSERT_EQUAL(line_list.get_count(), 1) + + foreach (line in line_list) { + ASSERT_TRUE(line != null) + ASSERT_EQUAL(line.is_valid(), true) + ASSERT_EQUAL(line.get_waytype(), wt_road) + } + + foreach (line in line_list) { + ASSERT_TRUE(line.destroy(pl)) + } + + ASSERT_EQUAL(pl.get_line_list().get_count(), 0) + } + + // TODO Destroy lines owned by other players + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + diff --git a/tests/tests/test_powerline.nut b/tests/tests/test_powerline.nut new file mode 100644 index 00000000000..22ead2b3283 --- /dev/null +++ b/tests/tests/test_powerline.nut @@ -0,0 +1,544 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for powerline stuff and transformers +// + + +function test_powerline_connect() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + local wayremover = command_x(tool_remove_way) + + ASSERT_TRUE(powerline != null) + + local center = coord3d(3, 4, 0) + local north = center + dir.to_coord(dir.north) + local east = center + dir.to_coord(dir.east) + local south = center + dir.to_coord(dir.south) + local west = center + dir.to_coord(dir.west) + + // preconditions + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + { + ASSERT_EQUAL(command_x.build_way(pl, center, south, powerline, true), null) + ASSERT_EQUAL(wayremover.work(pl, south, south, "" + wt_power), null) + + ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(center.x, center.y, center.z))) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "...0....", + "........", + "........", + "........" + ]) + } + + local maint_per_tile = pl.get_current_maintenance() + ASSERT_TRUE(maint_per_tile > 0) + + { + ASSERT_EQUAL(command_x.build_way(pl, center, north, powerline, true), null) + + ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(north.x, north.y, north.z))) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...1....", + "........", + "........", + "........" + ]) + ASSERT_EQUAL(pl.get_current_maintenance(), 2*maint_per_tile) + } + + + { + ASSERT_EQUAL(command_x.build_way(pl, center, east, powerline, true), null) + + ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(east.x, east.y, east.z))) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...38...", + "........", + "........", + "........" + ]) + ASSERT_EQUAL(pl.get_current_maintenance(), 3*maint_per_tile) + } + + { + ASSERT_EQUAL(command_x.build_way(pl, center, south, powerline, true), null) + + ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(south.x, south.y, south.z))) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...78...", + "...1....", + "........", + "........" + ]) + ASSERT_EQUAL(pl.get_current_maintenance(), 4*maint_per_tile) + } + + { + ASSERT_EQUAL(command_x.build_way(pl, center, west, powerline, true), null) + + ASSERT_TRUE(powerline_x(center.x, center.y, center.z).is_connected(powerline_x(west.x, west.y, west.z))) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "..2F8...", + "...1....", + "........", + "........" + ]) + ASSERT_EQUAL(pl.get_current_maintenance(), 5*maint_per_tile) + } + + // clean up + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_power), null) + ASSERT_EQUAL(wayremover.work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_power), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_powerline_bridge() +{ + local pl = player_x(0) + local power_bridge = bridge_desc_x.get_available_bridges(wt_power)[0] + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + + // build normal powerline below power bridge, should fail + { + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), power_bridge), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), powerline, true), "") + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_power), null) + } + + RESET_ALL_PLAYER_FUNDS() + + // build flat powerline bridge + { + // Note: Build way first, then bridge (see above for why) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), power_bridge), null) + + local bridge_part = powerline_x(3, 3, 1) + local ground_part = powerline_x(3, 3, 0) + ASSERT_TRUE(bridge_part != null) + ASSERT_TRUE(ground_part != null) + + ASSERT_FALSE(bridge_part.is_connected(ground_part)) + ASSERT_FALSE(ground_part.is_connected(bridge_part)) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_power), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), "" + wt_power), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_powerline_build_transformer() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local build_trafo = command_x(tool_build_transformer) + local setclimate = command_x(tool_set_climate) + local setslope = command_x(tool_setslope) + local power_tunnel = tunnel_desc_x.get_available_tunnels(wt_power)[0] + + // preconditions + ASSERT_TRUE(power_tunnel != null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // build coal mine + coal power plant, then link them + ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, 1024, "Kohlegrube"), null) + ASSERT_EQUAL(build_factory(public_pl, coord3d(6, 6, 0), 1, 1, 1024, "Kohlekraftwerk"), null) + command_x(tool_link_factory).work(public_pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "") + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + + { + // build transformer not adjacent to a factory + ASSERT_EQUAL(build_trafo.work(pl, coord3d(0, 7, 0)), "Transformer only next to factory!") + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // diagonally adjacent to factory, not allowed + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 3, 0)), "Transformer only next to factory!") + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + { + // cannot build transformer on water even if adjacent to factory + ASSERT_EQUAL(setclimate.work(pl, coord3d(3, 2, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only on flat bare land!") + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ASSERT_EQUAL(setclimate.work(pl, coord3d(3, 2, 0), coord3d(3, 2, 0), "" + cl_mediterran), null) + } + + { + // build transformer in the air, same as building on map ground + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 1)), null) + ASSERT_TRUE(pl.get_current_maintenance() > 0) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + { + // cannot build transformer on sloped tile + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.north), null) + + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only on flat bare land!") + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 1)), "Transformer only on flat bare land!") + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + { + // Cannot build transformer underground adjacent to factory + // Depends also on allow_underground_transformers setting + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only next to factory!") + // 2 height levels + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 1), "" + slope.all_up_slope), null) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), "Transformer only next to factory!") + + // However, building on flat ground on a different height level works + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 2)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 2, 2)), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 2), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + { + // Build transformer underground below mine (senke_t) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(2, 2, -1)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // And below power plant (pumpe_t) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(6, 6, -1)), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(6, 6, -1)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + { + // build underground transformer on underground powerline, should fail + local lower = command_x(tool_lower_land) + local raise = command_x(tool_raise_land) + local digger = command_x(tool_build_tunnel) + digger.set_flags(2) # Ctrl + + ASSERT_EQUAL(lower.work(pl, coord3d(2, 4, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(3, 4, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ASSERT_EQUAL(digger.work(pl, coord3d(2, 3, -1), power_tunnel.get_name()), null) + ASSERT_EQUAL(digger.work(pl, coord3d(2, 3, -1), coord3d(2, 2, -1), power_tunnel.get_name()), null) + + // test + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(build_trafo.work(pl, coord3d(2, 2, -1)), "Tile not empty.") + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), null) + + // and clean up + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, -1)), "") // underground ??? + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 3, -1)), null) // above ground ??? + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ASSERT_EQUAL(raise.work(pl, coord3d(2, 4, -1)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(3, 4, -1)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(7, 7, 0)), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_powerline_build_over_transformer() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local build_trafo = command_x(tool_build_transformer) + local production = 1024 // 1/s + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + + ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) + + { + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 1, 0)), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 0, 0), coord3d(3, 2, 0), powerline, true), null) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 0, 0), coord3d(3, 2, 0), "" + wt_power), null) + + ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_transformer_s), null) + ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_transformer_c), null) + } + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_powerline_build_transformer_multiple() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local build_trafo = command_x(tool_build_transformer) + local production = 1024 // 1/s + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + + // build coal mine + coal power plant + ASSERT_EQUAL(build_factory(public_pl, coord3d(0, 0, 0), 1, 1, production, "Kohlegrube"), null) + ASSERT_EQUAL(build_factory(public_pl, coord3d(3, 0, 0), 1, 1, production, "Kohlekraftwerk"), null) + + // single transformer adjacent to multiple factories + { + ASSERT_EQUAL(build_trafo.work(pl, coord3d(3, 2, 0)), null) + ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_transformer_s) != null) + ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_transformer_c) == null) + + local trafo = transformer_x(3, 2, 0) + local pp = factory_x(3, 0) + local mine = factory_x(0, 0) + ASSERT_EQUAL(trafo.get_factory().get_name(), pp.get_name()) + ASSERT_TRUE(pp.is_transformer_connected()) + ASSERT_FALSE(mine.is_transformer_connected()) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 2, 0)), null) + } + + // multiple transformers next to single factory + { + ASSERT_EQUAL(build_trafo.work(pl, coord3d(0, 3, 0)), null) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(1, 3, 0)), "Only one transformer per factory!") + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(0, 3, 0)), null) + } + + // same as before, but for electricity producers + { + ASSERT_EQUAL(build_trafo.work(pl, coord3d(5, 1, 0)), null) + ASSERT_EQUAL(build_trafo.work(pl, coord3d(5, 0, 0)), "Only one transformer per factory!") + } + + // remove factory, should remove transformer and disconnect power nets + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(6, 0, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(6, 2, 0), powerline, true), null) + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 0, 0)), null) + + ASSERT_EQUAL(tile_x(5, 1, 0).find_object(mo_transformer_s), null) + ASSERT_EQUAL(tile_x(5, 1, 0).find_object(mo_transformer_c), null) + + ASSERT_FALSE(powerline_x(5, 0, 0).is_connected(powerline_x(5, 2, 0))) + + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(5, 0, 0), coord3d(6, 0, 0), "" + wt_power), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(5, 2, 0), coord3d(6, 2, 0), "" + wt_power), null) + } + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(0, 0, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_powerline_ways() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road, true), null) + + { + // road-powerline crossing + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), powerline, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + "..1.....", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + ".2A8....", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_power), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + "..1.....", + "........", + "........", + "........", + "........" + ]) + } + + { + // build powerline ending on road + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(2, 2, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // build powerline over end of road + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(2, 3, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // build powerline in the same direction as way below + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 0, 0), coord3d(2, 4, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // remove straight way, add diagonal way + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 0, 0), coord3d(0, 7, 0), road, true), null) + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 4, 0), coord3d(6, 4, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + // and the other direction + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 1, 0), coord3d(4, 6, 0), powerline, true), "") + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(0, 7, 0), coord3d(7, 0, 0), "" + wt_road), null) + + // clean up + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_reservation.nut b/tests/tests/test_reservation.nut new file mode 100644 index 00000000000..5265b22c068 --- /dev/null +++ b/tests/tests/test_reservation.nut @@ -0,0 +1,70 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests convoi reservation +// + + +function test_reservation_clear_ground() +{ + local clear_reservation = command_x(tool_clear_reservation) + local pl = player_x(0) + + // invalid coord + { + ASSERT_EQUAL(clear_reservation.work(pl, coord3d(-1, -1, 0)), null) + } + + // flat ground + { + ASSERT_EQUAL(clear_reservation.work(pl, coord3d(0, 0, 0)), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_reservation_clear_road() +{ + local clear_reservation = command_x(tool_clear_reservation) + local pl = player_x(0) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road, true), null) + + { + ASSERT_EQUAL(clear_reservation.work(pl, coord3d(4, 3, 0)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_reservation_clear_rail() +{ + local clear_reservation = command_x(tool_clear_reservation) + local pl = player_x(0) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), rail, true), null) + + { + ASSERT_EQUAL(clear_reservation.work(pl, coord3d(4, 3, 0)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_reservation_clear_rail_occupied() +{ + // TODO +} diff --git a/tests/tests/test_scenario.nut b/tests/tests/test_scenario.nut new file mode 100644 index 00000000000..3ab784a9e02 --- /dev/null +++ b/tests/tests/test_scenario.nut @@ -0,0 +1,317 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for scenario rules/conditions +// + +function test_scenario_rules_allow_forbid_tool() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local pl = player_x(0) + + { + rules.forbid_tool(0, tool_raise_land) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 2, 0)), null) // FIXME this should fail + ASSERT_EQUAL(lower.work(pl, coord3d(4, 2, 1)), null) + } + + // clean up + rules.allow_tool(player_all, tool_raise_land) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_scenario_rules_allow_forbid_way_tool_rect() +{ + local waybuilder = command_x(tool_build_way) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local pl = player_x(0) + + rules.forbid_way_tool_rect(0, tool_build_way, wt_road, coord(2, 2), coord(5, 5), "Foo Bar") + + // Fully in forbiden zone + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(5, 5, 0), road, true), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // Ending in forbidden zone + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(2, 2, 0), road, true), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // Starting in forbidden zone + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // make sure we can build other ways + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), rail, true), null) + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "........", + "........", + "2A8.....", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), "" + wt_rail), null) + } + + // clean up + rules.clear() + RESET_ALL_PLAYER_FUNDS() +} + + +function test_scenario_rules_allow_forbid_way_tool_cube() +{ + local waybuilder = command_x(tool_build_way) + local setslope = command_x(tool_setslope) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local pl = player_x(0) + + rules.forbid_way_tool_cube(0, tool_build_way, wt_road, coord3d(2, 2, 1), coord3d(5, 5, 2), "Foo Bar") + + // build below + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "2A8.....", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), "" + wt_road), null) + } + + // build into forbidden zone + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 4, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.north), null) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 0, 0), coord3d(3, 4, 1), road, true), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 4, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.flat), null) + } + + rules.clear() + rules.forbid_way_tool_cube(0, tool_build_way, wt_road, coord3d(0, 0, 1), coord3d(0, 0, 1), "Foo Bar") + + // build double height slope through forbidden cube + { + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "" + (2*slope.east)), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(1, 1, 0), road, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + ".28.....", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(1, 1, 0), "" + wt_road), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "" + slope.flat), null) + } + + // clean up + rules.clear() + RESET_ALL_PLAYER_FUNDS() +} + + +function test_scenario_rules_allow_forbid_tool_stacked_rect() +{ + local pl = player_x(0) + local waybuilder = command_x(tool_build_way) + local setslope = command_x(tool_setslope) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + rules.forbid_way_tool_rect(0, tool_build_way, wt_road, coord(1, 1), coord(14, 14), "Foo Bar 1") + + // build in outer allowed ring, near map border + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), road_desc, false), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + // build in outer forbidden ring + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(1, 5, 0), road_desc, false), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + rules.allow_way_tool_rect(0, tool_build_way, wt_road, coord(2, 2), coord(13, 13)) + + // try building in allowed ring, does not work because rules cannot be stacked + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), road_desc, false), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + rules.clear() + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_scenario_rules_allow_forbid_tool_stacked_cube() +{ + local pl = player_x(0) + local waybuilder = command_x(tool_build_way) + local setslope = command_x(tool_setslope) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + rules.forbid_way_tool_cube(0, tool_build_way, wt_road, coord3d(1, 1, 0), coord3d(14, 14, 0), "Foo Bar 1") + + // build in outer allowed ring, near map border + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), road_desc, false), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + // build in outer forbidden ring + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(1, 5, 0), road_desc, false), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + rules.allow_way_tool_cube(0, tool_build_way, wt_road, coord3d(2, 2, 0), coord3d(13, 13, 0)) + + // try building in allowed ring, does not work because rules cannot be stacked + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 2, 0), coord3d(2, 5, 0), road_desc, false), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "6AAAA8..", + "5.......", + "5.......", + "5.......", + "5.......", + "1.......", + "........", + "........" + ]) + } + + rules.clear() + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(5, 0, 0), coord3d(0, 5, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_sign.nut b/tests/tests/test_sign.nut new file mode 100644 index 00000000000..7fee094a718 --- /dev/null +++ b/tests/tests/test_sign.nut @@ -0,0 +1,941 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for signs/signals +// + + +function test_sign_build_oneway() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + local remover = command_x(tool_remover) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local sign = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_one_way())[0] + + ASSERT_TRUE(road != null) + ASSERT_TRUE(sign != null) + ASSERT_TRUE(sign.is_one_way()) + + // on ground without way + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + // in the air + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 1), sign), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + // build way + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) + + { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) + + local s = tile_x(2, 3, 0).find_object(mo_roadsign) + ASSERT_TRUE(s != null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_TRUE(s.can_pass(public_pl)) + + local w = tile_x(2, 3, 0).find_object(mo_way) + ASSERT_TRUE(w != null) + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.south) + + // change direction of sign + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_TRUE(s.can_pass(public_pl)) + + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.north) + + // change direction again, should have original direction + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_TRUE(s.can_pass(public_pl)) + + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.south) + } + + // build road/rail crossing over sign, should fail if straight + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), rail, true), "") + ASSERT_FALSE(tile_x(2, 3, 0).find_object(mo_way).is_crossing()) + ASSERT_EQUAL(tile_x(1, 3, 0).find_object(mo_way), null) + ASSERT_EQUAL(tile_x(3, 3, 0).find_object(mo_way), null) + } + + // build road/road crossing over sign, should succeed + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), road, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + local w = tile_x(2, 3, 0).find_object(mo_way) + ASSERT_FALSE(w.is_crossing()) + ASSERT_EQUAL(w.get_dirs_masked(), dir.southeastwest) + + // change direction of sign on road crossing, should fail + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + + // remove sign, try to build again (should fail because of crossing) + { + ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0)), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) + + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) + + // Cannot remove signs of other players (except public player) + { + ASSERT_EQUAL(command_x.build_sign_at(public_pl, coord3d(2, 2, 0), sign), null) + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(2, 2, 0)), "Der Besitzer erlaubt das Entfernen nicht") + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(2, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(2, 3, 0)), null) + } + + // remove stuff + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_build_trafficlight() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + local remover = command_x(tool_remover) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local trafficlight = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_traffic_light())[0] + + ASSERT_TRUE(trafficlight != null) + + // on ground + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) + + // end of way + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 1, 0), trafficlight), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + // 2-way + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(2, 3, 0), road, true), null) + + // 3-way + { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), trafficlight), null) + + local tl = tile_x(2, 3, 0).find_object(mo_roadsign) + ASSERT_TRUE(tl != null) + ASSERT_TRUE(tl.can_pass(pl)) + ASSERT_TRUE(tl.can_pass(public_pl)) + ASSERT_TRUE(tl.get_desc().is_traffic_light()) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2D.....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(tile_x(2, 3, 0).get_way_dirs_masked(wt_road), dir.northsouthwest) + } + + + // Make 4-way from 3-way + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(3, 3, 0), road, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(tile_x(2, 3, 0).get_way_dirs_masked(wt_road), dir.all) + } + + // remove traffic light on crossing via wayremover + { + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_build_private_way() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + local remover = command_x(tool_remover) + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local sign = sign_desc_x.get_available_signs(wt_road).filter(@(idx, sign) sign.is_private_way())[0] + + ASSERT_TRUE(road != null) + ASSERT_TRUE(sign != null) + ASSERT_TRUE(sign.is_private_way()) + + // on ground + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + // in the air + { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 1), sign), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + } + + // build way + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), road, true), null) + + { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) + + local s = tile_x(2, 3, 0).find_object(mo_roadsign) + ASSERT_TRUE(s != null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_FALSE(s.can_pass(public_pl)) + + local w = tile_x(2, 3, 0).find_object(mo_way) + ASSERT_TRUE(w != null) + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) + + // change direction of sign + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_FALSE(s.can_pass(public_pl)) + + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) + + // change direction again, should have original direction + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + ASSERT_TRUE(s.is_valid()) + ASSERT_TRUE(s.can_pass(pl)) + ASSERT_FALSE(s.can_pass(public_pl)) + + ASSERT_EQUAL(w.get_dirs(), dir.northsouth) + ASSERT_EQUAL(w.get_dirs_masked(), dir.northsouth) + } + + // build road/rail crossing over sign, should fail if straight + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), rail, true), "") + ASSERT_FALSE(tile_x(2, 3, 0).find_object(mo_way).is_crossing()) + ASSERT_EQUAL(tile_x(1, 3, 0).find_object(mo_way), null) + ASSERT_EQUAL(tile_x(3, 3, 0).find_object(mo_way), null) + } + + // build road/road crossing over sign, should succeed + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), road, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + local w = tile_x(2, 3, 0).find_object(mo_way) + ASSERT_FALSE(w.is_crossing()) + ASSERT_EQUAL(w.get_dirs_masked(), dir.all) + + // change direction of sign on road crossing, should fail + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + + // remove sign, try to build again (should fail because of crossing) + { + ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0)), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_roadsign), null) + + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), sign), null) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + } + + // remove stuff + ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 3, 0), coord3d(3, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 1, 0), coord3d(2, 4, 0), "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_build_signal() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + local remover = command_x(tool_remover) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] + local presignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_pre_signal())[0] + local priosignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_priority_signal())[0] + local lbsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_longblock_signal())[0] + local chsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_choose_sign())[0] + local eocsignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_end_choose_signal())[0] + + local all_signals = [ signal, presignal, priosignal, lbsignal, chsignal, eocsignal ] + + foreach (s in all_signals) { + ASSERT_TRUE(s != null) + } + + { + // Build signs on flat ground, should fail + foreach (s in all_signals) { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), s), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) + } + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 0, 0), coord3d(2, 7, 0), road, true), null) + + { + // Rail signals on road, should fail + foreach (s in all_signals) { + local error_caught = false + try { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(2, 3, 0), s), "") + } + catch (e) { + error_caught = true + ASSERT_EQUAL(e, "Tool has no effects") + } + ASSERT_TRUE(error_caught) + ASSERT_EQUAL(tile_x(2, 3, 0).find_object(mo_signal), null) + } + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(2, 0, 0), coord3d(2, 7, 0), "" + wt_road), null) + + + // build signals + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "........", + "........" + ]) + } + + // make signals directional + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "288.....", + "288.....", + "288.....", + "288.....", + "288.....", + "2A8.....", + "........", + "........" + ]) + } + + // signals into different direction + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(2, i, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(1, i, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(1, i, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "2A8.....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "228.....", + "228.....", + "228.....", + "228.....", + "228.....", + "2A8.....", + "........", + "........" + ]) + } + + // build way across signal + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 0, 0), coord3d(1, all_signals.len()-1, 0), rail, true), null) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2E8.....", + "2F8.....", + "2F8.....", + "2F8.....", + "2F8.....", + "2B8.....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "268.....", + "278.....", + "278.....", + "278.....", + "278.....", + "2B8.....", + "........", + "........" + ]) + } + + // remove everything + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(wayremover.work(pl, coord3d(0, i, 0), coord3d(2, i, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(1, 0, 0), coord3d(1, all_signals.len()-1, 0), "" + wt_rail), null) + } + + // build signals, dir 2 + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(i, 0, 0), coord3d(i, 2, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "555555..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "555555..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // make signals directional, dir 2 + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "555555..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "444445..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // signals into different direction, dir 2 + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(command_x.build_sign_at(pl, coord3d(i, 1, 0), s), null) + + if (s == eocsignal) { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_roadsign) != null) // for end-of-choose + } + else { + ASSERT_TRUE(tile_x(i, 1, 0).find_object(mo_signal) != null) + } + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "555555..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "111115..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // build way across signal + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 1, 0), coord3d(all_signals.len()-1, 1, 0), rail, true), null) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "7FFFFD..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "444444..", + "3BBBBD..", + "111111..", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // remove everything again + { + foreach (i, s in all_signals) { + ASSERT_EQUAL(wayremover.work(pl, coord3d(i, 2, 0), coord3d(i, 0, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(wayremover.work(pl, coord3d(0, 1, 0), coord3d(all_signals.len()-1, 1, 0), "" + wt_rail), null) + } + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_build_signal_multiple() +{ + local pl = player_x(0) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] + + // starts on way end, not possible + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), rail, true), null) + + ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), signal.get_name()), + "Hier kann kein\nSignal aufge-\nstellt werden!\n") + + for (local x = 2; x <= 7; ++x) { + ASSERT_EQUAL(tile_x(x, 2, 0).find_object(mo_signal), null) + } + + ASSERT_WAY_PATTERN_MASKED(wt_rail, coord3d(0, 0, 0), + [ + "........", + "........", + "2AAAAAA8", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), "" + wt_rail), null) + } + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), rail, true), null) + + // FIXME click-and-drag for tools is not supported + local error_caught = false + try { + command_x(tool_build_roadsign).work(pl, coord3d(1, 2, 0), coord3d(6, 2, 0), signal.get_name()) + } + catch (e) { + ASSERT_EQUAL(e, "First click has side effects") + error_caught = true + } + ASSERT_EQUAL(error_caught, true) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(0, 2, 0), coord3d(7, 2, 0), "" + wt_rail), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_replace_signal() +{ + local pl = player_x(0) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] + local presignal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_pre_signal())[0] + + // precondistions + ASSERT_TRUE(signal != null) + ASSERT_TRUE(presignal != null) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(6, 3, 0), rail, true), null) + + // try replacing one-directional signals + { + ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), signal.get_name()), null) + + local error_caught = false + try { + command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), presignal.get_name()) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + + local s = sign_x(5, 3, 0) + ASSERT_TRUE(s != null) + ASSERT_EQUAL(s.get_desc().get_name(), signal.get_name()) + } + + // and one-directional signals + { + ASSERT_EQUAL(command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), signal.get_name()), null) + + local error_caught = false + try { + command_x(tool_build_roadsign).work(pl, coord3d(5, 3, 0), presignal.get_name()) + } + catch (e) { + ASSERT_EQUAL(e, "Tool has no effects") + error_caught = true + } + ASSERT_TRUE(error_caught) + + local s = sign_x(5, 3, 0) + ASSERT_TRUE(s != null) + ASSERT_EQUAL(s.get_desc().get_name(), signal.get_name()) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(6, 3, 0), coord3d(4, 3, 0), "" + wt_rail), null) + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_sign_signal_when_player_removed() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local signal = sign_desc_x.get_available_signs(wt_rail).filter(@(idx, sign) sign.is_signal())[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(6, 3, 0), rail, true), null) + ASSERT_EQUAL(command_x(tool_build_roadsign).work(public_pl, coord3d(5, 3, 0), signal.get_name()), null) + + { + world.remove_player(pl) + ASSERT_EQUAL(tile_x(5, 3, 0).find_object(mo_way), null) + ASSERT_TRUE(tile_x(5, 3, 0).find_object(mo_signal) != null) + } + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(5, 3, 0)), null) + ASSERT_TRUE(tile_x(5, 3, 0).find_object(mo_signal) == null) + + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_slope.nut b/tests/tests/test_slope.nut new file mode 100644 index 00000000000..3b3351cc5ee --- /dev/null +++ b/tests/tests/test_slope.nut @@ -0,0 +1,191 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + +// +// Test set_/can_set_slope +// + +function test_slope_to_dir() +{ + local slope_to_dir = {} + + slope_to_dir[slope.north] <- dir.south + slope_to_dir[slope.east ] <- dir.west + slope_to_dir[slope.south] <- dir.north + slope_to_dir[slope.west ] <- dir.east + + slope_to_dir[2*slope.north] <- dir.south + slope_to_dir[2*slope.east ] <- dir.west + slope_to_dir[2*slope.south] <- dir.north + slope_to_dir[2*slope.west ] <- dir.east + + for (local sl = 0; sl < 81; ++sl) { + if (sl in slope_to_dir) { + ASSERT_EQUAL(slope.to_dir(sl), slope_to_dir[sl]) + } + else { + ASSERT_EQUAL(slope.to_dir(sl), dir.none) + } + } +} + + +function test_slope_can_set() +{ + local pl = player_x(0) + local pos = coord3d(2, 3, 0) + + for (local sl = 0; sl < slope.raised; ++sl) { + for (local target_sl = 0; target_sl < slope.raised; ++target_sl) { + command_x.set_slope(pl, pos, sl) + ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), sl) + RESET_ALL_PLAYER_FUNDS() + + local expected = "" + ASSERT_EQUAL(command_x.can_set_slope(pl, pos, sl), expected) + + local sq = square_x(2, 3) + ASSERT_TRUE(sq != null && sq.is_valid()) + ASSERT_EQUAL(sq.get_climate(), cl_mediterran) + + local tile = sq.get_tile_at_height(0) + ASSERT_TRUE(tile != null && tile.is_valid()) + ASSERT_EQUAL(tile.get_slope(), sl) + + ASSERT_EQUAL(pl.get_current_cash(), 33*1000*1000) // get_current_cash is in credits (returns float) + ASSERT_EQUAL(pl.get_current_net_wealth(), 33*1000*1000 * 100) // get_current_net_wealth is in 1/100 credits + } + } + + // reset to normal slope + command_x.set_slope(pl, pos + coord3d(0, 0, 1), slope.all_down_slope) + command_x.set_slope(pl, pos, slope.all_down_slope) + RESET_ALL_PLAYER_FUNDS() + + ASSERT_EQUAL(command_x.can_set_slope(pl, pos + coord3d(0, 0, 1), slope.all_up_slope), "") + ASSERT_EQUAL(command_x.can_set_slope(pl, pos - coord3d(0, 0, 1), slope.all_down_slope), "") + + ASSERT_EQUAL(pl.get_current_cash(), 33*1000*1000) // get_current_cash is in credits (returns float) + ASSERT_EQUAL(pl.get_current_net_wealth(), 33*1000*1000 * 100) // get_current_net_wealth is in 1/100 credits + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_slope_set_and_restore() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + local restoreslope = command_x(tool_restoreslope) + + { + // FIXME Crash when the "" are omitted (default_param is null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 0), "" + slope.north), null) + ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.north) + + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 0), "" + slope.south), null) + ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.south) + + ASSERT_EQUAL(restoreslope.work(pl, coord3d(2, 3, 0)), null) + ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.flat) + } + + { + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 0), "" + slope.all_up_slope), null) + ASSERT_TRUE(tile_x(2, 3, 0).is_valid()) + ASSERT_TRUE(tile_x(2, 3, 1).is_valid()) + ASSERT_EQUAL(tile_x(2, 3, 1).get_slope(), slope.flat) + + // fails as expected because ground is 1 unit higher + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 0), "" + slope.all_up_slope), "") + + // TODO check tile height + ASSERT_EQUAL(restoreslope.work(pl, coord3d(2, 3, 0)), "") + ASSERT_TRUE(tile_x(2, 3, 0).is_valid()) + ASSERT_EQUAL(tile_x(2, 3, 0).get_slope(), slope.flat) + + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(tile_x(2, 3, 1).get_slope(), slope.flat) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_slope_set_near_map_border() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + + // map edge + { + for (local sl = 0; sl < slope.raised; ++sl) { + ASSERT_EQUAL(setslope.work(pl, coord3d(0, 3, 0), "" + sl), "Zu nah am Kartenrand") + } + } + + // map corner + { + for (local sl = 0; sl < slope.raised; ++sl) { + ASSERT_EQUAL(setslope.work(pl, coord3d(0, 0, 0), "" + sl), "Zu nah am Kartenrand") + } + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_slope_max_height_diff() +{ + local pl = player_x(0) + local setslope = command_x(tool_setslope) + + // build upwards, height difference = 4 + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 1), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 2), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 3), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 4), "" + slope.all_up_slope), "Maximum tile height difference reached.") + } + + // diagonally, the height difference is unlimited (technically limited to 2*max_diff) + { + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -2), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -3), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -4), "" + slope.all_down_slope), "Maximum tile height difference reached.") + } + + // and clean up + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 4), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 3), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 2), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 1), "" + slope.all_down_slope), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -4), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -3), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -2), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 3, -1), "" + slope.all_up_slope), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_slope_get_price() +{ + local pl = player_x(0) + for (local sl = 0; sl < slope.raised; ++sl) { + ASSERT_EQUAL(command_x.slope_get_price(slope.flat), 3125 * 100) + } + + local restore_slope = 84 + ASSERT_EQUAL(command_x.slope_get_price(restore_slope), 1000 * 100) + + ASSERT_EQUAL(pl.get_current_cash(), 33*1000*1000) // get_current_cash is in credits (returns float) + ASSERT_EQUAL(pl.get_current_net_wealth(), 33*1000*1000 * 100) // get_current_net_wealth is in 1/100 credits + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_terraform.nut b/tests/tests/test_terraform.nut new file mode 100644 index 00000000000..5cb8a1c8501 --- /dev/null +++ b/tests/tests/test_terraform.nut @@ -0,0 +1,648 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for terraforming terrain +// + + +function test_terraform_raise_lower_land() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + + // TODO check terrain height + + local err = raise.work(player_x(0), coord(3, 2)) + ASSERT_EQUAL(err, "Zu nah am Kartenrand") // TODO Fix error message? + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 100)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 0)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 0)), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 0)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 1)), null) + + // clean up + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 0)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_terraform_raise_lower_land_at_map_border() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + + // TODO Check terrain height + + ASSERT_EQUAL(raise.work(player_x(0), coord(0, 0)), "Zu nah am Kartenrand") + ASSERT_EQUAL(raise.work(player_x(0), coord3d(0, 0, 100)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(0, 0, 0)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(0, 0, 0)), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(0, 0, 0)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(0, 0, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(0, 0, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(0, 0, 1)), null) + ASSERT_EQUAL(lower.work(player_x(0), coord3d(0, 0, 1)), null) + + // clean up + ASSERT_EQUAL(raise.work(player_x(0), coord3d(0, 0, 0)), null) + ASSERT_EQUAL(raise.work(player_x(0), coord3d(0, 0, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_terraform_raise_lower_land_at_water_center() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local clim = command_x(tool_set_climate) + + { + clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water) + + // raise center piece + local err = lower.work(player_x(0), coord3d(3, 3, 0)) + ASSERT_EQUAL(err, "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + + { + clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water) + + // lower center piece + local err = lower.work(player_x(0), coord3d(3, 3, 0)) + ASSERT_EQUAL(err, "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // clean up + ASSERT_EQUAL(clim.work(player_x(0), coord3d(3, 2, 0), coord3d(2, 3, 0), "" + cl_mediterran), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_terraform_raise_lower_land_at_water_corner() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local clim = command_x(tool_set_climate) + + // FIXME inconsistencies with raising/lowering depending on direction + + // raise north-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(2, 2, 0)), "") + + ASSERT_FALSE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // lower north-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(2, 2, 0)), "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + + // raise south-east corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(2, 4, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(2, 4, 0)), null) + } + + // lower south-east corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(2, 4, 0)), null) + + ASSERT_FALSE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(2, 4, 0)), null) + } + + // raise south-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 4, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 4, 0)), null) + } + + // lower south-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 4, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 4, 0)), null) + } + + // raise north-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 2, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 2, 0)), null) + } + + // lower north-west corner + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 2, 0)), null) + + ASSERT_FALSE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 2, 0)), null) + } + + // clean up + ASSERT_EQUAL(clim.work(player_x(0), coord3d(3, 2, 0), coord3d(2, 3, 0), "" + cl_mediterran), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_terraform_raise_lower_land_at_water_edge() +{ + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local clim = command_x(tool_set_climate) + + // FIXME inconsistencies with raising/lowering depending on direction + + // raise north edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 2, 0)), "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // lower north edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 2, 0)), "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // raise east edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 3, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 3, 0)), null) + } + + // lower east edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(4, 3, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(4, 3, 0)), null) + } + + // raise south edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 4, 0)), null) + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 4, 0)), null) + } + + // lower south edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(3, 4, 0)), null) + + ASSERT_FALSE(tile_x(2, 2, 0).is_water()) + ASSERT_FALSE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_FALSE(tile_x(3, 3, 0).is_water()) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(3, 4, 0)), null) + } + + // raise west edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(raise.work(player_x(0), coord3d(2, 3, 0)), "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_FALSE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // lower west edge + { + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_water), null) + + ASSERT_EQUAL(lower.work(player_x(0), coord3d(2, 3, 0)), "") + + ASSERT_TRUE(tile_x(2, 2, 0).is_water()) + ASSERT_TRUE(tile_x(3, 2, 0).is_water()) + ASSERT_TRUE(tile_x(2, 3, 0).is_water()) + ASSERT_TRUE(tile_x(3, 3, 0).is_water()) + } + + // clean up + ASSERT_EQUAL(clim.work(player_x(0), coord3d(2, 3, 0), coord3d(3, 2, 0), "" + cl_mediterran), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_terraform_raise_lower_land_below_way() +{ + local pl = player_x(0) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local setslope = command_x(tool_setslope) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // raise below way + { + ASSERT_EQUAL(raise.work(pl, coord3d(4, 2, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(4, 3, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(4, 4, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(4, 5, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(5, 2, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(5, 3, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(5, 4, 0)), "Tile not empty.") + ASSERT_EQUAL(raise.work(pl, coord3d(5, 5, 0)), "Tile not empty.") + } + + // and lower + { + ASSERT_EQUAL(lower.work(pl, coord3d(4, 2, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(4, 3, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(4, 4, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(4, 5, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(5, 2, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(5, 3, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(5, 4, 0)), "Tile not empty.") + ASSERT_EQUAL(lower.work(pl, coord3d(5, 5, 0)), "Tile not empty.") + } + + // set slope up + { + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.south), "") + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + 2*slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.north), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.north), "") + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + 2*slope.north), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), null) + } + + // set slope down + { + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, 0), "" + slope.all_down_slope), "") + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 2, -1), "" + slope.all_up_slope), null) + + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, 0), "" + slope.all_down_slope), "") + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 4, -1), "" + slope.all_up_slope), null) + } + + // non-dead-end, should fail for all slopes + { + for (local sl = slope.flat+1; sl <= slope.all_down_slope; ++sl) { + if (sl != slope.raised && sl != 81) { + ASSERT_EQUAL(setslope.work(pl, coord3d(4, 3, 0), "" + sl), "Tile not empty.") + } + } + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +/// Helper function: Raise / lower land along the edge of a rectangular region +function terraform_volcano(pl, pos, size, h) +{ + local raise = h > 0 + local tool = raise ? command_x(tool_raise_land) : command_x(tool_lower_land) + + for (local dz = 0; raise ? dzh; dz += raise?1:-1) { + for (local i = 0; i= 0) + ASSERT_TRUE(tree.get_age() < (1<<12)) + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) + } + + // Check building with ignore_climate = true + { + ASSERT_EQUAL(command_x(tool_set_climate).work(public_pl, coord3d(4, 3, 0), coord3d(4, 3, 0), "" + cl_arctic), null) + + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc, false, false), "") + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_tree), null) + + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc, true, false), null) + ASSERT_TRUE(tile_x(4, 3, 0).find_object(mo_tree) != null) + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_set_climate).work(public_pl, coord3d(4, 3, 0), coord3d(4, 3, 0), "" + cl_mediterran), null) + } + + // Check building over max_no_of_trees_on_square limit (= 5) + { + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), null) + + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 3, 0), tree_desc), "") + + ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(4, 3, 0)), null) // removes all trees at once + ASSERT_EQUAL(tile_x(4, 3, 0).find_object(mo_tree), null) + } + + // Cannot build trees on ways + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), road, true), null) + ASSERT_EQUAL(build_tree(public_pl, coord3d(4, 4, 0), tree_desc), "") + ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_tree), null) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), "" + wt_road), null) + } + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_trees_plant_forest() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local planter = command_x(tool_forest) + local tree_desc = tree_desc_x("Busch_1") + + local topleft = coord3d(1, 1, 0) + local bottomright = coord3d(6, 6, 0) + + // 1x1 + { + ASSERT_EQUAL(planter.work(public_pl, topleft, topleft, ""), null) + ASSERT_EQUAL(tile_x(topleft.x, topleft.y, 0).find_object(mo_tree), null) + } + + // 2x2 + { + ASSERT_EQUAL(planter.work(public_pl, topleft, topleft + coord(1, 1), ""), null) + ASSERT_EQUAL(tile_x(topleft.x+0, topleft.y+0, 0).find_object(mo_tree), null) + ASSERT_EQUAL(tile_x(topleft.x+1, topleft.y+0, 0).find_object(mo_tree), null) + ASSERT_EQUAL(tile_x(topleft.x+0, topleft.y+1, 0).find_object(mo_tree), null) + ASSERT_TRUE(tile_x(topleft.x+1, topleft.y+1, 0).find_object(mo_tree) != null) + } + + // this works with player 0 even though trees are disabled + { + ASSERT_EQUAL(planter.work(pl, topleft, bottomright, ""), null) + + for (local y = 0; y < 8; ++y) { + for (local x = 0; x < 8; ++x) { + local outside = ((y+1)%8 < 3) || ((x+1)%8 < 3) // FIXME: this should fail for == 2 + + if (outside) { + ASSERT_EQUAL(tile_x(x, y, 0).find_object(mo_tree), null) + } + } + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + } + + // clean up + for (local y = 0; y < 8; ++y) { + for (local x = 0; x < 8; ++x) { + command_x(tool_remover).work(pl, coord3d(x, y, 0)) + } + } + + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_way_bridge.nut b/tests/tests/test_way_bridge.nut new file mode 100644 index 00000000000..f5c6b7e47fc --- /dev/null +++ b/tests/tests/test_way_bridge.nut @@ -0,0 +1,626 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for bridge building / removal +// + + +function test_way_bridge_build_ground() +{ + local pl = player_x(0) + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + local remover = command_x(tool_remove_way) + ASSERT_TRUE(bridge_desc != null) + + // build bridge on flat ground + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 4, 0), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "...5....", + "...0....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + + // build bridge over bridge ramp (should fail) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), bridge_desc), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + + // build bridge ramp under bridge (should fail) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 3, 0), coord3d(5, 3, 0), bridge_desc), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + + // build bridge crossing (should fail) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), bridge_desc), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_EQUAL(remover.work(pl, tile_x(3, 1, 0), tile_x(3, 5, 0), "" + wt_road), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) +} + + +function test_way_bridge_build_at_slope() +{ + local start_pos = coord3d(2, 1, 0) + local end_pos = coord3d(2, 6, 0) + local remover = command_x(tool_remove_way) + local setslope = command_x(tool_setslope) + local pl = player_x(0) + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + ASSERT_EQUAL(setslope.work(pl, coord3d(2, 1, 0), "" + slope.south), null) + + { + // down slope + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, "Bridge is too long for this type!\n") + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // wrong single-height slope + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + slope.west), null) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, "A bridge must start on a way!") + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // double height slope + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + 2*slope.north), null) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, "Cannot connect to the\ncenter of a double slope!") + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // wrong double height slope + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + 2*slope.west), null) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, "Cannot connect to the\ncenter of a double slope!") + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + // flat double height slope (i.e. 2 stacked flat slopes) + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, end_pos + coord3d(0, 0, 1), "" + slope.all_up_slope), null) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, "Cannot connect to the\ncenter of a double slope!") + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(setslope.work(pl, end_pos + coord3d(0, 0, 2), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, end_pos + coord3d(0, 0, 1), "" + slope.all_down_slope), null) + } + + { + // flat single height slope + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + slope.all_up_slope), null) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "..4.....", + "..5.....", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + // remove way + ASSERT_EQUAL(remover.work(pl, start_pos, end_pos + coord3d(0, 0, 1), "" + wt_road), null) + + // remove slope + ASSERT_EQUAL(setslope.work(pl, end_pos + coord3d(0, 0, 1), "" + slope.all_down_slope), null) + } + + { + // correct slope + setslope.work(pl, end_pos, "" + slope.north) + local err = command_x.build_bridge_at(pl, start_pos, bridge_desc) + ASSERT_EQUAL(err, null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "..4.....", + "..5.....", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + // remove way + ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) + + // remove slope + ASSERT_EQUAL(setslope.work(pl, end_pos, "" + slope.flat), null) + } + + // clean up + ASSERT_EQUAL(setslope.work(pl, start_pos, "" + slope.flat), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_bridge_build_at_slope_stacked() +{ + local remover = command_x(tool_remove_way) + local setslope = command_x(tool_setslope) + local pl = player_x(0) + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.north), null) + + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + + + // second bridge layer + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 1, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 1, 1), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 6, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 6, 1), "" + slope.north), null) + + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 1, 1), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...4....", + "...5....", + "...5....", + "...1....", + "...1....", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + } + + { + // remove lower bridge and rebuild it + ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) + + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "...4....", + "...4....", + "...5....", + "...5....", + "...1....", + "...1....", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), + [ + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + + ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 1), coord3d(3, 6, 1), "" + wt_road), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 1, 1), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 6, 1), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 1, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 6, 1), "" + slope.all_down_slope), null) + } + + { + // second bridge layer + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 3, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 3, 1), "" + slope.east), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(6, 3, 0), "" + slope.all_up_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(6, 3, 1), "" + slope.west), null) + + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(1, 3, 1), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "...4....", + ".2.5..8.", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), + [ + "........", + "........", + "...4....", + ".2AAAA8.", + "........", + "...1....", + "........", + "........" + ]) + } + + { + // remove lower bridge and rebuild it + ASSERT_EQUAL(remover.work(pl, coord3d(3, 5, 0), coord3d(3, 2, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 5, 0), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "...4....", + ".2.5..8.", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 2), + [ + "........", + "........", + "...4....", + ".2AAAA8.", + "........", + "...1....", + "........", + "........" + ]) + + } + + // clean up + ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) + ASSERT_EQUAL(remover.work(pl, coord3d(1, 3, 1), coord3d(6, 3, 1), "" + wt_road), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 3, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 3, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(6, 3, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(6, 3, 1), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.all_down_slope), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +// TODO Try to build bridge when way_height_clearance == 2 +function test_way_bridge_build_above_way() +{ + local remover = command_x(tool_remove_way) + local setslope = command_x(tool_setslope) + local pl = player_x(0) + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + local way_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + ASSERT_TRUE(bridge_desc != null) + ASSERT_TRUE(way_desc != null) + + { + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.north), null) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), way_desc, true), null) + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 2, 0), bridge_desc), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "..2A8...", + "........", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...1....", + "........", + "........" + ]) + } + + { + // remove way, rebuild it under bridge + ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), way_desc, true), null) + } + + ASSERT_EQUAL(remover.work(pl, coord3d(2, 3, 0), coord3d(4, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 2, 0), "" + slope.all_down_slope), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.all_down_slope), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_bridge_build_above_runway() +{ + local pl = player_x(0) + local wayremover = command_x(tool_remove_way) + local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] + local runway = way_desc_x.get_available_ways(wt_air, st_elevated)[0] + local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] + local setslope = command_x(tool_setslope) + + // preconditions + + // build bridge across taxiway + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), taxiway, true), null) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(6, 8, 0), coord3d(8, 8, 0), bridge), null) + + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + "........", + "..4.....", + "..5.....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(5, 5, 0), + [ + "........", + "........", + "........", + "2A.A8...", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), "" + wt_air), null) + ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 8, 0), coord3d(9, 8, 0), "" + wt_road), null) + } + + // build bridge across runway, should fail + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), runway, true), null) + ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(6, 8, 0), coord3d(8, 8, 0), bridge), "") + + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + "........", + "..4.....", + "..5.....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 7, 0), coord3d(7, 9, 0), "" + wt_air), null) + } + + // clean up + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_bridge_planner() +{ + local pl = player_x(0) + local start_pos = coord3d(2, 3, 0) + local end_pos = coord3d(2, 6, 0) + local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0] + + local working_slopes = [ slope.north ] // maybe also slope.south in the future + + ASSERT_EQUAL(command_x.set_slope(pl, start_pos, slope.south), null) + + { + for (local sl = slope.flat; sl < slope.raised; ++sl) { + command_x.set_slope(pl, end_pos, sl) + + local end = bridge_planner_x.find_end(pl, start_pos, dir.south, bridge_desc, 0) + local expected_end = (working_slopes.find(sl) != null) ? end_pos : coord3d(-1, -1, -1) + + ASSERT_EQUAL(end.tostring(), expected_end.tostring()) + } + + ASSERT_EQUAL(command_x.set_slope(pl, end_pos, slope.all_up_slope), null) + local end = bridge_planner_x.find_end(pl, start_pos, dir.south, bridge_desc, 0) + ASSERT_EQUAL(end.tostring(), (end_pos + coord3d(0, 0, 1)).tostring()) + + // clean up + ASSERT_EQUAL(command_x.set_slope(pl, start_pos, slope.flat), null) + ASSERT_EQUAL(command_x.set_slope(pl, end_pos + coord3d(0, 0, 1), slope.all_down_slope), null) + } + + // min length + { + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 1, 0), slope.south), null) + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 1, 0), slope.south), null) + + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 2, 0), slope.north), null) + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 3, 0), slope.north), null) + + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(2, 1, 0), dir.south, bridge_desc, 0).tostring(), coord3d(2, 2, 0).tostring()) + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(2, 1, 0), dir.south, bridge_desc, 1).tostring(), coord3d(2, 2, 0).tostring()) + + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 0).tostring(), coord3d(3, 3, 0).tostring()) + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 1).tostring(), coord3d(-1, -1, -1).tostring()) + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 2).tostring(), coord3d(3, 3, 0).tostring()) + ASSERT_EQUAL(bridge_planner_x.find_end(pl, coord3d(3, 1, 0), dir.south, bridge_desc, 3).tostring(), coord3d(-1, -1, -1).tostring()) + + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 1, 0), slope.flat), null) + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 1, 0), slope.flat), null) + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(2, 2, 0), slope.flat), null) + ASSERT_EQUAL(command_x.set_slope(pl, coord3d(3, 3, 0), slope.flat), null) + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_way_road.nut b/tests/tests/test_way_road.nut new file mode 100644 index 00000000000..fee665cc80c --- /dev/null +++ b/tests/tests/test_way_road.nut @@ -0,0 +1,1098 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for way building / removal +// + + +function test_way_road_build_single_tile() +{ + local pl = player_x(0) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local default_cash = pl.get_current_cash() + + ASSERT_TRUE(road_desc != null) + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), road_desc, true), "") + ASSERT_EQUAL(pl.get_current_cash(), default_cash) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 2, 0), road_desc, false), "") + ASSERT_EQUAL(pl.get_current_cash(), default_cash) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_remove_straight() +{ + local default_cash = 33*1000*1000 * 100 + + local pl = player_x(0) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + ASSERT_TRUE(road_desc != null) + + //prerequisites + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), road_desc, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + "..1.....", + "........", + "........", + "........", + "........" + ]) + +// ASSERT_EQUAL(pl.get_current_maintenance(), 3*road_desc.get_maintenance()) // FIXME + ASSERT_TRUE(pl.get_current_net_wealth() < default_cash) + ASSERT_TRUE(pl.get_current_cash() < default_cash / 100) + + // now remove + ASSERT_TRUE(command_x(tool_remove_way).work(pl, tile_x(2, 1, 0), tile_x(2, 3, 0), "" + wt_road) == null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // clean up + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_t_junction() +{ + local pl = player_x(0) + current_cash = pl.get_current_net_wealth() + + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 4, 0), desc, true) == null) + + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 3, 0), coord3d(3, 3, 0), desc, true) == null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..7AAA8.", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + } +} + + +function test_way_road_build_straight() +{ + local pl = player_x(0) + local default_cash = 33*1000*1000 * 100 + local current_cash = pl.get_current_net_wealth() + local maintenance_per_tile = pl.get_current_maintenance() / 6 + local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local tool_result = command_x.build_way(pl, coord3d(2, 2, 0), coord3d(6, 2, 0), desc, true) + + ASSERT_EQUAL(tool_result, null) + ASSERT_TRUE(pl.get_current_net_wealth() < current_cash) + ASSERT_TRUE(pl.get_current_cash() < default_cash / 100) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..7AAA8.", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = command_x.build_way(pl, coord3d(16, 5, 0), coord3d(5, 5, 0), desc, true) + ASSERT_EQUAL(tool_result, "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..7AAA8.", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = command_x.build_way(pl, coord3d(1, 1, 1), coord3d(1, 5, 1), desc, true) + ASSERT_EQUAL(tool_result, "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..7AAA8.", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = command_x.build_way(pl, coord3d(1, 1, -1), coord3d(1, 5, -1), desc, true) + ASSERT_EQUAL(tool_result, "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..7AAA8.", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + // clean up + + local remover = command_x(tool_remove_way) + tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(6, 2, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + "..5.....", + "..5.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = remover.work(pl, tile_x(2, 3, 0), tile_x(2, 4, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + "..5.....", + "..1.....", + "..4.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = remover.work(pl, tile_x(2, 1, 0), tile_x(2, 2, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "..4.....", + "..1.....", + "..4.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(2, 6, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, "Ways not connected") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "..4.....", + "..1.....", + "..4.....", + "..5.....", + "..1.....", + "........" + ]) + + tool_result = remover.work(pl, tile_x(2, 2, 0), tile_x(2, 3, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, null) + tool_result = remover.work(pl, tile_x(2, 4, 0), tile_x(2, 6, 0), "" + wt_road) + ASSERT_EQUAL(tool_result, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_bend() +{ + local pl = player_x(0) + local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local remover = command_x(tool_remove_way) + + ASSERT_TRUE(desc != null) + + { + local tool_result = command_x.build_way(pl, coord3d(0, 1, 0), coord3d(1, 0, 0), desc, true) + ASSERT_EQUAL(tool_result, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + ".4......", + "29......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + remover.work(pl, tile_x(0, 1, 0), coord3d(1, 0, 0), "" + wt_road) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + { + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + + raise.work(pl, coord3d(2, 2, 0)) + + local err = command_x.build_way(pl, coord3d(0, 1, 0), coord3d(1, 0, 0), desc, true) + ASSERT_EQUAL(err, null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "68......", + "1.......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + remover.work(pl, tile_x(0, 1, 0), coord3d(1, 0, 0), "" + wt_road) + lower.work(pl, coord3d(2, 2, 0)) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_parallel() +{ + local pl = player_x(0) + local desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local rail_desc = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local remover = command_x(tool_remove_way) + + { + for (local i = 1; i < 16; ++i) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(i, i, 0), desc, true), null) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "................", + "28..............", + "2A8.............", + "2AA8............", + "2AAA8...........", + "2AAAA8..........", + "2AAAAA8.........", + "2AAAAAA8........", + "2AAAAAAA8.......", + "2AAAAAAAA8......", + "2AAAAAAAAA8.....", + "2AAAAAAAAAA8....", + "2AAAAAAAAAAA8...", + "2AAAAAAAAAAAA8..", + "2AAAAAAAAAAAAA8.", + "2AAAAAAAAAAAAAA8" + ]) + + for (local i = 1; i < 16; ++i) { + ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(i, i, 0), "" + wt_road), null) + } + } + + { + for (local i = 1; i < 16; ++i) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(i, i, 0), desc, false), null) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "................", + "28..............", + "2A8.............", + "2AA8............", + "2AAA8...........", + "2AAAA8..........", + "2AAAAA8.........", + "2AAAAAA8........", + "2AAAAAAA8.......", + "2AAAAAAAA8......", + "2AAAAAAAAA8.....", + "2AAAAAAAAAA8....", + "2AAAAAAAAAAA8...", + "2AAAAAAAAAAAA8..", + "2AAAAAAAAAAAAA8.", + "2AAAAAAAAAAAAAA8" + ]) + + for (local i = 1; i < 16; ++i) { + ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(i, i, 0), "" + wt_road), null) + } + } + + { + for (local i = 0; i < 16; ++i) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), desc, false), null) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8" + ]) + + for (local i = 0; i < 16; ++i) { + ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(15, i, 0), "" + wt_road), null) + } + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_below_powerline() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local remover = command_x(tool_remove_way) + local powerline = way_desc_x.get_available_ways(wt_power, st_flat)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(1, 3, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(1, 1, 0), powerline, true), null) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + ".6AC....", + ".5.5....", + ".3A9....", + "........", + "........", + "........", + "........" + ]) + + // build way along powerline above, should fail + { + local old_maintenance = pl.get_current_maintenance() + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), road, true), "") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + } + + // build way ending below power line, should succeed + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 1, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(3, 2, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 3, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(1, 2, 0), road, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "..4.....", + ".2F8....", + "..1.....", + "........", + "........", + "........", + "........" + ]) + } + + // remove ways + ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_road), null) + ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_road), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + // build way not ending under power line, should succeed + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 0, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(4, 2, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(2, 4, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 2, 0), coord3d(0, 2, 0), road, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "..4.....", + "..5.....", + "2AFA8...", + "..5.....", + "..1.....", + "........", + "........", + "........" + ]) + } + + // remove ways + ASSERT_EQUAL(remover.work(pl, coord3d(2, 0, 0), coord3d(2, 4, 0), "" + wt_road), null) + ASSERT_EQUAL(remover.work(pl, coord3d(0, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) + ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, 0), coord3d(3, 3, 0), "" + wt_power), null) + ASSERT_EQUAL(remover.work(pl, coord3d(1, 1, 0), coord3d(3, 3, 0), "" + wt_power), null) + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // build way under end of powerline, should fail + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), powerline, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), powerline, true), null) + + { + local old_maintenance = pl.get_current_maintenance() + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 1, 0), coord3d(3, 1, 0), road, true), "") + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), road, true), "") + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(1, 3, 0), road, true), "") + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 3, 0), coord3d(1, 1, 0), road, true), "") + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "........", + "..4.....", + ".2F8....", + "..1.....", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + } + + ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), "" + wt_power), null) + ASSERT_EQUAL(remover.work(pl, coord3d(1, 2, 0), coord3d(3, 2, 0), "" + wt_power), null) + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + + // add diagonal power line + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), powerline, true), null) + + { + // build way across diagonal power line, should fail + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, 7, 0), coord3d(7, 0, 0), road, true), "") + + ASSERT_WAY_PATTERN(wt_power, coord3d(0, 0, 0), + [ + "2C......", + ".3C.....", + "..3C....", + "...3C...", + "....3C..", + ".....3C.", + "......3C", + ".......1" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // and remove power line + ASSERT_EQUAL(remover.work(pl, coord3d(0, 0, 0), coord3d(7, 7, 0), "" + wt_power), null) + + // clean up + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_upgrade_downgrade() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local remover = command_x(tool_remove_way) + local start_pos = coord3d(4, 3, 0) + local end_pos = coord3d(4, 5, 0) + + local all_ways = way_desc_x.get_available_ways(wt_road, st_flat) + all_ways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + + ASSERT_TRUE(all_ways.len() >= 2) + local slow_road = all_ways[0] + local fast_road = all_ways[1] + ASSERT_TRUE(slow_road.get_name() != fast_road.get_name()) + ASSERT_TRUE(slow_road.get_topspeed() < fast_road.get_topspeed()) + + // build road + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, slow_road, true), null) + + // upgrade road + { + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "....4...", + "....5...", + "....1...", + "........", + "........" + ]) + } + + // Replace road with same road, should incur no cost + { + local old_cash = pl.get_current_cash() + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "....4...", + "....5...", + "....1...", + "........", + "........" + ]) + } + + // downgrade road without ctrl; should fail + // note that we cannot use command_x.buid_way for this, because there is no way to enable + // ctrl-pressed behaviour (the way is replaced in any case) + { + local old_maintenance = pl.get_current_maintenance() + local old_cash = pl.get_current_cash() + + local tool = command_x(tool_build_way) + ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) + + ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "....4...", + "....5...", + "....1...", + "........", + "........" + ]) + } + + // downgrade road with ctrl + { + local tool = command_x(tool_build_way) + tool.set_flags(2) // ctrl + ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) + + ASSERT_EQUAL(tile_x(4, 4, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "....4...", + "....5...", + "....1...", + "........", + "........" + ]) + } + + ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) + + // clean up + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_upgrade_downgrade_across_bridge() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local remover = command_x(tool_remove_way) + local start_pos = coord3d(3, 6, 0) + local end_pos = coord3d(3, 2, 0) + local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] + local all_ways = way_desc_x.get_available_ways(wt_road, st_flat) + all_ways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + + ASSERT_TRUE(all_ways.len() >= 2) + local slow_road = all_ways[0] + local fast_road = all_ways[1] + ASSERT_TRUE(slow_road.get_name() != fast_road.get_name()) + ASSERT_TRUE(slow_road.get_topspeed() < fast_road.get_topspeed()) + + // build bridge + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(3, 6, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(4, 6, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(3, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 5, 0), bridge), null) + + // upgrade road + { + ASSERT_TRUE(tile_x(3, 6, 0).find_object(mo_way) != null) + ASSERT_TRUE(tile_x(3, 2, 0).find_object(mo_way) != null) + + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) + ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) + ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "........", + "...5....", + "...1....", + "........" + ]) + } + + // Replace road with same road, should incur no cost + { + local old_cash = pl.get_current_cash() + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, fast_road, true), null) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "........", + "...5....", + "...1....", + "........" + ]) + } + + // downgrade road without ctrl; should fail + // note that we cannot use command_x.buid_way for this, because there is no way to enable + // ctrl-pressed behaviour (the way is replaced in any case) + { + local old_maintenance = pl.get_current_maintenance() + local old_cash = pl.get_current_cash() + + local tool = command_x(tool_build_way) + ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) + + ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) + ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), fast_road.get_name()) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maintenance) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "........", + "...5....", + "...1....", + "........" + ]) + } + + // downgrade road with ctrl + { + local tool = command_x(tool_build_way) + tool.set_flags(2) // ctrl + ASSERT_EQUAL(tool.work(pl, start_pos, end_pos, slow_road.get_name()), null) + + ASSERT_EQUAL(tile_x(3, 6, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) + ASSERT_EQUAL(tile_x(3, 2, 0).find_object(mo_way).get_desc().get_name(), slow_road.get_name()) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "........", + "...5....", + "...1....", + "........" + ]) + } + + // remove bridge + ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(3, 6, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(4, 6, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(3, 3, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(4, 3, 1)), null) + + // clean up + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_build_cityroad() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local builder = command_x(tool_build_cityroad) + local remover = command_x(tool_remove_way) + local start_pos = coord3d(3, 6, 0) + local end_pos = coord3d(3, 2, 0) + local all_roads = way_desc_x.get_available_ways(wt_road, st_flat) + all_roads.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + + local slow_road = all_roads[0] + local fast_road = all_roads[1] + + // build city road + { + ASSERT_EQUAL(builder.work(pl, start_pos, end_pos, slow_road.get_name()), null) + + for (local y = start_pos.y; y < end_pos.y; ++y) { + local r = way_x(3, y, 0) + ASSERT_TRUE(r != null) + ASSERT_TRUE(r.is_valid()) + ASSERT_TRUE(r.has_sidewalk()) + ASSERT_EQUAL(r.get_desc().get_name(), slow_road.get_name()) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + } + + // upgrade cityroad + { + ASSERT_EQUAL(builder.work(pl, start_pos, end_pos, fast_road.get_name()), null) + + for (local y = start_pos.y; y < end_pos.y; ++y) { + local r = way_x(3, y, 0) + ASSERT_TRUE(r != null) + ASSERT_TRUE(r.is_valid()) + ASSERT_TRUE(r.has_sidewalk()) + ASSERT_EQUAL(r.get_desc().get_name(), fast_road.get_name()) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + } + + // downgrade cityroad + { + builder.set_flags(2) + ASSERT_EQUAL(builder.work(pl, start_pos, end_pos, fast_road.get_name()), null) + builder.set_flags(0) + for (local y = start_pos.y; y < end_pos.y; ++y) { + local r = way_x(3, y, 0) + ASSERT_TRUE(r != null) + ASSERT_TRUE(r.is_valid()) + ASSERT_TRUE(r.has_sidewalk()) + ASSERT_EQUAL(r.get_desc().get_name(), slow_road.get_name()) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + } + + // replace cityroad by normal road + { + ASSERT_EQUAL(command_x.build_way(pl start_pos, end_pos, fast_road, true), null) + for (local y = start_pos.y; y < end_pos.y; ++y) { + local r = way_x(3, y, 0) + ASSERT_TRUE(r != null) + ASSERT_TRUE(r.is_valid()) + ASSERT_FALSE(r.has_sidewalk()) + ASSERT_EQUAL(r.get_desc().get_name(), fast_road.get_name()) + } + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "...4....", + "...5....", + "...5....", + "...5....", + "...1....", + "........" + ]) + } + + // clean up + ASSERT_EQUAL(remover.work(pl, start_pos, end_pos, "" + wt_road), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_road_has_double_slopes() +{ + local roads = way_desc_x.get_available_ways(wt_road, st_flat) + + foreach (r in roads) { + ASSERT_TRUE(r.has_double_slopes()) + } +} + + +function test_way_road_make_public() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local wayremover = command_x(tool_remove_way) + local road_desc = way_desc_x.get_available_ways(wt_road, st_flat)[0] + local makepublic = command_x(tool_make_stop_public) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 2, 0), coord3d(4, 4, 0), road_desc, true), null) + + // invalid coord + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(-1, -1, -1)), "No suitable ground!") + + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // nothing to make public + { + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(3, 4, 0)), null) + + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // make road public + { + local old_pl_cash = pl.get_current_cash() + local old_pl_maint = pl.get_current_maintenance() + local old_public_cash = public_pl.get_current_cash() + local old_public_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 2, 0)), null) + + ASSERT_TRUE(way_x(4, 2, 0).get_owner() != null) + ASSERT_EQUAL(way_x(4, 2, 0).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * road_desc.get_maintenance()) // 60 == cst_make_public_months + ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - road_desc.get_maintenance()) + + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + road_desc.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) + } + + ASSERT_EQUAL(wayremover.work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_way_runway.nut b/tests/tests/test_way_runway.nut new file mode 100644 index 00000000000..9a24d55679c --- /dev/null +++ b/tests/tests/test_way_runway.nut @@ -0,0 +1,283 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// tests for runways/taxiways +// + + +function test_way_runway_build_rw_flat() +{ + local pl = player_x(0) + local wayremover = command_x(tool_remove_way) + local runway = way_desc_x.get_available_ways(wt_air, st_elevated)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + // preconditions + ASSERT_TRUE(runway != null) + ASSERT_TRUE(road != null) + + // Build runway next to map border, should fail (causes glitches with planes taking off/landing) + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), runway, true), "") + ASSERT_WAY_PATTERN(wt_air, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // Straight runway away from map border + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), runway, true), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + ".5......", + ".1......", + "........", + "........", + "........", + "........" + ]) + } + + // Cross runway with existing one + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), runway, true), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + "2F8.....", + ".1......", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), "" + wt_air), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + ".5......", + ".1......", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), "" + wt_air), null) + } + + // no runways across ways + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(9, 7, 0), coord3d(9, 9, 0), runway, true), "") + + ASSERT_WAY_PATTERN(wt_road, coord3d(6, 6, 0), + [ + "........", + "........", + "..2A8...", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), "" + wt_road), null) + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_runway_build_tw_flat() +{ + local pl = player_x(0) + local wayremover = command_x(tool_remove_way) + local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + // preconditions + ASSERT_TRUE(taxiway != null) + ASSERT_TRUE(road != null) + + // Build taxiway next to map border, should succeed (contrary to runway) + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), taxiway, true), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "....4...", + "....5...", + "....1...", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(4, 3, 0), coord3d(4, 5, 0), "" + wt_air), null) + } + + // Straight taxiway away from map border + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), taxiway, true), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + ".5......", + ".1......", + "........", + "........", + "........", + "........" + ]) + } + + // Cross taxiway with existing one + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), taxiway, true), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + "2F8.....", + ".1......", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(5, 7, 0), coord3d(7, 7, 0), "" + wt_air), null) + ASSERT_WAY_PATTERN(wt_air, coord3d(5, 5, 0), + [ + "........", + ".4......", + ".5......", + ".1......", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(6, 6, 0), coord3d(6, 8, 0), "" + wt_air), null) + } + + // no taxiways across ways + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), road, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(9, 7, 0), coord3d(9, 9, 0), taxiway, true), "") + + ASSERT_WAY_PATTERN(wt_road, coord3d(6, 6, 0), + [ + "........", + "........", + "..2A8...", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(10, 8, 0), "" + wt_road), null) + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_runway_build_mixed_flat() +{ + local pl = player_x(0) + local wayremover = command_x(tool_remove_way) + local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0] + local runway = way_desc_x.get_available_ways(wt_air, st_flat)[0] + + // t junction + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(8, 8, 0), coord3d(8, 10, 0), runway, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 9, 0), coord3d(8, 9, 0), taxiway, true), null) + + ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), + [ + "........", + "........", + "..4.....", + ".2D.....", + "..1.....", + "........", + "........", + "........" + ]) + } + + // crossing + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(7, 9, 0), coord3d(9, 9, 0), taxiway, true), null) + + ASSERT_WAY_PATTERN(wt_air, coord3d(6, 6, 0), + [ + "........", + "........", + "..4.....", + ".2F8....", + "..1.....", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(wayremover.work(pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_air), null) + ASSERT_EQUAL(wayremover.work(pl, coord3d(8, 8, 0), coord3d(8, 10, 0), "" + wt_air), null) + } + + ASSERT_EQUAL(pl.get_current_maintenance(), 0) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_way_tram.nut b/tests/tests/test_way_tram.nut new file mode 100644 index 00000000000..ac935ffb106 --- /dev/null +++ b/tests/tests/test_way_tram.nut @@ -0,0 +1,357 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for tramways +// + +function test_way_tram_build_flat() +{ + local pl = player_x(0) + local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + + // build straight + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_build_parallel() +{ + local pl = player_x(0) + local rail_desc = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + local remover = command_x(tool_remove_way) + + { + for (local i = 0; i < 16; ++i) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), rail_desc, true), null) + } + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8", + "2AAAAAAAAAAAAAA8" + ]) + + for (local i = 0; i < 16; ++i) { + ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(15, i, 0), "" + wt_rail), null) + } + } + + RESET_ALL_PLAYER_FUNDS() + + { + for (local i = 0; i < 16; ++i) { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(0, i, 0), coord3d(15, i, 0), rail_desc, false), null) + } + + // FIXME this is different from the road pattern (which is all straight roads even without ctrl) + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "2AAEEAAAAAAAAAE8", + "2AEB9.........3C", + "2A9............1", + "2AAEEAAAAAAAAAE8", + "2AEB9.........3C", + "2A9............1", + "2AAEEAAAAAAAAAE8", + "2AEB9.........3C", + "2A9............1", + "2AAEEAAAAAAAAAE8", + "2AEB9.........3C", + "2A9............1", + "2AAEEAAAAAAAAAE8", + "2AEB9.........3C", + "2A9............1", + "2AAAAAAAAAAAAAA8" + ]) + + for (local i = 0; i < 15; i = i + 3) { + ASSERT_EQUAL(remover.work(pl, coord3d(0, i, 0), coord3d(15, i, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(3, i, 0), coord3d(4, i, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(0, i+1, 0), coord3d(0, i+2, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(2, i+1, 0), coord3d(3, i+1, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(14, i, 0), coord3d(15, i+2, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(remover.work(pl, coord3d(0, 15, 0), coord3d(15, 15, 0), "" + wt_rail), null) + } + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_build_on_road() +{ + local pl = player_x(0) + local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + local road = way_desc_x.get_available_ways(wt_road, st_flat)[0] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), road, true), null) + + // build fully on existing road + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) + } + + // cross existing road + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), tramway, true), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "...4....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "..2A8...", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_build_across_road_bridge() +{ + local pl = player_x(0) + local bridge = bridge_desc_x.get_available_bridges(wt_road)[0] + local setslope = command_x(tool_setslope) + local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + + // build bridge + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.south), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.north), null) + ASSERT_EQUAL(command_x.build_bridge_at(pl, coord3d(3, 3, 0), bridge), null) + + // and build tram track + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), tramway, true), null) + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "...4....", + "...5....", + "...1....", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 1), + [ + "........", + "........", + "........", + "...4....", + "...5....", + "...1....", + "........", + "........" + ]) + + // do not remove tram tracks here, this is done by tool_remover below + } + + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(3, 3, 0)), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 3, 0), "" + slope.flat), null) + ASSERT_EQUAL(setslope.work(pl, coord3d(3, 5, 0), "" + slope.flat), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_build_across_crossing() +{ + local pl = player_x(0) + + local tramways = way_desc_x.get_available_ways(wt_rail, st_tram) + tramways.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local tramway = tramways[0] + + local roads = way_desc_x.get_available_ways(wt_road, st_flat) + roads.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + local road = roads[0] + + local rails = way_desc_x.get_available_ways(wt_rail, st_flat) + rails.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + rails.filter(@(idx, desc) desc.get_topspeed() >= tramway.get_topspeed()) + local rail = rails[0] + + // build crossing + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), road, true), null) + + // build tram track + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), tramway, true), null) + + // crossing should have been replaced by tramway + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 3, 0), coord3d(3, 5, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 4, 0), coord3d(4, 4, 0), "" + wt_road), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_build_in_tunel() +{ + local pl = player_x(0) + local tramway = way_desc_x.get_available_ways(wt_rail, st_tram)[0] + local road_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] + local rail_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] + + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(2, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(2, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(3, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(3, 3, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(command_x(tool_raise_land).work(pl, coord3d(4, 3, 0)), null) + + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(1, 2, 0), road_tunnel.get_name()), null) + + // build tramway through road tunnel + { + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), tramway, true), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) + } + + // cross road tunnel with tramway + { + local tunnel_builder = command_x(tool_build_tunnel) + tunnel_builder.set_flags(2) // ctrl + ASSERT_EQUAL(command_x.build_way(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), tramway, true), null) + ASSERT_EQUAL(tunnel_builder.work(pl, coord3d(2, 1, 0), rail_tunnel.get_name()), null) + ASSERT_EQUAL(tunnel_builder.work(pl, coord3d(2, 3, 0), rail_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "........", + "..0.....", + ".2AA8...", + "..0.....", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 3, 0), tramway, true), "") + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + "........", + "..0.....", + ".2AA8...", + "..0.....", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + ".2AA8...", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(2, 1, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 3, 0), coord3d(2, 3, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_rail), null) + } + + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(1, 2, 0), coord3d(4, 2, 0), "" + wt_road), null) + + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(2, 2, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(2, 3, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(3, 2, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(3, 3, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(4, 2, 1)), null) + ASSERT_EQUAL(command_x(tool_lower_land).work(pl, coord3d(4, 3, 1)), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tram_has_double_slopes() +{ + local roads = way_desc_x.get_available_ways(wt_rail, st_tram) + + foreach (r in roads) { + ASSERT_FALSE(r.has_double_slopes()) + } +} diff --git a/tests/tests/test_way_tunnel.nut b/tests/tests/test_way_tunnel.nut new file mode 100644 index 00000000000..e907ae39fd9 --- /dev/null +++ b/tests/tests/test_way_tunnel.nut @@ -0,0 +1,455 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for building and removal of tunnels +// + + +function test_way_tunnel_build_straight() +{ + local digger = command_x(tool_build_tunnel) + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local remover = command_x(tool_remover) + local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0] + local pl = player_x(0) + + ASSERT_TRUE(default_tunnel != null) + + { + ASSERT_EQUAL(raise.work(pl, coord3d(3, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 2, 0)), null) + + digger.set_flags(2) + ASSERT_EQUAL(digger.work(pl, tile_x(3, 1, 0), default_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...0....", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(digger.work(pl, tile_x(3, 2, 0), default_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...0....", + "...0....", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0)), null) + ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0)), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + digger.set_flags(0) + } + + { + ASSERT_EQUAL(raise.work(pl, coord3d(3, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(4, 3, 0)), null) + + ASSERT_EQUAL(digger.work(pl, tile_x(3, 1, 0), default_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "...5....", + "...1....", + "........", + "........", + "........", + "........" + ]) + } + + { + ASSERT_EQUAL(digger.work(pl, tile_x(2, 2, 0), default_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "..2D....", + "...1....", + "........", + "........", + "........", + "........" + ]) + + // building with ctrl + digger.set_flags(2) + ASSERT_EQUAL(digger.work(pl, tile_x(4, 2, 0), default_tunnel.get_name()), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "..2D0...", + "...1....", + "........", + "........", + "........", + "........" + ]) + + digger.set_flags(0) + + // remove the single tunnel entrance + ASSERT_EQUAL(remover.work(pl, coord3d(4, 2, 0)), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "..2D....", + "...1....", + "........", + "........", + "........", + "........" + ]) + + // remove tunnel network (more than 2 entrances) + // should fail without ctrl + local err = remover.work(pl, coord3d(3, 3, 0)) + ASSERT_EQUAL(err, "This tunnel branches. You can try Control+Click to remove.") + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "...4....", + "..2D....", + "...1....", + "........", + "........", + "........", + "........" + ]) + + remover.set_flags(2) // activate ctrl + ASSERT_EQUAL(remover.work(pl, coord3d(3, 3, 0)), null) + + ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0), + [ + "........", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + remover.set_flags(0) // deactivate ctrl + } + + // clean up + ASSERT_EQUAL(lower.work(pl, coord3d(3, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(3, 3, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(4, 2, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(4, 3, 0)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tunnel_build_up_down() +{ + local digger = command_x(tool_build_tunnel) + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + local setslope = command_x(tool_setslope) + local remover = command_x(tool_remover) + local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_rail)[0] + local pl = player_x(0) + + ASSERT_TRUE(default_tunnel != null) + + ASSERT_EQUAL(raise.work(pl, coord3d(1, 1, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(2, 1, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(1, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(2, 2, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(1, 3, 0)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(2, 3, 0)), null) + + digger.set_flags(2) // ctrl + ASSERT_EQUAL(digger.work(pl, coord3d(1, 0, 0), default_tunnel.get_name()), null) + ASSERT_EQUAL(digger.work(pl, coord3d(1, 0, 0), coord3d(1, 1, 0), default_tunnel.get_name()), null) + + + // invalid param + { + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "42"), "Only up and down movement in the underground!") + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), // no change + [ + ".4......", + ".1......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // Build up: Does not work: surface in the way + { + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "" + slope.all_up_slope), "Tile not empty.") + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), // no change + [ + ".4......", + ".1......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + } + + // Build down + { + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "" + slope.all_down_slope), null) + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + ".4......", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), + [ + "........", + ".1......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // try building duble slope down, rail does not support double slopes + { + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, -1), "" + slope.all_down_slope), "Tile not empty.") + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + ".4......", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), + [ + "........", + ".1......", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + ASSERT_EQUAL(digger.work(pl, coord3d(1, 1, -1), coord3d(1, 2, -1), default_tunnel.get_name()), null) + + // Build up + { + local old_maint = pl.get_current_maintenance() + + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 2, -1), "" + slope.all_up_slope), null) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + ".4......", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), + [ + "........", + ".5......", + ".1......", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + } + + // try building double slope up, rail does not support double slopes + { + ASSERT_EQUAL(raise.work(pl, coord3d(1, 2, 1)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(2, 2, 1)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(1, 3, 1)), null) + ASSERT_EQUAL(raise.work(pl, coord3d(2, 3, 1)), null) + + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(setslope.work(pl, coord3d(1, 1, 0), "" + slope.all_up_slope), "") + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, 0), + [ + ".4......", + "........", + "........", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_WAY_PATTERN(wt_rail, coord3d(0, 0, -1), + [ + "........", + ".5......", + ".1......", + "........", + "........", + "........", + "........", + "........" + ]) + + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + + ASSERT_EQUAL(lower.work(pl, coord3d(1, 2, 2)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(2, 2, 2)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(1, 3, 2)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(2, 3, 2)), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(1, 0, 0)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(1, 1, 1)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(2, 1, 1)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(1, 2, 1)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(2, 2, 1)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(1, 3, 1)), null) + ASSERT_EQUAL(lower.work(pl, coord3d(2, 3, 1)), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_way_tunnel_make_public() +{ + local pl = player_x(0) + local public_pl = player_x(1) + local tunnel_desc = tunnel_desc_x.get_available_tunnels(wt_road)[0] + local makepublic = command_x(tool_make_stop_public) + local raise = command_x(tool_raise_land) + local lower = command_x(tool_lower_land) + + ASSERT_EQUAL(raise.work(public_pl, coord3d(4, 3, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(5, 3, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(4, 4, 0)), null) + ASSERT_EQUAL(raise.work(public_pl, coord3d(5, 4, 0)), null) + + ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(4, 2, 0), tunnel_desc.get_name()), null) + + // make tunnel portal public + { + local old_pl_cash = pl.get_current_cash() + local old_pl_maint = pl.get_current_maintenance() + local old_public_cash = public_pl.get_current_cash() + local old_public_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 2, 0)), null) + + ASSERT_TRUE(way_x(4, 2, 0).get_owner() != null) + ASSERT_EQUAL(way_x(4, 2, 0).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * tunnel_desc.get_maintenance()) // 60 == cst_make_public_months + ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - tunnel_desc.get_maintenance()) + + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + tunnel_desc.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) + } + + // make tunnel inside public + { + local old_pl_cash = pl.get_current_cash() + local old_pl_maint = pl.get_current_maintenance() + local old_public_cash = public_pl.get_current_cash() + local old_public_maint = public_pl.get_current_maintenance() + + ASSERT_EQUAL(makepublic.work(pl, coord3d(4, 3, 0)), null) + + ASSERT_TRUE(way_x(4, 3, 0).get_owner() != null) + ASSERT_EQUAL(way_x(4, 3, 0).get_owner().get_name(), public_pl.get_name()) + + ASSERT_EQUAL(pl.get_current_cash()*100, old_pl_cash*100 - 60 * tunnel_desc.get_maintenance()) // 60 == cst_make_public_months + ASSERT_EQUAL(pl.get_current_maintenance(), old_pl_maint - tunnel_desc.get_maintenance()) + + ASSERT_EQUAL(public_pl.get_current_maintenance(), old_public_maint + tunnel_desc.get_maintenance()) + ASSERT_EQUAL(public_pl.get_current_cash(), old_public_cash) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(4, 2, 0), coord3d(4, 4, 0), "" + wt_road), null) + + ASSERT_EQUAL(lower.work(public_pl, coord3d(4, 3, 1)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(5, 3, 1)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(4, 4, 1)), null) + ASSERT_EQUAL(lower.work(public_pl, coord3d(5, 4, 1)), null) + RESET_ALL_PLAYER_FUNDS() +} diff --git a/tests/tests/test_wayobj.nut b/tests/tests/test_wayobj.nut new file mode 100644 index 00000000000..51b9edeecf8 --- /dev/null +++ b/tests/tests/test_wayobj.nut @@ -0,0 +1,274 @@ +// +// This file is part of the Simutrans-Extended project under the Artistic License. +// (see LICENSE.txt) +// + + +// +// Tests for wayobj +// + + +function test_wayobj_build_straight() +{ + local pl = player_x(0) + local remover = command_x(tool_remove_wayobj) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local overhead_line = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line())[0] + ASSERT_TRUE(overhead_line != null) + + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(2, 1, 0), overhead_line), "") + ASSERT_EQUAL(tile_x(2, 1, 0).find_object(mo_wayobj), null) + + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 1, 0), coord3d(2, 1, 0), overhead_line), "") + ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_wayobj), null) + ASSERT_EQUAL(tile_x(2, 1, 0).find_object(mo_wayobj), null) + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), rail, true), null) + + // now build wayobj + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(2, 1, 0), overhead_line), null) + local wo = tile_x(2, 1, 0).find_object(mo_wayobj) + ASSERT_TRUE(wo != null) + ASSERT_TRUE(wo.is_valid()) + } + + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 1, 0), coord3d(4, 1, 0), overhead_line), null) + local wo = tile_x(3, 1, 0).find_object(mo_wayobj) + ASSERT_TRUE(wo != null) + ASSERT_TRUE(wo.is_valid()) + wo = tile_x(4, 1, 0).find_object(mo_wayobj) + ASSERT_TRUE(wo != null) + ASSERT_TRUE(wo.is_valid()) + + // todo: check for wayobj ribis ? + } + + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(4, 1, 0), overhead_line), null) + local wo = tile_x(3, 1, 0).find_object(mo_wayobj) + ASSERT_TRUE(wo != null) + ASSERT_TRUE(wo.is_valid()) + } + + + // remove wayobj + { + ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(2, 2, 0), "" + wt_rail), "") + ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(3, 1, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(2, 1, 0), coord3d(3, 1, 0), "" + wt_rail), null) // returns success even if nothing was removed + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 1, 0), "" + wt_rail), null) + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 1, 0), "" + wt_rail), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_wayobj_build_disconnected() +{ + local pl = player_x(0) + local remover = command_x(tool_remove_wayobj) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local overhead_line = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line())[0] + ASSERT_TRUE(overhead_line != null) + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 5, 0), coord3d(5, 5, 0), rail, true), null) + + // build disconnected + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 5, 0), coord3d(3, 1, 0), overhead_line), "Ways not connected") + ASSERT_EQUAL(tile_x(3, 5, 0).find_object(mo_wayobj), null) + ASSERT_EQUAL(tile_x(3, 1, 0).find_object(mo_wayobj), null) + } + + // remove disconnected wayobj + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 1, 0), coord3d(5, 1, 0), overhead_line), null) + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(2, 5, 0), coord3d(5, 5, 0), overhead_line), null) + + ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 5, 0), "" + wt_rail), "Ways not connected") + ASSERT_TRUE(tile_x(3, 1, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(3, 5, 0).find_object(mo_wayobj) != null) + } + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(2, 1, 0), coord3d(2, 5, 0), rail, true), null) + ASSERT_EQUAL(command_x.build_way(pl, coord3d(5, 1, 0), coord3d(5, 5, 0), rail, true), null) + + { + ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0), coord3d(3, 5, 0), "" + wt_rail), null) + ASSERT_TRUE(tile_x(3, 1, 0).find_object(mo_wayobj) == null) + ASSERT_TRUE(tile_x(3, 5, 0).find_object(mo_wayobj) == null) + ASSERT_TRUE(tile_x(4, 1, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 5, 0).find_object(mo_wayobj) != null) + } + + // clean up + ASSERT_EQUAL(remover.work(pl, coord3d(4, 1, 0), coord3d(4, 5, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 5, 0), "" + wt_rail), null) + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(2, 1, 0), coord3d(5, 5, 0), "" + wt_rail), null) + + RESET_ALL_PLAYER_FUNDS() +} + + +function test_wayobj_upgrade_downgrade() +{ + local pl = player_x(0) + local wobj_remover = command_x(tool_remove_wayobj) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local catenaries = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line()) + catenaries.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + + // FIXME need at least 2 catenaries + ASSERT_TRUE(catenaries.len() >= 2) + local slow_cat = catenaries[0] + local fast_cat = catenaries[1] + + ASSERT_EQUAL(command_x.build_way(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), rail, true), null) + + // upgrade wayobj + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + + ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + } + + // upgrade wayobj to same type, should incur no cost + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(slow_cat)) + + ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + } + + // upgrade wayobj to faster type + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) + ASSERT_TRUE(pl.get_current_cash() < old_cash) + ASSERT_TRUE(pl.get_current_maintenance() > old_maint) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + + ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + } + + // downgrade wayobj without ctrl, should fail + { + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat), null) + local old_cash = pl.get_current_cash() + local old_maint = pl.get_current_maintenance() + ASSERT_EQUAL(command_x.build_wayobj(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat), null) + ASSERT_EQUAL(pl.get_current_cash(), old_cash) + ASSERT_EQUAL(pl.get_current_maintenance(), old_maint) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + + ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + } + + // downgrade wayobj with ctrl, should succeed + { + local builder = command_x(tool_build_wayobj) + builder.set_flags(2) // enable ctrl + ASSERT_EQUAL(builder.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), fast_cat.get_name()), null) + ASSERT_EQUAL(builder.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), slow_cat.get_name()), null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj) != null) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj) != null) + + ASSERT_TRUE(tile_x(3, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(4, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + ASSERT_TRUE(tile_x(5, 4, 0).find_object(mo_wayobj).get_desc().is_equal(fast_cat)) + + ASSERT_EQUAL(wobj_remover.work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + } + + // clean up + ASSERT_EQUAL(command_x(tool_remove_way).work(pl, coord3d(3, 4, 0), coord3d(5, 4, 0), "" + wt_rail), null) + RESET_ALL_PLAYER_FUNDS() +} + + +function test_wayobj_electrify_depot() +{ + local pl = player_x(0) + local start_pos = coord3d(3, 4, 0) + local end_pos = coord3d(5, 4, 0) + local wayremover = command_x(tool_remove_way) + local wobj_remover = command_x(tool_remove_wayobj) + local rail = way_desc_x.get_available_ways(wt_rail, st_flat)[0] + local catenaries = wayobj_desc_x.get_available_wayobjs(wt_rail).filter(@(idx, wobj) wobj.is_overhead_line()) + catenaries.sort(@(a, b) a.get_topspeed() <=> b.get_topspeed()) + + local slow_cat = catenaries[0] + local fast_cat = catenaries[1] + local depot = get_depot_by_wt(wt_rail) + + ASSERT_TRUE(slow_cat != null) + ASSERT_TRUE(fast_cat != null) + ASSERT_TRUE(depot != null) + + ASSERT_EQUAL(command_x.build_way(pl, start_pos, end_pos, rail, true), null) + ASSERT_EQUAL(command_x.build_depot(pl, start_pos, depot), null) + ASSERT_EQUAL(command_x.build_depot(pl, end_pos, depot), null) + + { + ASSERT_EQUAL(command_x.build_wayobj(pl, start_pos, end_pos, slow_cat), null) + ASSERT_TRUE(way_x(start_pos.x, start_pos.y, start_pos.z).is_electrified()) + ASSERT_TRUE(way_x(end_pos.x, end_pos.y, end_pos.z).is_electrified()) + + // TODO check that depot is really electrified + } + + // clean up + ASSERT_EQUAL(command_x(tool_remover).work(pl, start_pos, "" + mo_depot_rail), null) + ASSERT_EQUAL(command_x(tool_remover).work(pl, end_pos, "" + mo_depot_rail), null) + ASSERT_EQUAL(wayremover.work(pl, start_pos, end_pos, "" + wt_rail), null) // also removes way with wayobj + + RESET_ALL_PLAYER_FUNDS() +} diff --git a/themes.src/build_themes.sh b/themes.src/build_themes.sh index b3255015970..22a5417809e 100755 --- a/themes.src/build_themes.sh +++ b/themes.src/build_themes.sh @@ -1,7 +1,7 @@ #!/bin/bash # -# This file is part of the Simutrans project under the Artistic License. +# This file is part of the Simutrans-Extended project under the Artistic License. # (see LICENSE.txt) # diff --git a/tpl/array2d_tpl.h b/tpl/array2d_tpl.h index 7c41167eac1..a1f2200efbf 100644 --- a/tpl/array2d_tpl.h +++ b/tpl/array2d_tpl.h @@ -7,7 +7,9 @@ #define TPL_ARRAY2D_TPL_H -#include //for memcpy +#include +#include + #include "../dataobj/koord.h" #include "../simdebug.h" @@ -48,6 +50,13 @@ class array2d_tpl return h; } + void clear() + { + delete [] data; + data = 0; + w = h = 0; + } + void init( T value ) { if(sizeof(T)==1) { @@ -61,7 +70,7 @@ class array2d_tpl } } - // YOu will loose all informations in the array + // all informations in the array are lost void resize(unsigned resize_x, unsigned resize_y ) { if( w*h != resize_x*resize_y ) { @@ -73,6 +82,47 @@ class array2d_tpl h = resize_y; } + void rotate90() + { + if( w*h > 0 ) { + T *new_data = new T[w*h]; + for( unsigned y=0; y class array_tpl { - public: - typedef const T* const_iterator; - typedef T* iterator; +public: + typedef const T* const_iterator; + typedef T* iterator; - typedef uint32 index; + typedef uint32 index; - explicit array_tpl() : data(NULL), size(0) {} + explicit array_tpl() : data(NULL), size(0) {} - explicit array_tpl(index s) : data(new T[s]), size(s) {} + explicit array_tpl(index s) : data(new T[s]), size(s) {} - explicit array_tpl(index s, const T& value) : data(new T[s]), size(s) - { - for (index i = 0; i < size; i++) { - data[i] = value; - } + explicit array_tpl(index s, const T& value) : data(new T[s]), size(s) + { + for (index i = 0; i < size; i++) { + data[i] = value; } + } - ~array_tpl() { delete [] data; } + ~array_tpl() { delete [] data; } - index get_count() const { return size; } + index get_count() const { return size; } - bool empty() const { return size == 0; } + bool empty() const { return size == 0; } - void clear() - { - delete [] data; - data = 0; - size = 0; - } + void clear() + { + delete [] data; + data = 0; + size = 0; + } - void resize(index resize) - { - if (size < resize) { - // extend if needed - T* new_data = new T[resize]; - for (index i = 0; i < size; i++) { - new_data[i] = data[i]; - } - delete [] data; - data = new_data; + void resize(index resize) + { + if (size < resize) { + // extend if needed + T* new_data = new T[resize]; + for (index i = 0; i < size; i++) { + new_data[i] = data[i]; } - size = resize; + delete [] data; + data = new_data; } - - void resize(index resize, const T& value) - { - if (size < resize) { - T* new_data = new T[resize]; - index i; - for (i = 0; i < size; i++) { - new_data[i] = data[i]; - } - for (; i < resize; i++) { - new_data[i] = value; - } - delete [] data; - data = new_data; - size = resize; + size = resize; + } + + void resize(index resize, const T& value) + { + if (size < resize) { + T* new_data = new T[resize]; + index i; + for (i = 0; i < size; i++) { + new_data[i] = data[i]; } + for (; i < resize; i++) { + new_data[i] = value; + } + delete [] data; + data = new_data; + size = resize; } + } - T& operator [](index i) - { - if (i >= size) { - dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); - } - return data[i]; + T& operator [](index i) + { + if (i >= size) { + dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); } + return data[i]; + } - const T& operator [](index i) const - { - if (i >= size) { - dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); - } - return data[i]; + const T& operator [](index i) const + { + if (i >= size) { + dbg->fatal("array_tpl::[]", "index out of bounds: %d not in 0..%d, T=%s", i, size - 1, typeid(T).name()); } + return data[i]; + } - iterator begin() { return data; } - iterator end() { return data + size; } + iterator begin() { return data; } + iterator end() { return data + size; } - const_iterator begin() const { return data; } - const_iterator end() const { return data + size; } + const_iterator begin() const { return data; } + const_iterator end() const { return data + size; } - private: - array_tpl(const array_tpl&); - array_tpl& operator=( array_tpl const& other ); +private: + array_tpl(const array_tpl&); + array_tpl& operator=( array_tpl const& other ); - T* data; - index size; + T* data; + index size; }; #endif diff --git a/tpl/binary_heap_tpl.h b/tpl/binary_heap_tpl.h index d53b49b0596..e787eb09c11 100644 --- a/tpl/binary_heap_tpl.h +++ b/tpl/binary_heap_tpl.h @@ -54,10 +54,10 @@ class binary_heap_tpl } /** - * Inserts an element into the queue. - * in such a way that the lowest is at the top of this tree in an array - * parts inspired from OTTD - */ + * Inserts an element into the queue. + * in such a way that the lowest is at the top of this tree in an array + * parts inspired from OTTD + */ void insert(const T item) { node_count ++; @@ -83,8 +83,8 @@ class binary_heap_tpl /** - * unfortunately, the removing is as much effort as the insertion ... - */ + * unfortunately, the removing is as much effort as the insertion ... + */ T pop() { assert(!empty()); @@ -129,9 +129,9 @@ class binary_heap_tpl } /** - * Recycles all nodes. Doesn't delete the objects. - * Leaves the list empty. - */ + * Recycles all nodes. Doesn't delete the objects. + * Leaves the list empty. + */ void clear() { node_count = 0; @@ -152,6 +152,7 @@ class binary_heap_tpl return nodes[1]; } + private: binary_heap_tpl(const binary_heap_tpl& other); binary_heap_tpl& operator=( binary_heap_tpl const& other ); diff --git a/tpl/freelist_iter_tpl.h b/tpl/freelist_iter_tpl.h new file mode 100644 index 00000000000..0badba81944 --- /dev/null +++ b/tpl/freelist_iter_tpl.h @@ -0,0 +1,235 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef TPL_FREELIST_ITER_TPL_H +#define TPL_FREELIST_ITER_TPL_H + + +#include + +#include "../simmem.h" +#include + +#ifdef MULTI_THREADx +#include "../utils/simthread.h" +#endif + +// define USE_VALGRIND_MEMCHECK to make +// valgrind aware of the freelist memory pool +#ifdef USE_VALGRIND_MEMCHECK +#include +#endif + + +/** + * A template class for const sized memory pool + * Must be a static member! Does not use exceptions + */ +template class freelist_iter_tpl +{ +private: + struct nodelist_node_t + { +#ifdef DEBUG_FREELIST + unsigned magic : 15; + unsigned free : 1; +#endif + nodelist_node_t* next; + }; + + +#ifdef MULTI_THREADx + pthread_mutex_t freelist_mutex = PTHREAD_MUTEX_INITIALIZER;; +#endif + + // next free node (or NULL) + nodelist_node_t* freelist; + + // number of currently allocated node + size_t nodecount; + + // we aim for near 32 kB chunks, hoping that the system will allocate them on each page + // and they fit the L1 cache + const size_t new_chuck_size = (32250*8) / (sizeof(T)*8+1); + + struct chunklist_node_t { + chunklist_node_t *chunk_next; + // marking empty and allocated tiles for fast interation + std::bitset<(32250 * 8) / (sizeof(T) * 8 + 1)> allocated_mask; + }; + + // list of all allocated memory + chunklist_node_t* chunk_list; + + void change_obj(char *p,bool b) + { + char *c_list = (char *)chunk_list; + const size_t chunk_mem_size = sizeof(chunklist_node_t) + sizeof(T) * new_chuck_size; + while (c_list && (p continue + c_list = (char *)((chunklist_node_t *)c_list)->chunk_next; + } + // we have found us (or we crash on error) + size_t index = ((p - c_list) - sizeof(chunklist_node_t)) / sizeof(T); + assert(index < new_chuck_size); + ((chunklist_node_t*)c_list)->allocated_mask.set(index, b); + } + + // clears all list memories + void free_all_nodes() + { + while (chunk_list) { + chunklist_node_t* p = chunk_list; + chunk_list = chunk_list->chunk_next; + + // now release memory +#ifdef USE_VALGRIND_MEMCHECK + VALGRIND_DESTROY_MEMPOOL(p); +#endif + free(p); + } + freelist = 0; + chunk_list = 0; + nodecount = 0; + } + +public: + freelist_iter_tpl() : freelist(0), nodecount(0), chunk_list(0) {} + + void sync_step(uint32 delta_t) + { + chunklist_node_t* c_list = chunk_list; + while (c_list) { + T *p = (T *)(((char *)c_list)+sizeof(chunklist_node_t)); + for (unsigned i = 0; i < new_chuck_size; i++) { + if (c_list->allocated_mask.test(i)) { + // is active object + if (sync_result result = p[i].sync_step(delta_t)) { + // remove from sync + c_list->allocated_mask.set(i, false); + // and maybe delete + if (result == SYNC_DELETE) { + delete (p+i); + if (nodecount == 0) { + return; // since even the main chunk list became invalid + } + } + } + } + } + c_list = c_list->chunk_next; + } + } + + // switch on off sync handling + void add_sync(T* p) { change_obj((char*)p,true); }; + void remove_sync(T* p) { change_obj((char*)p,false); }; + + size_t get_nodecout() const { return nodecount; } + + void *gimme_node() + { +#ifdef MULTI_THREADx + pthread_mutex_lock(&freelist_mutex); +#endif + nodelist_node_t *tmp; + if (freelist == NULL) { + char* p = (char*)xmalloc(new_chuck_size *sizeof(T)+sizeof(chunklist_node_t)); + memset(p, 0, sizeof(chunklist_node_t)); // clear allocation bits and next pointer + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we still cannot access the pool p + VALGRIND_MAKE_MEM_NOACCESS(p, new_chuck_size * sizeof(T) + sizeof(chunklist_node_t)); +#endif + // put the memory into the chunklist for free it + chunklist_node_t* chunk = (chunklist_node_t *)p; + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we reserved space for one nodelist_node_t + VALGRIND_CREATE_MEMPOOL(chunk, 0, false); + VALGRIND_MEMPOOL_ALLOC(chunk, chunk, sizeof(*chunk)); + VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(*chunk)); +#endif + chunk->chunk_next = chunk_list; + chunk_list = chunk; + p += sizeof(chunklist_node_t); + // then enter nodes into nodelist + for (size_t i = 0; i < new_chuck_size; i++) { + nodelist_node_t* tmp = (nodelist_node_t*)(p + i * sizeof(T)); +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we reserved space for one nodelist_node_t + VALGRIND_CREATE_MEMPOOL(tmp, 0, false); + VALGRIND_MEMPOOL_ALLOC(tmp, tmp, sizeof(*tmp)); + VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(*tmp)); +#endif + tmp->next = freelist; + freelist = tmp; + } + } + + // return first node of list + tmp = freelist; + freelist = tmp->next; + + change_obj((char *)(&(tmp->next)),true); + +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we now have access to a chunk of size bytes + VALGRIND_MEMPOOL_CHANGE(tmp, tmp, tmp, sizeof(T)); + VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(T)); +#endif + +#ifdef DEBUG_FREELIST + tmp->magic = 0x5555; + tmp->free = 0; +#endif + nodecount++; + + return (void *)(&(tmp->next)); + } + + void putback_node(void* p) + { +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we keep access to a nodelist_node_t within the memory chunk + VALGRIND_MEMPOOL_CHANGE(p, p, p, sizeof(nodelist_node_t)); + VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(T)); + VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(nodelist_node_t)); +#endif + +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + + // putback to first node + nodelist_node_t* tmp = (nodelist_node_t*)p; +#ifdef DEBUG_FREELIST + tmp = (nodelist_node_t*)((char*)p - min_size); + assert(tmp->magic == 0x5555 && tmp->free == 0 && tmp->size == size / 4); + tmp->free = 1; +#endif + + tmp->next = freelist; + freelist = tmp; + + change_obj((char *)p,0); + nodecount--; + + if (nodecount == 0) { + free_all_nodes(); + } + +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + } + +}; + +#endif diff --git a/tpl/freelist_tpl.h b/tpl/freelist_tpl.h new file mode 100644 index 00000000000..faf65a5fe8f --- /dev/null +++ b/tpl/freelist_tpl.h @@ -0,0 +1,175 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef TPL_FREELIST_TPL_H +#define TPL_FREELIST_TPL_H + + +struct nodelist_node_t +{ +#ifdef DEBUG_FREELIST + unsigned magic : 15; + unsigned free : 1; +#endif + nodelist_node_t* next; +}; + +#include + +#include "../simmem.h" + +#ifdef MULTI_THREADx +#include "../utils/simthread.h" +#endif + +// define USE_VALGRIND_MEMCHECK to make +// valgrind aware of the freelist memory pool +#ifdef USE_VALGRIND_MEMCHECK +#include +#endif + +/** + * A template class for const sized memory pool + * Must be a static member! Does not use exceptions + */ +template class freelist_tpl +{ +private: + // next free node (or NULL) + nodelist_node_t* freelist; + + // number of currently allocated node + size_t nodecount; + + // list of all allocated memory + nodelist_node_t* chunk_list; + + const size_t new_chuck_size = (32768 - sizeof(void*)) / sizeof(T); + +#ifdef MULTI_THREADx + pthread_mutex_t freelist_mutex = PTHREAD_MUTEX_INITIALIZER;; +#endif + +public: + freelist_tpl() : freelist(0), nodecount(0), chunk_list(0) {} + + freelist_tpl(size_t ncs) : freelist(0), nodecount(0), chunk_list(0), new_chuck_size(ncs) {} + + void *gimme_node() + { +#ifdef MULTI_THREADx + pthread_mutex_lock(&freelist_mutex); +#endif + nodelist_node_t *tmp; + if (freelist == NULL) { + int chunksize = 0x1000; + char* p = (char*)xmalloc(chunksize *sizeof(T) + sizeof(nodelist_node_t)); + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we still cannot access the pool p + VALGRIND_MAKE_MEM_NOACCESS(p, chunksize *sizeof(T) + sizeof(nodelist_node_t)); +#endif + // put the memory into the chunklist for free it + nodelist_node_t* chunk = (nodelist_node_t *)p; + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we reserved space for one nodelist_node_t + VALGRIND_CREATE_MEMPOOL(chunk, 0, false); + VALGRIND_MEMPOOL_ALLOC(chunk, chunk, sizeof(*chunk)); + VALGRIND_MAKE_MEM_UNDEFINED(chunk, sizeof(*chunk)); +#endif + chunk->next = chunk_list; + chunk_list = chunk; + p += sizeof(nodelist_node_t); + // then enter nodes into nodelist + for (int i = 0; i < chunksize; i++) { + nodelist_node_t* tmp = (nodelist_node_t*)(p + i * sizeof(T)); +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we reserved space for one nodelist_node_t + VALGRIND_CREATE_MEMPOOL(tmp, 0, false); + VALGRIND_MEMPOOL_ALLOC(tmp, tmp, sizeof(*tmp)); + VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(*tmp)); +#endif + tmp->next = freelist; + freelist = tmp; + } + } + + // return first node of list + tmp = freelist; + freelist = tmp->next; + +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we now have access to a chunk of size bytes + VALGRIND_MEMPOOL_CHANGE(tmp, tmp, tmp, sizeof(T)); + VALGRIND_MAKE_MEM_UNDEFINED(tmp, sizeof(T)); +#endif + +#ifdef DEBUG_FREELIST + tmp->magic = 0x5555; + tmp->free = 0; +#endif + nodecount++; + + return (void *)(&(tmp->next)); + } + + // clears all list memories + void free_all_nodes() + { + while (chunk_list) { + nodelist_node_t* p = chunk_list; + chunk_list = chunk_list->next; + + // now release memory +#ifdef USE_VALGRIND_MEMCHECK + VALGRIND_DESTROY_MEMPOOL(p); +#endif + free(p); + } + freelist = 0; + nodecount = 0; + } + + void putback_node(void* p) + { +#ifdef USE_VALGRIND_MEMCHECK + // tell valgrind that we keep access to a nodelist_node_t within the memory chunk + VALGRIND_MEMPOOL_CHANGE(p, p, p, sizeof(nodelist_node_t)); + VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(T)); + VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(nodelist_node_t)); +#endif + +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + + // putback to first node + nodelist_node_t* tmp = (nodelist_node_t*)p; +#ifdef DEBUG_FREELIST + tmp = (nodelist_node_t*)((char*)p - min_size); + assert(tmp->magic == 0x5555 && tmp->free == 0 && tmp->size == size / 4); + tmp->free = 1; +#endif + tmp->next = freelist; + freelist = tmp; + + nodecount--; + + if (nodecount == 0) { + free_all_nodes(); + } +#ifdef MULTI_THREADx + pthread_mutex_unlock(&freelist_mutex); +#endif + } + +}; + +#endif diff --git a/tpl/hashtable_tpl.h b/tpl/hashtable_tpl.h index cbbf8169c8c..4615dc38f65 100644 --- a/tpl/hashtable_tpl.h +++ b/tpl/hashtable_tpl.h @@ -54,48 +54,48 @@ class hashtable_tpl class iterator { friend class hashtable_tpl; - public: - typedef std::forward_iterator_tag iterator_category; - typedef node_t value_type; - typedef ptrdiff_t difference_type; - typedef node_t* pointer; - typedef node_t& reference; - - iterator() : bag_i(), bag_end(), node_i() {} - - iterator(slist_tpl* const bag_i, slist_tpl* const bag_end, typename slist_tpl::iterator const& node_i) : - bag_i(bag_i), - bag_end(bag_end), - node_i(node_i) - {} - - pointer operator ->() const { return &*node_i; } - reference operator *() const { return *node_i; } - - iterator& operator ++() - { - if (++node_i == bag_i->end()) { - for (;;) { - if (++bag_i == bag_end) { - node_i = typename slist_tpl::iterator(); - break; - } - if (!bag_i->empty()) { - node_i = bag_i->begin(); - break; - } + public: + typedef std::forward_iterator_tag iterator_category; + typedef node_t value_type; + typedef ptrdiff_t difference_type; + typedef node_t* pointer; + typedef node_t& reference; + + iterator() : bag_i(), bag_end(), node_i() {} + + iterator(slist_tpl* const bag_i, slist_tpl* const bag_end, typename slist_tpl::iterator const& node_i) : + bag_i(bag_i), + bag_end(bag_end), + node_i(node_i) + {} + + pointer operator ->() const { return &*node_i; } + reference operator *() const { return *node_i; } + + iterator& operator ++() + { + if (++node_i == bag_i->end()) { + for (;;) { + if (++bag_i == bag_end) { + node_i = typename slist_tpl::iterator(); + break; + } + if (!bag_i->empty()) { + node_i = bag_i->begin(); + break; } } - return *this; } + return *this; + } - bool operator ==(iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } - bool operator !=(iterator const& o) const { return !(*this == o); } + bool operator ==(iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } + bool operator !=(iterator const& o) const { return !(*this == o); } - private: - slist_tpl* bag_i; - slist_tpl* bag_end; - typename slist_tpl::iterator node_i; + private: + slist_tpl* bag_i; + slist_tpl* bag_end; + typename slist_tpl::iterator node_i; }; /* Erase element at pos @@ -125,48 +125,48 @@ class hashtable_tpl class const_iterator { - public: - typedef std::forward_iterator_tag iterator_category; - typedef node_t value_type; - typedef ptrdiff_t difference_type; - typedef node_t const* pointer; - typedef node_t const& reference; - - const_iterator() : bag_i(), bag_end(), node_i() {} - - const_iterator(slist_tpl const* const bag_i, slist_tpl const* const bag_end, typename slist_tpl::const_iterator const& node_i) : - bag_i(bag_i), - bag_end(bag_end), - node_i(node_i) - {} - - pointer operator ->() const { return &*node_i; } - reference operator *() const { return *node_i; } - - const_iterator& operator ++() - { - if (++node_i == bag_i->end()) { - for (;;) { - if (++bag_i == bag_end) { - node_i = typename slist_tpl::const_iterator(); - break; - } - if (!bag_i->empty()) { - node_i = bag_i->begin(); - break; - } + public: + typedef std::forward_iterator_tag iterator_category; + typedef node_t value_type; + typedef ptrdiff_t difference_type; + typedef node_t const* pointer; + typedef node_t const& reference; + + const_iterator() : bag_i(), bag_end(), node_i() {} + + const_iterator(slist_tpl const* const bag_i, slist_tpl const* const bag_end, typename slist_tpl::const_iterator const& node_i) : + bag_i(bag_i), + bag_end(bag_end), + node_i(node_i) + {} + + pointer operator ->() const { return &*node_i; } + reference operator *() const { return *node_i; } + + const_iterator& operator ++() + { + if (++node_i == bag_i->end()) { + for (;;) { + if (++bag_i == bag_end) { + node_i = typename slist_tpl::const_iterator(); + break; + } + if (!bag_i->empty()) { + node_i = bag_i->begin(); + break; } } - return *this; } + return *this; + } - bool operator ==(const_iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } - bool operator !=(const_iterator const& o) const { return !(*this == o); } + bool operator ==(const_iterator const& o) const { return bag_i == o.bag_i && node_i == o.node_i; } + bool operator !=(const_iterator const& o) const { return !(*this == o); } - private: - slist_tpl const* bag_i; - slist_tpl const* bag_end; - typename slist_tpl::const_iterator node_i; + private: + slist_tpl const* bag_i; + slist_tpl const* bag_end; + typename slist_tpl::const_iterator node_i; }; iterator begin() @@ -212,7 +212,7 @@ class hashtable_tpl const value_t &get(const key_t key) const { static value_t nix; - FORT(slist_tpl, const& node, bags[get_hash(key)]) { + for(auto const& node : bags[get_hash(key)]) { typename hash_t::diff_type diff = hash_t::comp(node.key, key); if( diff == 0 ) { return node.value; @@ -230,7 +230,7 @@ class hashtable_tpl value_t *access(const key_t key) { slist_tpl& bag = bags[get_hash(key)]; - FORT(slist_tpl, & node, bag) { + for(auto & node : bag) { typename hash_t::diff_type diff = hash_t::comp(node.key, key); if( diff == 0 ) { return &node.value; diff --git a/tpl/piecewise_linear_tpl.h b/tpl/piecewise_linear_tpl.h index 00e4b3e7cc4..a6ebfc38508 100644 --- a/tpl/piecewise_linear_tpl.h +++ b/tpl/piecewise_linear_tpl.h @@ -1,7 +1,7 @@ /** * Copyright 2013 Nathanael Nerode * - * This file is licensed under the Artistic License as part of the Simutrans project. (See license.txt) + * This file is licensed under the Artistic License as part of the Simutrans-Extended project. (See license.txt) * This file is also licensed under the Artistic License 2.0 and the GNU General Public License 3.0. * * Implements a piecewise linear function diff --git a/tpl/slist_tpl.h b/tpl/slist_tpl.h index c7839f74335..6bde5dd9eed 100644 --- a/tpl/slist_tpl.h +++ b/tpl/slist_tpl.h @@ -54,41 +54,42 @@ class slist_tpl */ class iterator { - public: - typedef std::forward_iterator_tag iterator_category; - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef value_type* pointer; - typedef value_type& reference; - - iterator() : ptr(), pred() {} - - pointer operator ->() const { return &ptr->data; } - reference operator *() const { return ptr->data; } - - iterator& operator ++() - { - pred = ptr; - ptr = ptr->next; - return *this; - } + public: + typedef std::forward_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; - iterator operator ++(int) - { - iterator const old = *this; - ++*this; - return old; - } + iterator() : ptr(), pred() {} - bool operator ==(iterator const& o) const { return ptr == o.ptr; } - bool operator !=(iterator const& o) const { return ptr != o.ptr; } + pointer operator ->() const { return &ptr->data; } + reference operator *() const { return ptr->data; } - private: - iterator(node_t* ptr_, node_t* pred_) : ptr(ptr_), pred(pred_) {} + iterator& operator ++() + { + pred = ptr; + ptr = ptr->next; + return *this; + } + + iterator operator ++(int) + { + iterator const old = *this; + ++*this; + return old; + } - node_t* ptr; - node_t* pred; + bool operator ==(iterator const& o) const { return ptr == o.ptr; } + bool operator !=(iterator const& o) const { return ptr != o.ptr; } + private: + iterator(node_t* ptr_, node_t* pred_) : ptr(ptr_), pred(pred_) {} + + node_t* ptr; + node_t* pred; + + private: friend class slist_tpl; friend class const_iterator; }; @@ -99,37 +100,38 @@ class slist_tpl */ class const_iterator { - public: - typedef std::forward_iterator_tag iterator_category; - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef T const* pointer; - typedef T const& reference; + public: + typedef std::forward_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef T const* pointer; + typedef T const& reference; - const_iterator() : ptr() {} + const_iterator() : ptr() {} - const_iterator(const iterator& o) : ptr(o.ptr) {} + const_iterator(const iterator& o) : ptr(o.ptr) {} - pointer operator ->() const { return &ptr->data; } - reference operator *() const { return ptr->data; } + pointer operator ->() const { return &ptr->data; } + reference operator *() const { return ptr->data; } - const_iterator& operator ++() { ptr = ptr->next; return *this; } + const_iterator& operator ++() { ptr = ptr->next; return *this; } - const_iterator operator ++(int) - { - const_iterator const old = *this; - ++*this; - return old; - } + const_iterator operator ++(int) + { + const_iterator const old = *this; + ++*this; + return old; + } - bool operator ==(const_iterator const& o) const { return ptr == o.ptr; } - bool operator !=(const_iterator const& o) const { return ptr != o.ptr; } + bool operator ==(const_iterator const& o) const { return ptr == o.ptr; } + bool operator !=(const_iterator const& o) const { return ptr != o.ptr; } - private: - explicit const_iterator(node_t* ptr_) : ptr(ptr_) {} + private: + explicit const_iterator(node_t* ptr_) : ptr(ptr_) {} - const node_t* ptr; + const node_t* ptr; + private: friend class slist_tpl; }; diff --git a/tpl/sparse_tpl.h b/tpl/sparse_tpl.h index 24be62c9c4e..f6b1c4e7b47 100644 --- a/tpl/sparse_tpl.h +++ b/tpl/sparse_tpl.h @@ -20,231 +20,232 @@ template void swap(sparse_tpl& a, sparse_tpl& b); * It's using Compressed Row Storage (CRS). * @see array2d_tpl */ - template class sparse_tpl { - private: - koord size; - - T* data; - uint16* col_ind; - uint16* row_ptr; - - uint16 data_size; - uint16 data_count; - - public: - sparse_tpl( koord _size ) { - size = _size; - data_size = 0; - data_count = 0; - data = NULL; - col_ind = NULL; - row_ptr = new uint16[ size.y + 1]; - for( uint16 i = 0; i < size.y + 1; i++ ) { - row_ptr[i] = 0; - } +private: + koord size; + + T* data; + uint16* col_ind; + uint16* row_ptr; + + uint16 data_size; + uint16 data_count; + +public: + sparse_tpl( koord _size ) { + size = _size; + data_size = 0; + data_count = 0; + data = NULL; + col_ind = NULL; + row_ptr = new uint16[ size.y + 1]; + for( uint16 i = 0; i < size.y + 1; i++ ) { + row_ptr[i] = 0; } - - ~sparse_tpl() { - delete[] data; - data = NULL; - delete[] col_ind; - col_ind = NULL; - delete[] row_ptr; - row_ptr = NULL; + } + + ~sparse_tpl() { + delete[] data; + data = NULL; + delete[] col_ind; + col_ind = NULL; + delete[] row_ptr; + row_ptr = NULL; + } + + void clear() { + data_count = 0; + for( uint16 i = 0; i < size.y + 1; i++ ) { + row_ptr[i] = 0; } + resize_data(0); + } - void clear() { - data_count = 0; - for( uint16 i = 0; i < size.y + 1; i++ ) { - row_ptr[i] = 0; - } - resize_data(0); - } + const koord& get_size() const { + return size; + } - const koord& get_size() const { - return size; - } + uint16 get_data_size() const { + return data_size; + } - uint16 get_data_size() const { - return data_size; - } + uint16 get_data_count() const { + return data_count; + } - uint16 get_data_count() const { - return data_count; + T get( koord pos ) const { + if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { + return get_unsafe( pos ); } - - T get( koord pos ) const { - if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { - return get_unsafe( pos ); - } - return 0; + return 0; + } + + T get( uint16 i, uint16 j ) const { + return get(koord(i,j)); + } + + /* + * Access to the nonzero elements. Result is saved in pos and value! + */ + void get_nonzero( uint16 index, koord& pos, T& value ) const { + if( index > data_count ) { + pos = koord::invalid; + value = 0; + return; } - - T get( uint16 i, uint16 j ) const { - return get(koord(i,j)); + value = data[index]; + pos = koord(col_ind[index], 0); + while( row_ptr[ pos.y+1 ] <= index ) { + pos.y++; } + } - /* - * Access to the nonzero elements. Result is saved in pos and value! - */ - void get_nonzero( uint16 index, koord& pos, T& value ) const { - if( index > data_count ) { - pos = koord::invalid; - value = 0; - return; - } - value = data[index]; - pos = koord(col_ind[index], 0); - while( row_ptr[ pos.y+1 ] <= index ) { - pos.y++; - } + void set( koord pos, T value ) { + if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { + set_unsafe( pos, value ); } + } - void set( koord pos, T value ) { - if( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y ) { - set_unsafe( pos, value ); - } - } - - void set( uint16 i, uint16 j, T value ) { - set(koord(i,j),value); - } + void set( uint16 i, uint16 j, T value ) { + set(koord(i,j),value); + } - private: - T get_unsafe( koord pos ) const { - uint16 index = pos_to_index( pos ); - if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { - return data[index]; - } - else { - return 0; - } +private: + T get_unsafe( koord pos ) const { + uint16 index = pos_to_index( pos ); + if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { + return data[index]; } + else { + return 0; + } + } - void set_unsafe( koord pos, T value ) { - uint16 index = pos_to_index( pos ); - if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { - if( value == 0 ) { - move_data(index+1, data_count, -1); - for( uint16 i = pos.y+1; i < size.y+1; i++ ){ - row_ptr[i]--; - } - data_count--; - } - else { - data[index] = value; + void set_unsafe( koord pos, T value ) { + uint16 index = pos_to_index( pos ); + if( index < row_ptr[pos.y+1] && col_ind[index] == pos.x ) { + if( value == 0 ) { + move_data(index+1, data_count, -1); + for( uint16 i = pos.y+1; i < size.y+1; i++ ){ + row_ptr[i]--; } + data_count--; } else { - if( value == 0 ) { - // Don't add 0 to data! - return; - } - if( data_count == data_size ) { - resize_data(data_size==0 ? 1 : 2*data_size); - } - move_data(index, data_count, 1); data[index] = value; - col_ind[index] = pos.x; - for( uint16 i = pos.y+1; i < size.y+1; i++ ){ - row_ptr[i]++; - } - data_count++; } } - - /* - * Moves the elements data[start_index]..data[end_index-1] to - * data[start_index+offset]..data[end_index-1+offset] - */ - void move_data(uint16 start_index, uint16 end_index, sint8 offset) - { - uint16 num = end_index - start_index; - if( offset < 0 ) { - for( uint16 i=0; i 0 ) { - for( uint16 i=num ; i>0 ; i-- ){ - data[start_index+i+offset-1] = data[start_index+i-1]; - col_ind[start_index+i+offset-1] = col_ind[start_index+i-1]; - } - } - } - - void resize_data( uint16 new_size ) { - if( new_size < data_count || new_size == data_size ) { - // new_size is too small or equal our current size. + else { + if( value == 0 ) { + // Don't add 0 to data! return; } - - T* new_data; - uint16* new_col_ind; - if( new_size > 0 ) { - new_data = new T[new_size]; - new_col_ind = new uint16[new_size]; + if( data_count == data_size ) { + resize_data(data_size==0 ? 1 : 2*data_size); } - else - { - new_data = NULL; - new_col_ind = NULL; + move_data(index, data_count, 1); + data[index] = value; + col_ind[index] = pos.x; + for( uint16 i = pos.y+1; i < size.y+1; i++ ){ + row_ptr[i]++; } - if( data_size > 0 ) { - for( uint16 i = 0; i < data_count; i++ ) { - new_data[i] = data[i]; - new_col_ind[i] = col_ind[i]; - } - delete[] data; - delete[] col_ind; + data_count++; + } + } + + /* + * Moves the elements data[start_index]..data[end_index-1] to + * data[start_index+offset]..data[end_index-1+offset] + */ + void move_data(uint16 start_index, uint16 end_index, sint8 offset) + { + uint16 num = end_index - start_index; + if( offset < 0 ) { + for( uint16 i=0; i 0 ) { + for( uint16 i=num ; i>0 ; i-- ){ + data[start_index+i+offset-1] = data[start_index+i-1]; + col_ind[start_index+i+offset-1] = col_ind[start_index+i-1]; } - data_size = new_size; - data = new_data; - col_ind = new_col_ind; + } + } + + void resize_data( uint16 new_size ) { + if( new_size < data_count || new_size == data_size ) { + // new_size is too small or equal our current size. + return; } - /* - * Returns an index i - * - to the pos (if pos already in array) - * - to the index, where pos can be inserted. - */ - uint16 pos_to_index( koord pos ) const { - uint16 row_start = row_ptr[ pos.y ]; - uint16 row_end = row_ptr[ pos.y + 1 ]; - if (row_start >= row_end || col_ind[row_end-1] < pos.x) return row_end; - if (col_ind[row_start]>=pos.x) return row_start; - - do { + T* new_data; + uint16* new_col_ind; + if( new_size > 0 ) { + new_data = new T[new_size]; + new_col_ind = new uint16[new_size]; + } + else + { + new_data = NULL; + new_col_ind = NULL; + } + if( data_size > 0 ) { + for( uint16 i = 0; i < data_count; i++ ) { + new_data[i] = data[i]; + new_col_ind[i] = col_ind[i]; + } + delete[] data; + delete[] col_ind; + } + data_size = new_size; + data = new_data; + col_ind = new_col_ind; + } + + /* + * Returns an index i + * - to the pos (if pos already in array) + * - to the index, where pos can be inserted. + */ + uint16 pos_to_index( koord pos ) const { + uint16 row_start = row_ptr[ pos.y ]; + uint16 row_end = row_ptr[ pos.y + 1 ]; + if (row_start >= row_end || col_ind[row_end-1] < pos.x) return row_end; + if (col_ind[row_start]>=pos.x) return row_start; + + do { uint16 i = (row_start + row_end) / 2; - if( col_ind[i] >= pos.x ) { - row_end = i; - } - else { - row_start = i; - } - } while (row_end > row_start + 1); + if( col_ind[i] >= pos.x ) { + row_end = i; + } + else { + row_start = i; + } + } while (row_end > row_start + 1); - /*for( uint16 i = row_start; i < row_end; i++ ) { - if( col_ind[i] >= pos.x ) { + /*for( uint16 i = row_start; i < row_end; i++ ) { + if( col_ind[i] >= pos.x ) { - return i; - } - }*/ - return row_end; - } + return i; + } + }*/ + return row_end; + } - friend void ::swap<>(sparse_tpl& a, sparse_tpl& b); +private: + friend void ::swap<>(sparse_tpl& a, sparse_tpl& b); - sparse_tpl(const sparse_tpl& other); - sparse_tpl& operator=(sparse_tpl const& other); + sparse_tpl(const sparse_tpl& other); + sparse_tpl& operator=(sparse_tpl const& other); }; + template void swap(sparse_tpl& a, sparse_tpl& b) { sim::swap(a.size, b.size); diff --git a/tpl/test_piecewise_linear_tpl.cc b/tpl/test_piecewise_linear_tpl.cc index 3e3770e1030..0c03a23a507 100644 --- a/tpl/test_piecewise_linear_tpl.cc +++ b/tpl/test_piecewise_linear_tpl.cc @@ -1,6 +1,6 @@ /** * Copyright 2013 Nathanael Nerode - * This file is licensed under the Artistic License as part of the Simutrans project. (See license.txt) + * This file is licensed under the Artistic License as part of the Simutrans-Extended project. (See license.txt) * This file is also licensed under the Artistic License 2.0 and the GNU General Public License 3.0. * * Unit Test for piecewise_linear_tpl.h diff --git a/tpl/vector_tpl.h b/tpl/vector_tpl.h index 8ff8566601a..c4a08036815 100644 --- a/tpl/vector_tpl.h +++ b/tpl/vector_tpl.h @@ -27,305 +27,306 @@ template inline void swap(vector_tpl& a, vector_tpl& b); /** A template class for a simple vector type */ template class vector_tpl { - public: - typedef const T* const_iterator; - typedef T* iterator; - - /** Construct a vector for cap elements */ - vector_tpl() : data(NULL), size(0), count(0) {} - - explicit vector_tpl(const uint32 cap) : - data(cap > 0 ? new T[cap] : NULL), - size(cap), - count(0) {} - - vector_tpl(const vector_tpl& copy_from) : - data( copy_from.get_size() > 0 ? new T[ copy_from.get_size() ] : 0 ), - size( copy_from.get_size() ), - count( copy_from.get_count() ) { - for( uint32 i = 0; i < count; i++ ) { - data[i] = copy_from.data[i]; - } - } - - vector_tpl& operator=( vector_tpl const& other ) { - vector_tpl tmp(other); - swap(tmp, *this); - return *this; +public: + typedef const T* const_iterator; + typedef T* iterator; + + /** Construct a vector for cap elements */ + vector_tpl() : data(NULL), size(0), count(0) {} + + explicit vector_tpl(const uint32 cap) : + data(cap > 0 ? new T[cap] : NULL), + size(cap), + count(0) {} + + vector_tpl(const vector_tpl& copy_from) : + data( copy_from.get_size() > 0 ? new T[ copy_from.get_size() ] : 0 ), + size( copy_from.get_size() ), + count( copy_from.get_count() ) { + for( uint32 i = 0; i < count; i++ ) { + data[i] = copy_from.data[i]; } + } - ~vector_tpl() { delete [] data; } + vector_tpl& operator=( vector_tpl const& other ) { + vector_tpl tmp(other); + swap(tmp, *this); + return *this; + } - /** sets the vector to empty */ - void clear() { count = 0; } + ~vector_tpl() { delete [] data; } - /** - * Resizes the maximum data that can be hold by this vector. - * Existing entries are preserved, new_size must be big enough to hold them - */ - void resize(const uint32 new_size) - { - if (new_size <= size) return; // do nothing - - T* new_data = new T[new_size]; - if(size>0) { - for (uint32 i = 0; i < count; i++) { - new_data[i] = data[i]; - } - delete [] data; - } - size = new_size; - data = new_data; - } + /** sets the vector to empty */ + void clear() { count = 0; } - /** - * Checks if element elem is contained in vector. - * Uses the == operator for comparison. - */ - bool is_contained(const T& elem) const - { + /** + * Resizes the maximum data that can be hold by this vector. + * Existing entries are preserved, new_size must be big enough to hold them + */ + void resize(const uint32 new_size) + { + if (new_size <= size) return; // do nothing + + T* new_data = new T[new_size]; + if(size>0) { for (uint32 i = 0; i < count; i++) { - if (data[i] == elem) { - return true; - } + new_data[i] = data[i]; } - return false; + delete [] data; } + size = new_size; + data = new_data; + } - /** - * Checks if element elem is contained in vector. - * Uses the == operator for comparison. - */ - uint32 index_of(const T& elem) const - { - for (uint32 i = 0; i < count; i++) { - if (data[i] == elem) { - return i; - } + /** + * Checks if element elem is contained in vector. + * Uses the == operator for comparison. + */ + bool is_contained(const T& elem) const + { + for (uint32 i = 0; i < count; i++) { + if (data[i] == elem) { + return true; } - //assert(false); - return 0xFFFFFFFFu; } + return false; + } - void append(const T& elem) - { - if( count == size ) { - resize(size == 0 ? 1 : size * 2); + /** + * Checks if element elem is contained in vector. + * Uses the == operator for comparison. + */ + uint32 index_of(const T& elem) const + { + for (uint32 i = 0; i < count; i++) { + if (data[i] == elem) { + return i; } - data[count++] = elem; } + //assert(false); + return 0xFFFFFFFFu; + } - /** - * Checks if element is contained. Appends only new elements. - * extend vector if necessary - */ - bool append_unique(const T& elem) - { - if (is_contained(elem)) { - return false; + void append(const T& elem) + { + if( count == size ) { + resize(size == 0 ? 1 : size * 2); + } + data[count++] = elem; + } + + /** + * Checks if element is contained. Appends only new elements. + * extend vector if necessary + */ + bool append_unique(const T& elem) + { + if (is_contained(elem)) { + return false; + } + append(elem); + return true; + } + + /** insert data at a certain pos */ + void insert_at(const uint32 pos, const T& elem) + { + if (pos < count) { + if (count == size) { + resize(size == 0 ? 1 : size * 2); } + for (uint i = count; i > pos; i--) { + data[i] = data[i - 1]; + } + data[pos] = elem; + count++; + } + else { append(elem); - return true; } + } - /** insert data at a certain pos */ - void insert_at(const uint32 pos, const T& elem) - { - if (pos < count) { - if (count == size) { - resize(size == 0 ? 1 : size * 2); - } - for (uint i = count; i > pos; i--) { - data[i] = data[i - 1]; - } - data[pos] = elem; - count++; + /** + * Insert `elem' with respect to ordering. + */ + template + void insert_ordered(const T& elem, StrictWeakOrdering comp) + { + sint32 low = -1, high = count; + while( high - low>1 ) { + const sint32 mid = ((uint32) (low + high)) >> 1; + T &mid_elem = data[mid]; + if( comp(elem, mid_elem) ) { + high = mid; } else { - append(elem); + low = mid; } } + insert_at(high, elem); + } - /** - * Insert `elem' with respect to ordering. - */ - template - void insert_ordered(const T& elem, StrictWeakOrdering comp) - { - sint32 low = -1, high = count; - while( high - low>1 ) { - const sint32 mid = ((uint32) (low + high)) >> 1; - T &mid_elem = data[mid]; - if( comp(elem, mid_elem) ) { - high = mid; - } - else { - low = mid; - } + /** + * Only insert `elem' if not already contained in this vector. + * Respects the ordering and assumes the vector is ordered. + * Returns NULL if insertion is successful; + * otherwise return the address of the element in conflict + */ + template + T* insert_unique_ordered(const T& elem, StrictWeakOrdering comp) + { + sint32 low = -1, high = count; + while( high - low>1 ) { + const sint32 mid = ((uint32) (low + high)) >> 1; + T &mid_elem = data[mid]; + if( elem==mid_elem ) { + return &mid_elem; + } + else if( comp(elem, mid_elem) ) { + high = mid; + } + else { + low = mid; } - insert_at(high, elem); } + insert_at(high, elem); + return NULL; + } - /** - * Only insert `elem' if not already contained in this vector. - * Respects the ordering and assumes the vector is ordered. - * Returns NULL if insertion is successful; - * otherwise return the address of the element in conflict - */ - template - T* insert_unique_ordered(const T& elem, StrictWeakOrdering comp) - { - sint32 low = -1, high = count; - while( high - low>1 ) { - const sint32 mid = ((uint32) (low + high)) >> 1; - T &mid_elem = data[mid]; - if( elem==mid_elem ) { - return &mid_elem; - } - else if( comp(elem, mid_elem) ) { - high = mid; - } - else { - low = mid; - } - } - insert_at(high, elem); - return NULL; + /** + * Set length of vector. + * BEWARE: using this function will create default objects, depending on + * the type of the vector + */ + void set_count(const uint32 count) + { + if (count >= size) { + resize((count & 0xFFFFFFF8) + 8); } + this->count = count; + } - /** - * Set length of vector. - * BEWARE: using this function will create default objects, depending on - * the type of the vector - */ - void set_count(const uint32 count) - { - if (count >= size) { - resize((count & 0xFFFFFFF8) + 8); + /** + * Put the data at a certain position. + * Possibly resizes vector (and hence creates default objects). + * @param pos index + * @param elem this element will be copied + */ + void store_at(const uint32 pos, const T& elem) + { + if (pos >= size) { + uint32 new_size = size == 0 ? 1 : size * 2; + while (pos >= new_size) { + new_size *= 2; + new_size *= 2; } - this->count = count; + resize(new_size); + } + data[pos] = elem; + if (pos >= count) { + count = pos + 1; } + } - /** - * Put the data at a certain position. - * Possibly resizes vector (and hence creates default objects). - * @param pos index - * @param elem this element will be copied - */ - void store_at(const uint32 pos, const T& elem) - { - if (pos >= size) { - uint32 new_size = size == 0 ? 1 : size * 2; - while (pos >= new_size) { - new_size *= 2; - } - resize(new_size); - } - data[pos] = elem; - if (pos >= count) { - count = pos + 1; + /** removes element, if contained */ + bool remove(const T& elem) + { + for (uint32 i = 0; i < count; i++) { + if (data[i] == elem) { + remove_at(i); + return true; } } + return false; + } - - /** removes element, if contained */ - bool remove(const T& elem) + /** removes element at position */ + void remove_at(const uint32 pos, const bool preserve_order = true) + { + assert(pos0); - --count; - return data[count]; - } + T const& get_element(uint e) const + { + return (*this)[e]; + } - T& operator [](uint i) - { - if (i >= count) { - dbg->fatal("vector_tpl::[]", "%s: index out of bounds: %lu not in 0..%lu", typeid(T).name(), i, count - 1); - } - return data[i]; + T& pop_back() + { + assert(count>0); + --count; + return data[count]; + } + + T& operator [](uint i) + { + if (i >= count) { + dbg->fatal("vector_tpl::[]", "%s: index out of bounds: %lu not in 0..%lu", typeid(T).name(), i, count - 1); } + return data[i]; + } - const T& operator [](uint32 i) const - { - if (i >= count) { + const T& operator [](uint i) const + { + if (i >= count) { dbg->fatal("vector_tpl::[]", "%s: index out of bounds: %lu not in 0..%lu", typeid(T).name(), i, count - 1); - } - return data[i]; } + return data[i]; + } - T& front() const { return data[0]; } + T& front() const { return data[0]; } - T& back() const { return data[count - 1]; } + T& back() const { return data[count - 1]; } - iterator begin() { return data; } - iterator end() { return data + count; } - iterator erase(iterator i) { remove_at( i-data ); return i; } + iterator begin() { return data; } + iterator end() { return data + count; } + iterator erase(iterator i) { remove_at( i-data ); return i; } - const_iterator begin() const { return data; } - const_iterator end() const { return data + count; } + const_iterator begin() const { return data; } + const_iterator end() const { return data + count; } - iterator swap_erase(iterator pos) - { - if( pos == end() ) { - --count; - return pos; - } - else { - *pos = back(); - --count; - return pos; - } + iterator swap_erase(iterator pos) + { + if( pos == end() ) { + --count; + return pos; } + else { + *pos = back(); + --count; + return pos; + } + } - /** Get the number of elements in the vector */ - uint32 get_count() const { return count; } + /** Get the number of elements in the vector */ + uint32 get_count() const { return count; } - /** Get the capacity */ - uint32 get_size() const { return size; } + /** Get the capacity */ + uint32 get_size() const { return size; } - bool empty() const { return count == 0; } + bool empty() const { return count == 0; } - private: - T* data; - uint32 size; ///< Capacity - uint32 count; ///< Number of elements in vector +private: + T* data; + uint32 size; ///< Capacity + uint32 count; ///< Number of elements in vector friend void swap(vector_tpl& a, vector_tpl& b) { @@ -342,7 +343,7 @@ template class vector_tpl */ template void clear_ptr_vector(vector_tpl& v) { - FORT(vector_tpl, const i, v) { + for(auto const i : v) { delete i; } v.clear(); diff --git a/tpl/weighted_vector_tpl.h b/tpl/weighted_vector_tpl.h index b4f9ba83d84..15b18d63bea 100644 --- a/tpl/weighted_vector_tpl.h +++ b/tpl/weighted_vector_tpl.h @@ -31,432 +31,434 @@ template void swap(weighted_vector_tpl&, weighted_vector_tpl&); /** A template class for a simple vector type */ template class weighted_vector_tpl { - private: - struct nodestruct - { - T data; - uint32 weight; - }; - +private: + struct nodestruct + { + T data; + uint32 weight; + }; + +public: + class const_iterator; + + class iterator + { public: - class const_iterator; - - class iterator - { - public: - typedef std::forward_iterator_tag iterator_category; - typedef std::ptrdiff_t difference_type; - typedef T const* pointer; - typedef T const& reference; - typedef T value_type; - - T& operator *() const { return ptr->data; } - - iterator& operator ++() { ++ptr; return *this; } + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef T const* pointer; + typedef T const& reference; + typedef T value_type; - bool operator !=(const iterator& o) { return ptr != o.ptr; } + T& operator *() const { return ptr->data; } - private: - explicit iterator(nodestruct* ptr_) : ptr(ptr_) {} + iterator& operator ++() { ++ptr; return *this; } - nodestruct* ptr; + bool operator !=(const iterator& o) { return ptr != o.ptr; } - friend class weighted_vector_tpl; - friend class const_iterator; - }; - - class const_iterator - { - public: - typedef std::forward_iterator_tag iterator_category; - typedef std::ptrdiff_t difference_type; - typedef T const* pointer; - typedef T const& reference; - typedef T value_type; - - const_iterator(const iterator& o) : ptr(o.ptr) {} + private: + explicit iterator(nodestruct* ptr_) : ptr(ptr_) {} - const T& operator *() const { return ptr->data; } + nodestruct* ptr; - const_iterator& operator ++() { ++ptr; return *this; } + private: + friend class weighted_vector_tpl; + friend class const_iterator; + }; - bool operator !=(const const_iterator& o) { return ptr != o.ptr; } + class const_iterator + { + public: + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef T const* pointer; + typedef T const& reference; + typedef T value_type; - private: - explicit const_iterator(nodestruct* ptr_) : ptr(ptr_) {} + const_iterator(const iterator& o) : ptr(o.ptr) {} - const nodestruct* ptr; + const T& operator *() const { return ptr->data; } - friend class weighted_vector_tpl; - }; + const_iterator& operator ++() { ++ptr; return *this; } - weighted_vector_tpl() : nodes(NULL), size(0), count(0), total_weight(0) {} + bool operator !=(const const_iterator& o) { return ptr != o.ptr; } - /** Construct a vector for size elements */ - explicit weighted_vector_tpl(uint32 size) - { - this->size = size; - nodes = (size > 0 ? new nodestruct[size] : NULL); - count = 0; - total_weight = 0; - } + private: + explicit const_iterator(nodestruct* ptr_) : ptr(ptr_) {} - ~weighted_vector_tpl() { delete [] nodes; } + const nodestruct* ptr; - /** sets the vector to empty */ - void clear() - { - count = 0; - total_weight = 0; + private: + friend class weighted_vector_tpl; + }; + + weighted_vector_tpl() : nodes(NULL), size(0), count(0), total_weight(0) {} + + /** Construct a vector for size elements */ + explicit weighted_vector_tpl(uint32 size) + { + this->size = size; + nodes = (size > 0 ? new nodestruct[size] : NULL); + count = 0; + total_weight = 0; + } + + ~weighted_vector_tpl() { delete [] nodes; } + + /** sets the vector to empty */ + void clear() + { + count = 0; + total_weight = 0; + } + + /** + * Resizes the maximum data that can be hold by this vector. + * Existing entries are preserved, new_size must be big enough + * to hold them. + */ + void resize(uint32 new_size) + { + if (new_size <= size) return; // do nothing + + nodestruct* new_nodes = new nodestruct[new_size]; + for (uint32 i = 0; i < count; i++) new_nodes[i] = nodes[i]; + delete [] nodes; + size = new_size; + nodes = new_nodes; + } + + /** + * Checks if element elem is contained in vector. + * Uses the == operator for comparison. + */ + bool is_contained(T elem) const + { + for (uint32 i = 0; i < count; i++) { + if (nodes[i].data == elem) return true; } - - /** - * Resizes the maximum data that can be hold by this vector. - * Existing entries are preserved, new_size must be big enough - * to hold them. - */ - void resize(uint32 new_size) - { - if (new_size <= size) return; // do nothing - - nodestruct* new_nodes = new nodestruct[new_size]; - for (uint32 i = 0; i < count; i++) new_nodes[i] = nodes[i]; - delete [] nodes; - size = new_size; - nodes = new_nodes; + return false; + } + + /** + * Checks if element elem is contained in vector. + * Uses the == operator for comparison. + */ + template uint32 index_of(U elem) const + { + for (uint32 i = 0; i < count; i++) { + if (nodes[i].data == elem) return i; } - - /** - * Checks if element elem is contained in vector. - * Uses the == operator for comparison. - */ - bool is_contained(T elem) const - { - for (uint32 i = 0; i < count; i++) { - if (nodes[i].data == elem) return true; - } + dbg->fatal("weighted_vector_tpl::index_of()", "not contained" ); + } + + /** + * Appends the element at the end of the vector. + * Extend if necessary. + */ + bool append(T elem, uint32 weight) + { +#ifdef IGNORE_ZERO_WEIGHT + if (weight == 0) { + // ignore unused entries ... return false; } - - /** - * Checks if element elem is contained in vector. - * Uses the == operator for comparison. - */ - template uint32 index_of(U elem) const - { - for (uint32 i = 0; i < count; i++) { - if (nodes[i].data == elem) return i; - } - dbg->fatal("weighted_vector_tpl::index_of()", "not contained" ); +#endif + if( count == size ) { + resize(size == 0 ? 1 : size * 2); } - - /** - * Appends the element at the end of the vector. - * Extend if necessary. - */ - bool append(T elem, uint32 weight) - { + nodes[count].data = elem; + nodes[count].weight = total_weight; + count++; + total_weight += weight; + return true; + } + + /** + * Checks if element is contained. Appends only new elements. + */ + bool append_unique(T elem, uint32 weight) + { + return is_contained(elem) || append(elem, weight); + } + + /** inserts data at a certain pos */ + bool insert_at(uint32 pos, T elem, uint32 weight) + { #ifdef IGNORE_ZERO_WEIGHT - if (weight == 0) { - // ignore unused entries ... - return false; - } + if (weight == 0) { + // ignore unused entries ... + return false; + } #endif - if( count == size ) { + if (pos < count) { + if( count==size ) { resize(size == 0 ? 1 : size * 2); } - nodes[count].data = elem; - nodes[count].weight = total_weight; - count++; + for (uint32 i = count; i > pos; i--) { + nodes[i].data = nodes[i - 1].data; + nodes[i].weight = nodes[i - 1].weight + weight; + } + nodes[pos].data = elem; total_weight += weight; + count++; return true; } - - /** - * Checks if element is contained. Appends only new elements. - */ - bool append_unique(T elem, uint32 weight) - { - return is_contained(elem) || append(elem, weight); + else { + return append(elem, weight); } - - /** inserts data at a certain pos */ - bool insert_at(uint32 pos, T elem, uint32 weight) - { -#ifdef IGNORE_ZERO_WEIGHT - if (weight == 0) { - // ignore unused entries ... - return false; - } -#endif - if (pos < count) { - if( count==size ) { - resize(size == 0 ? 1 : size * 2); - } - for (uint32 i = count; i > pos; i--) { - nodes[i].data = nodes[i - 1].data; - nodes[i].weight = nodes[i - 1].weight + weight; - } - nodes[pos].data = elem; - total_weight += weight; - count++; - return true; - } - else { - return append(elem, weight); - } + } + + /** + * Insert `elem' with respect to ordering. + */ + template + void insert_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) + { + if( count==size ) { + resize(size == 0 ? 1 : size * 2); } - - /** - * Insert `elem' with respect to ordering. - */ - template - void insert_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) - { - if( count==size ) { - resize(size == 0 ? 1 : size * 2); + sint32 high = count, low = -1; + while( high-low>1 ) { + const sint32 mid = ((uint32)(high + low)) >> 1; + if( comp(elem, nodes[mid].data) ) { + high = mid; } - sint32 high = count, low = -1; - while( high-low>1 ) { - const sint32 mid = ((uint32)(high + low)) >> 1; - if( comp(elem, nodes[mid].data) ) { - high = mid; - } - else { - low = mid; - } + else { + low = mid; } - insert_at(high, elem, weight); } - - /** - * Only insert `elem' if not already contained in this vector. - * Respects the ordering and assumes the vector is ordered. - * Returns NULL if insertion is successful; - * Otherwise return the address of the element in conflict. - */ - template - T* insert_unique_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) - { - if( count==size ) { - resize(size == 0 ? 1 : size * 2); - } - sint32 high = count, low = -1; - while( high-low>1 ) { - const sint32 mid = ((uint32)(high + low)) >> 1; - T &mid_elem = nodes[mid].data; - if( elem==mid_elem ) { - return &mid_elem; - } - else if( comp(elem, mid_elem) ) { - high = mid; - } - else { - low = mid; - } - } - insert_at(high, elem, weight); - return NULL; + insert_at(high, elem, weight); + } + + /** + * Only insert `elem' if not already contained in this vector. + * Respects the ordering and assumes the vector is ordered. + * Returns NULL if insertion is successful; + * Otherwise return the address of the element in conflict. + */ + template + T* insert_unique_ordered(const T& elem, uint32 weight, StrictWeakOrdering comp) + { + if( count==size ) { + resize(size == 0 ? 1 : size * 2); } - - /** - * Update the weight of the element, if contained - * @author Knightly - */ - bool update(T elem, uint32 weight) - { - for( uint32 i=0; i 1) { + const sint32 mid = ((uint32)(high + low)) >> 1; + T &mid_elem = nodes[mid].data; + if (elem == mid_elem) { + return &mid_elem; } - return false; - } - - /** - * Update the weight of the element at the specified position - * @author Knightly - */ - bool update_at(uint32 pos, uint32 weight) - { - if( pos>=count ) { - return false; - } - const uint32 elem_weight = ( pos + 1 < count ? nodes[pos + 1].weight : total_weight ) - nodes[pos].weight; - if( weight>elem_weight ) { - const uint32 delta_weight = weight - elem_weight; - while( ++pos void update_weights(U& get_weight) - { - uint32 sum = 0; - for (nodestruct* i = nodes, * const end = i + count; i != end; ++i) { - i->weight = sum; - sum += get_weight(i->data); + insert_at(high, elem, weight); + return NULL; + } + + /** + * Update the weight of the element, if contained + * @author Knightly + */ + bool update(T elem, uint32 weight) + { + for( uint32 i=0; i=count ) { return false; } - - /** removes all copies of element, if contained */ - bool remove_all(T elem) - { - bool any_to_remove = false; - for (uint32 i = 0; i < count; i++) - { - if(nodes != NULL && nodes[i].data == elem) - { - any_to_remove = remove_at(i); - i--; - } + const uint32 elem_weight = ( pos + 1 < count ? nodes[pos + 1].weight : total_weight ) - nodes[pos].weight; + if( weight>elem_weight ) { + const uint32 delta_weight = weight - elem_weight; + while( ++pos= count) return false; - // get the change in the weight; must check, if it isn't the last element - const uint32 diff_weight = ( pos + 1 < count ? nodes[pos + 1].weight : total_weight ) - nodes[pos].weight; - for (uint32 i = pos; i < count - 1; i++) { - nodes[i].data = nodes[i + 1].data; - nodes[i].weight = nodes[i + 1].weight - diff_weight; + else if( weight0); - --count; - total_weight = nodes[count].weight; - return nodes[count].data; + return true; + } + + /** + * Update the weights of all elements. The new weight of each element is + * retrieved from get_weight(). + */ + template void update_weights(U& get_weight) + { + uint32 sum = 0; + for (nodestruct* i = nodes, * const end = i + count; i != end; ++i) { + i->weight = sum; + sum += get_weight(i->data); } + total_weight = sum; + } - T& operator [](uint32 i) + /** removes element, if contained */ + bool remove(T elem) + { + for (uint32 i = 0; i < count; i++) { - if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); - return nodes[i].data; + if (nodes != NULL && nodes[i].data == elem) return remove_at(i); } - - const T& operator [](uint32 i) const + return false; + } + + /** removes all copies of element, if contained */ + bool remove_all(T elem) + { + bool any_to_remove = false; + for (uint32 i = 0; i < count; i++) { - if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); - return nodes[i].data; + if(nodes != NULL && nodes[i].data == elem) + { + any_to_remove = remove_at(i); + i--; + } } - - T& front() { return nodes[0].data; } - - /** returns the weight at a position */ - uint32 weight_at(uint32 pos) const - { - return (pos < count) ? nodes[pos].weight : total_weight + 1; + return any_to_remove; + } + + /** removes element at position */ + bool remove_at(uint32 pos) + { + if (pos >= count) return false; + // get the change in the weight; must check, if it isn't the last element + const uint32 diff_weight = ( pos + 1 < count ? nodes[pos + 1].weight : total_weight ) - nodes[pos].weight; + for (uint32 i = pos; i < count - 1; i++) { + nodes[i].data = nodes[i + 1].data; + nodes[i].weight = nodes[i + 1].weight - diff_weight; + } + count--; + total_weight -= diff_weight; + return true; + } + + T& pop_back() + { + assert(count>0); + --count; + total_weight = nodes[count].weight; + return nodes[count].data; + } + + T& operator [](uint32 i) + { + if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); + return nodes[i].data; + } + + const T& operator [](uint32 i) const + { + if (i >= count) dbg->fatal("weighted_vector_tpl::get()", "index out of bounds: %i not in 0..%d", i, count - 1); + return nodes[i].data; + } + + T& front() { return nodes[0].data; } + + /** returns the weight at a position */ + uint32 weight_at(uint32 pos) const + { + return (pos < count) ? nodes[pos].weight : total_weight + 1; + } + + /** Accesses the element at position i by weight */ + T& at_weight(const uint32 target_weight) const + { + if (target_weight > total_weight) { + dbg->fatal("weighted_vector_tpl::at_weight()", "weight out of bounds: %i not in 0..%d", target_weight, total_weight); } - - /** Accesses the element at position i by weight */ - T& at_weight(const uint32 target_weight) const - { - if (target_weight > total_weight) { - dbg->fatal("weighted_vector_tpl::at_weight()", "weight out of bounds: %i not in 0..%d", target_weight, total_weight); - } #if 0 - // that is the main idea (but slower, the more entries are in the list) - uint32 pos; - uint32 current_weight = 0; - for (pos = 0; pos < count - 1 && current_weight + nodes[pos + 1].weight < target_weight; pos++) { - current_weight += nodes[pos + 1].weight; - } + // that is the main idea (but slower, the more entries are in the list) + uint32 pos; + uint32 current_weight = 0; + for (pos = 0; pos < count - 1 && current_weight + nodes[pos + 1].weight < target_weight; pos++) { + current_weight += nodes[pos + 1].weight; + } #else - // ... and that the much faster binary search - uint32 diff = 1; - sint8 counter = 0; - // now make sure, diff is 2^n - while (diff <= count) { - diff <<= 1; - counter++; - } - diff >>= 1; - - uint pos = diff; - - // now search - while (counter-- > 0) { - diff = (diff + 1) >> 1; - if (pos < count && weight_at(pos) <= target_weight) { - if (weight_at(pos + 1) > target_weight) break; - pos += diff; - } else { - pos -= diff; - } + // ... and that the much faster binary search + uint32 diff = 1; + sint8 counter = 0; + // now make sure, diff is 2^n + while (diff <= count) { + diff <<= 1; + counter++; + } + diff >>= 1; + + uint pos = diff; + + // now search + while (counter-- > 0) { + diff = (diff + 1) >> 1; + if (pos < count && weight_at(pos) <= target_weight) { + if (weight_at(pos + 1) > target_weight) break; + pos += diff; + } else { + pos -= diff; } -#endif - //DBG_DEBUG("at_weight()", "target_weight=%i found_weight=%i at pos %i", target_weight, weight_at(pos), pos); - return nodes[pos].data; } +#endif + //DBG_DEBUG("at_weight()", "target_weight=%i found_weight=%i at pos %i", target_weight, weight_at(pos), pos); + return nodes[pos].data; + } - /** Gets the number of elements in the vector */ - uint32 get_count() const { return count; } + /** Gets the number of elements in the vector */ + uint32 get_count() const { return count; } - /** Gets the capacity */ - uint32 get_size() const { return size; } + /** Gets the capacity */ + uint32 get_size() const { return size; } - /** Gets the total weight */ - uint32 get_sum_weight() const { return total_weight; } + /** Gets the total weight */ + uint32 get_sum_weight() const { return total_weight; } - bool empty() const { return count == 0; } + bool empty() const { return count == 0; } - iterator begin() { return iterator(nodes); } - iterator end() { return iterator(nodes + count); } + iterator begin() { return iterator(nodes); } + iterator end() { return iterator(nodes + count); } - const_iterator begin() const { return const_iterator(nodes); } - const_iterator end() const { return const_iterator(nodes + count); } + const_iterator begin() const { return const_iterator(nodes); } + const_iterator end() const { return const_iterator(nodes + count); } - private: - nodestruct* nodes; - uint32 size; ///< Capacity - uint32 count; ///< Number of elements in vector - uint32 total_weight; ///< Sum of all weights +private: + nodestruct* nodes; + uint32 size; ///< Capacity + uint32 count; ///< Number of elements in vector + uint32 total_weight; ///< Sum of all weights - weighted_vector_tpl(const weighted_vector_tpl& other); + weighted_vector_tpl(const weighted_vector_tpl& other); - weighted_vector_tpl& operator=( weighted_vector_tpl const& other ); + weighted_vector_tpl& operator=( weighted_vector_tpl const& other ); - friend void swap(weighted_vector_tpl&a, weighted_vector_tpl&b) - { - sim::swap(a.nodes, b.nodes); - sim::swap(a.size, b.size); - sim::swap(a.count, b.count); - sim::swap(a.total_weight, b.total_weight); - } + friend void swap(weighted_vector_tpl&a, weighted_vector_tpl&b) + { + sim::swap(a.nodes, b.nodes); + sim::swap(a.size, b.size); + sim::swap(a.count, b.count); + sim::swap(a.total_weight, b.total_weight); + } }; #endif diff --git a/unicode.cc b/unicode.cc index 80d841490c3..2325a61146a 100644 --- a/unicode.cc +++ b/unicode.cc @@ -109,11 +109,10 @@ size_t utf8_get_next_char(const utf8* text, size_t pos) sint32 utf8_get_prev_char(const utf8* text, sint32 pos) { -/* not needed, since the only position calling it, checks it too - if(pos==0) { + if (pos <= 0) { return 0; } -*/ + // go left one character // the bytes in a sequence have always the format 10xxxxxx, thus we use is_cont_char() do { @@ -123,6 +122,16 @@ sint32 utf8_get_prev_char(const utf8* text, sint32 pos) } +size_t utf8_get_next_char(const char* text, size_t pos) +{ + return utf8_get_next_char((const utf8*)text, pos); +} +sint32 utf8_get_prev_char(const char* text, sint32 pos) +{ + return utf8_get_prev_char((const utf8*)text, pos); +} + + int utf16_to_utf8(utf16 c, utf8* out) { if (c < 0x80) { diff --git a/unicode.h b/unicode.h index d3518fb783a..59a02ffa324 100644 --- a/unicode.h +++ b/unicode.h @@ -10,12 +10,6 @@ #include #include "simtypes.h" -// Unicode type large enough to hold every single possible Unicode code point. -typedef uint32 utf32; - -typedef unsigned char utf8; -typedef unsigned short utf16; - extern utf32 const UNICODE_NUL; // the bytes in a sequence have always the format 10xxxxxx @@ -68,8 +62,12 @@ class utf8_decoder_t utf8 const *get_position(); }; +// unicode save moving in strings size_t utf8_get_next_char(const utf8 *text, size_t pos); +size_t utf8_get_next_char(const char* text, size_t pos); + sint32 utf8_get_prev_char(const utf8 *text, sint32 pos); +sint32 utf8_get_prev_char(const char* text, sint32 pos); int utf16_to_utf8(utf16 unicode, utf8 *out); diff --git a/utils/cbuffer_t.cc b/utils/cbuffer_t.cc index 47f7afda6f9..9260e5b95c1 100644 --- a/utils/cbuffer_t.cc +++ b/utils/cbuffer_t.cc @@ -136,6 +136,56 @@ const char* cbuffer_t::get_str() const } +/** + * Checks the format specifier. + * No flags, precision modifiers, n, * are allowed. + * @param format points to percent sign + * @param position positional parameter (if larger than zero) + * @param mask format mask for checking ('?' in case of error) + * @returns whether specifier is correct + */ +static bool check_format_specifier(const char*& format, int& position, char& mask) +{ + mask = '?'; + // skip % + assert(*format == '%'); + format++; +// %[n$][flags][width][.precision][length]specifier + int n = atoi(format); + // skip numbers + while(*format && ('0'<=*format && *format<='9') ) format++; + // positional argument? + position = *format == '$' ? n : -1; + // skip $ + if (*format == '$') format++; + // no flags + // skip numbers (width) + while(*format && ('0'<=*format && *format<='9') ) format++; + // skip . + if (*format == '.') format++; + // no precision modifiers allowed + // skip numbers (precision) + while(*format && ('0'<=*format && *format<='9') ) format++; + // now the specifier + static const char* all_types = "cdiouxXeEfFgGaAsp%"; // no n + static const char* all_masks = "ciiiiiiffffffffsp%"; + if (*format) { + if (const char* type = strchr(all_types, *format)) { + mask = *(all_masks + (type-all_types)); + if (mask == '%') { + // previous char has to be % as well + if ( *(format-1) != '%') { + mask = '?'; + } + } + // skip specifier + format++; + } + } + return mask != '?'; +} + + /** * Parses string @p format, and puts type identifiers into @p typemask. * If an error occurs, an error message is printed into @p error. @@ -143,6 +193,7 @@ const char* cbuffer_t::get_str() const * If positional parameter %[n]$ is specified then all up to n have to be present in the string as well. * Treats all integer parameters %i %u %d etc the same. * Ignores positional width parameters as *[n]. + * Positional parameters start with 1. * * @param format format string * @param typemask pointer to array of length @p max_params @@ -152,60 +203,43 @@ const char* cbuffer_t::get_str() const static void get_format_mask(const char* format, char *typemask, int max_params, cbuffer_t &error) { MEMZERON(typemask, max_params); - uint16 found = 0; + // count found parameters + int found = 0; bool positional = false; while(format && *format) { - uint16 pos = found; // skip until percent sign while(*format && *format!='%') format++; if (*format == 0) { break; } - format++; - // read out position - const int i = atoi(format); - // skip numbers - while(*format && ('0'<=*format && *format<='9') ) format++; - - // check for correct positional argument - if (i>0 && i<=max_params) { - if (format && *format=='$') { - format ++; - if (found > 0 && !positional) { - goto err_mix_pos_nopos; - } - positional = true; - pos = i-1; + int pos; + char mask; + if (check_format_specifier(format, pos, mask)) { + // mixed positional / non-positional arguments? + if (found>0 && (positional ^ (pos > 0) )) { + goto err_mix_pos_nopos; } - else { - // width specified, eg %2i + // has positional parameters? + // allowed range 1 .. max_params + if (found == 0) { + positional = pos > 0; } - } - else { - if (found>0 && positional) { - goto err_mix_pos_nopos; + if (pos > max_params) { + error.printf("Positional parameter too large (pos = %d). ", pos); + return; } - } - // now skip until format specifier - static const char* all_types = "cCdDeEfFgGiIoOsSuUxXpPnN \t\n"; - static const char* all_masks = "cciiffffffiiiissiiiippnn "; - while(format && *format) { - if (const char* type = strchr(all_types, *format)) { - char mask = *(all_masks + (type-all_types)); - if (mask == ' ') { - // broken format string - } - else if (pos < max_params) { - // found valid format - if (pos >= max_params) - error.printf("Too many parameters or illegal position %d not in supported range 0..%d.", pos, max_params - 1); - typemask[pos] = mask; - found++; - } - format++; - break; + if (mask != '%') { // ignore %% + // no positional parameter + // positional parameter is 1-based, indexing is 0-based + int idx = pos > 0 ? pos-1 : found; + // found valid format + typemask[idx] = mask; + found++; } - format++; + } + else { + error.printf("Format specifier %d contains invalid characters. ", found); + return; } } // check whether positional parameters are continuous @@ -213,14 +247,60 @@ static void get_format_mask(const char* format, char *typemask, int max_params, for(uint16 i=0; iwarning("cbuffer_t::check_format_strings", "Broken master string '%s': %s", master, (const char*) buf); return false; } + // read out translated string get_format_mask(translated, translated_tm, lengthof(translated_tm), buf); + if (buf.len() > 0 && repaired_p) { + // try to repair + repair_format_string(translated, *repaired_p); + // and check again + buf.clear(); + get_format_mask(*repaired_p, translated_tm, lengthof(translated_tm), buf); + if (buf.len() == 0) { + // acceptable now + dbg->warning("cbuffer_t::check_format_strings", "Repaired broken translation string '%.15s' of '%s'.", translated, master); + } + } if (buf.len() > 0) { // broken translated string - dbg->warning("cbuffer_t::check_format_strings", "Broken translation string '%s': %s", translated, (const char*) buf); + dbg->warning("cbuffer_t::check_format_strings", "Broken translation string '%.15s' of '%s': %s", translated, master, (const char*) buf); return false; } // check for consistency for(uint i=0; (translated_tm[i]) && (iwarning("cbuffer_t::check_format_strings", "Translation string '%s' has more parameters than master string '%s'", translated, master); + dbg->warning("cbuffer_t::check_format_strings", "Translation string '%.15s' has more parameters than master string '%s'", translated, master); return false; - } - return true; } else if (master_tm[i]!=translated_tm[i]) { // wrong type - dbg->warning("cbuffer_t::check_format_strings", "Parameter %d in translation string '%s' of '%s' has to be of type '%%%c' instead of '%%%c', Typemasks: Master = %s vs Translated = %s", + dbg->warning("cbuffer_t::check_format_strings", "Parameter %d in translation string '%.15s' of '%s' has to be of type '%%%c' instead of '%%%c', Typemasks: Master = %s vs Translated = %s", i+1, translated, master, master_tm[i], translated_tm[i], master_tm,translated_tm); return false; } diff --git a/utils/cbuffer_t.h b/utils/cbuffer_t.h index 92c3aabff02..c31dd73ea26 100644 --- a/utils/cbuffer_t.h +++ b/utils/cbuffer_t.h @@ -112,7 +112,7 @@ class cbuffer_t operator const char *() const {return buf;} /// checks whether format specifiers in @p translated match those in @p master - static bool check_format_strings(const char* master, const char* translated); + static bool check_and_repair_format_strings(const char* master, const char* translated, char **repaired = NULL); }; #endif diff --git a/utils/checklist.cc b/utils/checklist.cc index dcc1e8979bb..d5d61204052 100644 --- a/utils/checklist.cc +++ b/utils/checklist.cc @@ -30,7 +30,6 @@ checklist_t::checklist_t() : } } - checklist_t::checklist_t(const uint32 &hash) : hash(hash), random_seed(0), diff --git a/utils/fetchopt.h b/utils/fetchopt.h index 24ff4350303..0c0aa204fe8 100644 --- a/utils/fetchopt.h +++ b/utils/fetchopt.h @@ -7,7 +7,7 @@ #define UTILS_FETCHOPT_H -/* +/** This class can be used to parse unix-style single character command line options. It is intended to provide a similar interface to the POSIX getopt() function. diff --git a/utils/log.cc b/utils/log.cc index 93aff2dde02..b0fe7d24271 100644 --- a/utils/log.cc +++ b/utils/log.cc @@ -344,8 +344,8 @@ void log_t::vmessage(const char *what, const char *who, const char *format, va_l { if(debuglevel >= LEVEL_ERROR) { va_list args2; - va_copy(args2, args); + if( log ) { /* only log when a log */ fprintf(log ,"%s: %s:\t", what, who); /* is already open */ vfprintf(log, format, args); diff --git a/utils/searchfolder.cc b/utils/searchfolder.cc index cfd3504b7ee..8643a68eec7 100644 --- a/utils/searchfolder.cc +++ b/utils/searchfolder.cc @@ -52,7 +52,7 @@ void searchfolder_t::add_entry(const std::string &path, const char *entry, const void searchfolder_t::clear_list() { - FOR(vector_tpl, const i, files) { + for(char* const i : files) { free(i); } files.clear(); @@ -198,7 +198,7 @@ std::string searchfolder_t::complete(const std::string &filepath, const std::str */ searchfolder_t::~searchfolder_t() { - FOR(vector_tpl, const i, files) { + for(char* const i : files) { free(i); } files.clear(); diff --git a/utils/searchfolder.h b/utils/searchfolder.h index c4cf2d32676..122111b0a03 100644 --- a/utils/searchfolder.h +++ b/utils/searchfolder.h @@ -14,7 +14,8 @@ /** * Searches a disk folder for files matching certain restrictions. */ -class searchfolder_t { +class searchfolder_t +{ public: ~searchfolder_t(); /** @@ -60,6 +61,7 @@ class searchfolder_t { * We store the result of the search on this list */ vector_tpl files; + /** * Adds one entry to the list. * @param path Qualified path to the directory of the entry. diff --git a/utils/sha1.h b/utils/sha1.h index 97bc0965745..c8391192c5c 100644 --- a/utils/sha1.h +++ b/utils/sha1.h @@ -27,6 +27,7 @@ #include "../simtypes.h" + class SHA1 { diff --git a/utils/simrandom.cc b/utils/simrandom.cc index d1506e28c4c..6c01cd74312 100644 --- a/utils/simrandom.cc +++ b/utils/simrandom.cc @@ -52,6 +52,7 @@ static void init_genrand(uint32 s) } } + /* generate N words at one time */ static void MTgenerate() { @@ -91,7 +92,6 @@ uint32 get_random_seed() } - /* generates a random number on [0,0xffffffff]-interval */ uint32 simrand_plain() { @@ -295,6 +295,7 @@ uint32 setsimrand(uint32 seed,uint32 ns) return old_noise_seed; } + static double int_noise(const sint32 x, const sint32 y) { uint32 n = (uint32)x + (uint32)y*101U + noise_seed; diff --git a/utils/simrandom.h b/utils/simrandom.h index a610c5a8fe3..c6c5e65fe0d 100644 --- a/utils/simrandom.h +++ b/utils/simrandom.h @@ -14,6 +14,7 @@ class loadsave_t; + uint32 get_random_seed(); uint32 setsimrand(uint32 seed, uint32 noise_seed); diff --git a/utils/simstring.h b/utils/simstring.h index 2094585000c..71f0e3f0a1e 100644 --- a/utils/simstring.h +++ b/utils/simstring.h @@ -42,7 +42,7 @@ char get_fraction_sep(); const char *get_large_money_string(); /** - * Set thousand exponent (a3=1000, 4=10000), used in money_to_string and + * Set thousand exponent (3=1000, 4=10000), used in money_to_string and * number_to_string */ void set_thousand_sep_exponent(int new_thousand_sep_exponent); diff --git a/vehicle/air_vehicle.cc b/vehicle/air_vehicle.cc index c7e85f67afc..88e040a577d 100644 --- a/vehicle/air_vehicle.cc +++ b/vehicle/air_vehicle.cc @@ -136,7 +136,7 @@ ribi_t::ribi air_vehicle_t::get_ribi(const grund_t *gr) const // how expensive to go here (for way search) -int air_vehicle_t::get_cost(const grund_t *gr, const sint32, koord) +int air_vehicle_t::get_cost(const grund_t *gr, const sint32, ribi_t::ribi) { // first favor faster ways const weg_t *w=gr->get_weg(air_wt); diff --git a/vehicle/air_vehicle.h b/vehicle/air_vehicle.h index 9128d69aa99..20665120a6f 100644 --- a/vehicle/air_vehicle.h +++ b/vehicle/air_vehicle.h @@ -117,7 +117,7 @@ class air_vehicle_t : public vehicle_t ribi_t::ribi get_ribi(const grund_t* ) const OVERRIDE; // how expensive to go here (for way search) - int get_cost(const grund_t *, const sint32, koord) OVERRIDE; + int get_cost(const grund_t *, const sint32 max_speed, ribi_t::ribi from) OVERRIDE; bool can_enter_tile(const grund_t *gr_next, sint32 &restart_speed, uint8) OVERRIDE; diff --git a/vehicle/movingobj.cc b/vehicle/movingobj.cc index bf17d8a3bb7..1f79555334b 100644 --- a/vehicle/movingobj.cc +++ b/vehicle/movingobj.cc @@ -87,7 +87,7 @@ const groundobj_desc_t *movingobj_t::random_movingobj_for_climate(climate cl) int weight = 0; - FOR(vector_tpl, const i, movingobj_typen) { + for(groundobj_desc_t const* const i : movingobj_typen) { if (i->is_allowed_climate(cl) ) { weight += i->get_distribution_weight(); } @@ -97,7 +97,7 @@ const groundobj_desc_t *movingobj_t::random_movingobj_for_climate(climate cl) if (weight > 0) { const int w=simrand(weight, "const groundobj_desc_t *movingobj_t::random_movingobj_for_climate"); weight = 0; - FOR(vector_tpl, const i, movingobj_typen) { + for(groundobj_desc_t const* const i : movingobj_typen) { if (i->is_allowed_climate(cl)) { weight += i->get_distribution_weight(); if(weight>=w) { diff --git a/vehicle/rail_vehicle.cc b/vehicle/rail_vehicle.cc index 3445097235a..4a2eec2349a 100644 --- a/vehicle/rail_vehicle.cc +++ b/vehicle/rail_vehicle.cc @@ -294,7 +294,7 @@ bool rail_vehicle_t::check_next_tile(const grund_t *bd) const // how expensive to go here (for way search) -int rail_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, koord from_pos) +int rail_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, ribi_t::ribi from) { // first favor faster ways const weg_t *w = gr->get_weg(get_waytype()); @@ -315,9 +315,8 @@ int rail_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, koord fr // effect of slope if( gr->get_weg_hang()!=0 ) { // check if the slope is upwards relative to the previous tile - from_pos -= gr->get_pos().get_2d(); // 125 hardcoded, see get_cost_upslope() - costs += 125 * slope_t::get_sloping_upwards( gr->get_weg_hang(), from_pos.x, from_pos.y ); + costs += 125 * get_sloping_upwards( gr->get_weg_hang(), from ); } // Strongly prefer routes for which the vehicle is not overweight. @@ -2933,10 +2932,8 @@ sint32 rail_vehicle_t::block_reserver(route_t *route, uint16 start_index, uint16 halthandle_t train_halt = haltestelle_t::get_halt(cnv->get_pos(), cnv->get_owner()); halthandle_t signal_halt; - FOR(slist_tpl, const g, signs) - { - if(signal_t* const signal = g->find()) - { + for(grund_t* const g : signs) { + if (signal_t* const signal = g->find()) { signal_halt = haltestelle_t::get_halt(g->get_pos(), cnv->get_owner()); if(((counter -- > 0 || (pre_signals.empty() && (!starting_at_signal || signs.get_count() == 1)) || (reached_end_of_loop && (early_platform_index == INVALID_INDEX || diff --git a/vehicle/rail_vehicle.h b/vehicle/rail_vehicle.h index be42227806b..b1603eca88d 100644 --- a/vehicle/rail_vehicle.h +++ b/vehicle/rail_vehicle.h @@ -35,7 +35,7 @@ class rail_vehicle_t : public vehicle_t route_t::route_result_t calc_route(koord3d start, koord3d ziel, sint32 max_speed, bool is_tall, route_t* route) OVERRIDE; // how expensive to go here (for way search) - int get_cost(const grund_t *, const sint32, koord) OVERRIDE; + int get_cost(const grund_t *, const sint32 max_speed, ribi_t::ribi from) OVERRIDE; uint32 get_cost_upslope() const OVERRIDE { return 75; } // Standard is 15 diff --git a/vehicle/road_vehicle.cc b/vehicle/road_vehicle.cc index a0636780924..5f6708ac0a5 100644 --- a/vehicle/road_vehicle.cc +++ b/vehicle/road_vehicle.cc @@ -194,7 +194,7 @@ bool road_vehicle_t::check_next_tile(const grund_t *bd) const // how expensive to go here (for way search) -int road_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, koord from_pos) +int road_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, ribi_t::ribi from) { // first favor faster ways const weg_t *w=gr->get_weg(road_wt); @@ -224,9 +224,8 @@ int road_vehicle_t::get_cost(const grund_t *gr, const sint32 max_speed, koord fr // effect of slope if( gr->get_weg_hang()!=0 ) { // check if the slope is upwards, relative to the previous tile - from_pos -= gr->get_pos().get_2d(); // 75 hardcoded, see get_cost_upslope() - costs += 75 * slope_t::get_sloping_upwards( gr->get_weg_hang(), from_pos.x, from_pos.y ); + costs += 75 * get_sloping_upwards( gr->get_weg_hang(), from ); } // It is now difficult to calculate here whether the vehicle is overweight, so do this in the route finder instead. diff --git a/vehicle/road_vehicle.h b/vehicle/road_vehicle.h index 70015692c19..b376d7adf28 100644 --- a/vehicle/road_vehicle.h +++ b/vehicle/road_vehicle.h @@ -48,7 +48,7 @@ class road_vehicle_t : public vehicle_t, public traffic_vehicle_t void set_convoi(convoi_t *c) OVERRIDE; // how expensive to go here (for way search) - int get_cost(const grund_t *, const sint32, koord) OVERRIDE; + int get_cost(const grund_t *, const sint32 max_speed, ribi_t::ribi from) OVERRIDE; virtual route_t::route_result_t calc_route(koord3d start, koord3d ziel, sint32 max_speed, bool is_tall, route_t* route) OVERRIDE; diff --git a/vehicle/simroadtraffic.cc b/vehicle/simroadtraffic.cc index d1c9026cc35..d7cbbe82e91 100644 --- a/vehicle/simroadtraffic.cc +++ b/vehicle/simroadtraffic.cc @@ -394,7 +394,7 @@ void private_car_t::build_timeline_list(karte_t *welt) } } liste_timeline.resize( temp_liste.get_count() ); - FOR(vector_tpl, const i, temp_liste) { + for(citycar_desc_t const* const i : temp_liste) { liste_timeline.append(i, i->get_distribution_weight()); } } diff --git a/vehicle/vehicle.cc b/vehicle/vehicle.cc index 032a8ddeb30..932d1190006 100644 --- a/vehicle/vehicle.cc +++ b/vehicle/vehicle.cc @@ -773,8 +773,7 @@ void vehicle_t::rotate90_freight_destinations(const sint16 y_size) // now rotate the freight for (uint8 i = 0; i < number_of_classes; i++) { - FOR(slist_tpl, &tmp, fracht[i]) - { + for(ware_t & tmp : fracht[i]) { tmp.rotate90(y_size); } } @@ -815,8 +814,7 @@ void vehicle_t::set_convoi(convoi_t *c) // just correct freight destinations for (uint8 i = 0; i < number_of_classes; i++) { - FOR(slist_tpl, &c, fracht[i]) - { + for(ware_t & c : fracht[i]) { c.finish_rd(welt); } } @@ -849,12 +847,12 @@ uint16 vehicle_t::unload_cargo(halthandle_t halt, sint64 & revenue_from_unloadin halthandle_t via_halt = tmp.get_zwischenziel(); // check if destination or transfer is still valid - if (!end_halt.is_bound() || !via_halt.is_bound()) - { + if( !end_halt.is_bound() || !via_halt.is_bound() ) { + DBG_MESSAGE("vehicle_t::unload_cargo()", "destination of %d %s is no longer reachable", tmp.menge, translator::translate(tmp.get_name())); total_freight -= tmp.menge; sum_weight -= tmp.menge * tmp.get_desc()->get_weight_per_unit(); - i = fracht[j].erase(i); + i = fracht[j].erase( i ); } else if (end_halt == halt || via_halt == halt) { @@ -1048,8 +1046,7 @@ bool vehicle_t::load_freight_internal(halthandle_t halt, bool overcrowd, bool *s *skip_vehicles = true; if (!fracht[0].empty() && desc->get_mixed_load_prohibition()) { - FOR(slist_tpl, const& w, fracht[0]) - { + for (ware_t const& w : fracht[0]) { goods_restriction = w.index; break; } @@ -1076,8 +1073,7 @@ bool vehicle_t::load_freight_internal(halthandle_t halt, bool overcrowd, bool *s freight_this_class = 0; if (!fracht[i].empty()) { - FOR(slist_tpl, const& w, fracht[i]) - { + for (ware_t const& w : fracht[i]) { freight_this_class += w.menge; } } @@ -1101,11 +1097,11 @@ bool vehicle_t::load_freight_internal(halthandle_t halt, bool overcrowd, bool *s for (slist_tpl::iterator iter_z = freight_add.begin(); iter_z != freight_add.end(); ) { ware_t &ware = *iter_z; + total_freight += ware.menge; // could this be joined with existing freight? - FOR(slist_tpl, &tmp, fracht[i]) - { + for(ware_t & tmp : fracht[i] ) { // New system: only merges if origins are alike. // @author: jamespetts if (ware.can_merge_with(tmp)) @@ -1117,19 +1113,16 @@ bool vehicle_t::load_freight_internal(halthandle_t halt, bool overcrowd, bool *s } // if != 0 we could not join it to existing => load it - if (ware.menge != 0) - { + if( ware.menge != 0 ) { ++iter_z; // we add list directly } - else - { + else { iter_z = freight_add.erase(iter_z); } } - if (!freight_add.empty()) - { + if( !freight_add.empty() ) { // We now DON'T have to unpick which class was reassigned to i. // i is the accommodation class. fracht[i].append_list(freight_add); @@ -1168,8 +1161,7 @@ void vehicle_t::fix_class_accommodations() lowest_available_class = i; continue; } - FOR(slist_tpl, &tmp, fracht[i]) - { + for (ware_t &tmp : fracht[i]) { load[i] += tmp.menge; if (load[i] > capacity[i]) { @@ -1226,36 +1218,33 @@ void vehicle_t::remove_stale_cargo() for (uint8 i = 0; i < number_of_classes; i++) { - if (!fracht[i].empty()) - { - FOR(slist_tpl, &tmp, fracht[i]) - { - + if (!fracht[i].empty()) { + for(ware_t & tmp : fracht[i]) { bool found = false; - if (tmp.get_zwischenziel().is_bound()) { + if ( tmp.get_zwischenziel().is_bound() ) { // the original halt exists, but does we still go there? - FOR(minivec_tpl, const& i, cnv->get_schedule()->entries) { - if (haltestelle_t::get_halt(i.pos, cnv->get_owner()) == tmp.get_zwischenziel()) { + for(schedule_entry_t const& i : cnv->get_schedule()->entries) { + if( haltestelle_t::get_halt( i.pos, cnv->get_owner()) == tmp.get_zwischenziel() ) { found = true; break; } } } - if (!found) { + if( !found ) { // the target halt may have been joined or there is a closer one now, thus our original target is no longer valid const sint32 offset = cnv->get_schedule()->get_current_stop(); const sint32 max_count = cnv->get_schedule()->entries.get_count(); for (sint32 j = 0; j < max_count; j++) { // try to unload on next stop - halthandle_t halt = haltestelle_t::get_halt(cnv->get_schedule()->entries[(i + offset) % max_count].pos, cnv->get_owner()); - if (halt.is_bound()) { - if (halt->is_enabled(tmp.get_index())) { + halthandle_t halt = haltestelle_t::get_halt( cnv->get_schedule()->entries[ (i+offset)%max_count ].pos, cnv->get_owner() ); + if( halt.is_bound() ) { + if( halt->is_enabled(tmp.get_index()) ) { // ok, lets change here, since goods are accepted here tmp.access_zwischenziel() = halt; if (!tmp.get_ziel().is_bound()) { // set target, to prevent that unload_cargo drops cargo - tmp.set_ziel(halt); + tmp.set_ziel( halt ); } found = true; break; @@ -1264,22 +1253,21 @@ void vehicle_t::remove_stale_cargo() } } - if (!found) - { + if( !found ) { kill_queue.append(tmp); } else { // since we need to point at factory (0,0), we recheck this too koord k = tmp.get_zielpos(); - fabrik_t *fab = fabrik_t::get_fab(k); - tmp.set_zielpos(fab ? fab->get_pos().get_2d() : k); + fabrik_t *fab = fabrik_t::get_fab( k ); + tmp.set_zielpos( fab ? fab->get_pos().get_2d() : k ); total_freight += tmp.menge; } } - FOR(vector_tpl, const& c, kill_queue) { - fabrik_t::update_transit(c, false); + for(ware_t const& c : kill_queue) { + fabrik_t::update_transit( c, false ); fracht[i].remove(c); cnv->invalidate_weight_summary(); } @@ -1304,7 +1292,7 @@ void vehicle_t::play_sound() const */ void vehicle_t::initialise_journey(uint16 start_route_index, bool recalc) { - route_index = start_route_index + 1; + route_index = start_route_index+1; check_for_finish = false; use_calc_height = true; @@ -1456,8 +1444,8 @@ void vehicle_t::set_desc(const vehicle_desc_t* value) { if (!fracht[i].empty()) { - FOR(slist_tpl, &tmp, fracht[i]) { - fabrik_t::update_transit(tmp, false); + for (ware_t &tmp : fracht[i]) { + fabrik_t::update_transit( tmp, false ); } fracht[i].clear(); } @@ -1526,7 +1514,7 @@ grund_t* vehicle_t::hop_check() if ( nextnext_pos == get_pos() ) { dbg->error("vehicle_t::hop_check", "route contains point (%s) twice for %s", nextnext_pos.get_str(), cnv->get_name()); } - uint8 new_dir = ribi_type(nextnext_pos-pos_next); + uint8 new_dir = ribi_type(nextnext_pos - pos_next); if((dir&new_dir)==0) { // new one way sign here? cnv->suche_neue_route(); @@ -1559,7 +1547,7 @@ grund_t* vehicle_t::hop_check() return bd; } else { - // this is needed since in convoi_t::vorfahren the flag leading is set to null + // this is needed since in convoi_t::vorfahren the flag 'leading' is set to null if(check_for_finish) { return NULL; } @@ -1567,12 +1555,12 @@ grund_t* vehicle_t::hop_check() return welt->lookup(pos_next); } + bool vehicle_t::can_enter_tile(sint32 &restart_speed, uint8 second_check_count) { cnv->set_checked_tile_this_step(get_pos()); - grund_t *gr = welt->lookup(pos_next); - if (gr) - { + grund_t *gr = welt->lookup( pos_next ); + if ( gr ) { bool ok = can_enter_tile( gr, restart_speed, second_check_count ); if (!ok) { @@ -1589,6 +1577,7 @@ bool vehicle_t::can_enter_tile(sint32 &restart_speed, uint8 second_check_count) } } + void vehicle_t::leave_tile() { vehicle_base_t::leave_tile(); @@ -1672,7 +1661,7 @@ void vehicle_t::hop(grund_t* gr) enter_tile(gr); weg_t *weg = get_weg(); - if( weg ) { + if( weg ) { //const grund_t *gr_prev = welt->lookup(pos_prev); //const weg_t * weg_prev = gr_prev != NULL ? gr_prev->get_weg(get_waytype()) : NULL; @@ -1956,9 +1945,8 @@ sint16 get_friction_of_waytype(waytype_t waytype) } -/* calculates the current friction coefficient based on the current track +/** calculates the current friction coefficient based on the current track * flat, slope, (curve)... - * @author prissi, HJ, Dwachs */ void vehicle_t::calc_drag_coefficient(const grund_t *gr) //,const int h_alt, const int h_neu) { @@ -1986,8 +1974,7 @@ void vehicle_t::calc_drag_coefficient(const grund_t *gr) //,const int h_alt, con // See here for an explanation of the additional resistance // from hills: https://en.wikibooks.org/wiki/Fundamentals_of_Transportation/Grade const slope_t::type hang = gr->get_weg_hang(); - if(hang != slope_t::flat) - { + if( hang != slope_t::flat ) { // Bernd Gabriel, Nov, 30 2009: at least 1 partial direction must match for uphill (op '&'), but not the // complete direction. The hill might begin in a curve and then '==' accidently accelerates the vehicle. const uint slope_height = is_one_high(hang) ? 1 : 2; @@ -2052,11 +2039,13 @@ void vehicle_t::make_smoke() const { // does it smoke at all? if( smoke && desc->get_smoke() ) { - // Hajo: only produce smoke when heavily accelerating or steam engine + // only produce smoke when heavily accelerating or steam engine if( (cnv->get_akt_speed() < (sint32)((cnv->get_vehicle_summary().max_sim_speed * 7u) >> 3) && (route_index < cnv->get_route_infos().get_count() - 4)) || desc->get_engine_type() == vehicle_desc_t::steam ) { grund_t* const gr = welt->lookup( get_pos() ); if( gr ) { - wolke_t* const abgas = new wolke_t( get_pos(), get_xoff() + ((dx * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> 8), get_yoff() + ((dy * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> 8) + get_hoff(), desc->get_smoke() ); + wolke_t* const abgas = new wolke_t( get_pos(), + get_xoff() + ((dx * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> VEHICLE_STEPS_PER_TILE_SHIFT), + get_yoff() + ((dy * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> VEHICLE_STEPS_PER_TILE_SHIFT) + get_hoff(), desc->get_smoke() ); if( !gr->obj_add( abgas ) ) { abgas->set_flag( obj_t::not_on_map ); delete abgas; @@ -2082,10 +2071,10 @@ const char *vehicle_t::get_cargo_mass() const uint32 vehicle_t::get_cargo_weight() const { uint32 weight = 0; + for (uint8 i = 0; i < number_of_classes; i++) { - FOR(slist_tpl, const& c, fracht[i]) - { + for(ware_t const& c : fracht[i]) { weight += c.menge * c.get_desc()->get_weight_per_unit(); } } @@ -2100,9 +2089,8 @@ void vehicle_t::discard_cargo() { for (uint8 i = 0; i < number_of_classes; i++) { - FOR(slist_tpl, w, fracht[i]) - { - fabrik_t::update_transit(w, false); + for(ware_t w : fracht[i] ) { + fabrik_t::update_transit( w, false ); } fracht[i].clear(); } @@ -2250,8 +2238,7 @@ uint16 vehicle_t::get_total_cargo_by_class(uint8 a_class) const return 0; } - FOR(slist_tpl, const& ware, fracht[a_class]) - { + for (ware_t const& ware : fracht[a_class]) { carried += ware.menge; } @@ -2400,8 +2387,7 @@ uint8 vehicle_t::get_comfort(uint8 catering_level, uint8 g_class) const { if (class_reassignments[i] == g_class) { - FOR(slist_tpl, const& ware, fracht[i]) - { + for(ware_t const& ware : fracht[i]) { if(ware.is_passenger()) { passenger_count += ware.menge; @@ -2424,12 +2410,10 @@ uint8 vehicle_t::get_comfort(uint8 catering_level, uint8 g_class) const } - void vehicle_t::rdwr(loadsave_t *file) { // this is only called from objlist => we save nothing ... - assert( file->is_saving() ); - (void)file; + assert( file->is_saving() ); (void)file; } @@ -2487,6 +2471,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) set_pos(pos); } + sint8 hoff = file->is_saving() ? get_hoff() : 0; if(file->is_version_less(86, 6)) { @@ -2497,7 +2482,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) file->rdwr_long(l); dy = (sint8)l; file->rdwr_long(l); - hoff = (sint8)(l*TILE_HEIGHT_STEP / 16); + hoff = (sint8)(l*TILE_HEIGHT_STEP/16); file->rdwr_long(speed_limit); file->rdwr_enum(direction); file->rdwr_enum(previous_direction); @@ -2505,7 +2490,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) file->rdwr_long(l); route_index = (uint16)l; purchase_time = (purchase_time >> welt->ticks_per_world_month_shift) + welt->get_settings().get_starting_year(); - DBG_MESSAGE("vehicle_t::rdwr_from_convoi()","bought at %i/%i.",(purchase_time%12)+1,purchase_time/12); +DBG_MESSAGE("vehicle_t::rdwr_from_convoi()","bought at %i/%i.",(purchase_time%12)+1,purchase_time/12); } else { // changed several data types to save runtime memory @@ -2522,9 +2507,9 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) steps_next = diagonal_vehicle_steps_per_tile - 1; } } - sint16 dummy16 = ((16 * (sint16)hoff) / TILE_HEIGHT_STEP); + sint16 dummy16 = ((16*(sint16)hoff)/TILE_HEIGHT_STEP); file->rdwr_short(dummy16); - hoff = (sint8)((TILE_HEIGHT_STEP*(sint16)dummy16) / 16); + hoff = (sint8)((TILE_HEIGHT_STEP*(sint16)dummy16)/16); file->rdwr_long(speed_limit); file->rdwr_enum(direction); file->rdwr_enum(previous_direction); @@ -2617,8 +2602,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) } } - if(file->is_saving()) - { + if(file->is_saving()) { if (create_dummy_ware) { // create dummy freight for savegame compatibility @@ -2632,8 +2616,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) for (uint8 i = 0; i < saved_number_of_classes; i++) { - FOR(slist_tpl, ware, fracht[i]) - { + for (ware_t ware : fracht[i]) { ware.rdwr(file); } } @@ -2878,8 +2861,7 @@ void vehicle_t::rdwr_from_convoi(loadsave_t *file) for (uint8 i = 0; i < number_of_classes; i++) { - FOR(slist_tpl, const& c, fracht[i]) - { + for(ware_t const& c : fracht[i]) { total_freight += c.menge; } } @@ -2909,8 +2891,8 @@ uint32 vehicle_t::calc_sale_value() const return value.to_sint32(); } -void -vehicle_t::show_info() + +void vehicle_t::show_info() { if( cnv != NULL ) { cnv->show_info(); diff --git a/vehicle/water_vehicle.h b/vehicle/water_vehicle.h index 89cd8a746d4..b5da49da1a5 100644 --- a/vehicle/water_vehicle.h +++ b/vehicle/water_vehicle.h @@ -19,7 +19,7 @@ class water_vehicle_t : public vehicle_t { protected: // how expensive to go here (for way search) - int get_cost(const grund_t *, const sint32, koord) OVERRIDE { return 1; } + int get_cost(const grund_t *, const sint32, ribi_t::ribi) OVERRIDE { return 1; } void calc_drag_coefficient(const grund_t *gr) OVERRIDE;