summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorErich Eckner <git@eckner.net>2018-10-30 11:13:12 +0100
committerErich Eckner <git@eckner.net>2022-01-16 21:58:15 +0100
commit0c5ba5b7bf9a9dfa78c026f4da656940a460f47d (patch)
treed2a44494bbd70425b8f9e0e22b8c4e4dd8223001 /src
parent523901887827768d6bff347ddb57787b295cc9e1 (diff)
downloadopenttd-0c5ba5b7bf9a9dfa78c026f4da656940a460f47d.tar.xz
underground patch applied
Diffstat (limited to 'src')
-rw-r--r--src/base_station_base.h9
-rw-r--r--src/clear_cmd.cpp24
-rw-r--r--src/console_cmds.cpp2
-rw-r--r--src/genworld_gui.cpp44
-rw-r--r--src/gfx_type.h1
-rw-r--r--src/heightmap.cpp4
-rw-r--r--src/industry_cmd.cpp3
-rw-r--r--src/lang/english.txt41
-rw-r--r--src/lang/russian.txt42
-rw-r--r--src/layer.cpp137
-rw-r--r--src/layer_func.h159
-rw-r--r--src/layer_gui.h17
-rw-r--r--src/layer_type.h22
-rw-r--r--src/main_gui.cpp2
-rw-r--r--src/map.cpp7
-rw-r--r--src/map_func.h2
-rw-r--r--src/misc.cpp3
-rw-r--r--src/rail.h2
-rw-r--r--src/saveload/map_sl.cpp6
-rw-r--r--src/settings_table.cpp1
-rw-r--r--src/settings_type.h3
-rw-r--r--src/station.cpp35
-rw-r--r--src/station_cmd.cpp71
-rw-r--r--src/station_gui.cpp25
-rw-r--r--src/table/settings/game_settings.ini9
-rw-r--r--src/table/settings/gui_settings.ini8
-rw-r--r--src/terraform_cmd.cpp5
-rw-r--r--src/tgp.cpp14
-rw-r--r--src/tile_map.h2
-rw-r--r--src/tilearea_type.h10
-rw-r--r--src/toolbar_gui.cpp47
-rw-r--r--src/town_cmd.cpp6
-rw-r--r--src/tree_cmd.cpp8
-rw-r--r--src/tunnelbridge_cmd.cpp11
-rw-r--r--src/underground_gui.cpp321
-rw-r--r--src/underground_gui.h19
-rw-r--r--src/vehicle.cpp2
-rw-r--r--src/viewport.cpp42
-rw-r--r--src/water_cmd.cpp11
-rw-r--r--src/widgets/genworld_widget.h2
-rw-r--r--src/widgets/underground_widget.h21
-rw-r--r--src/window_type.h6
42 files changed, 1132 insertions, 74 deletions
diff --git a/src/base_station_base.h b/src/base_station_base.h
index 40543f1b8..9a5157ca4 100644
--- a/src/base_station_base.h
+++ b/src/base_station_base.h
@@ -26,6 +26,13 @@ struct StationSpecList {
/** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */
+/*
+** "layer"
+** :
+** "Rect" ()
+** ( , --
+** Rect )
+*/
struct StationRect : public Rect {
enum StationRectMode
{
@@ -36,7 +43,7 @@ struct StationRect : public Rect {
StationRect();
void MakeEmpty();
- bool PtInExtendedRect(int x, int y, int distance = 0) const;
+ bool PtInExtendedRect(int topx, int topy, int distance = 0) const;
bool IsEmpty() const;
CommandCost BeforeAddTile(TileIndex tile, StationRectMode mode);
CommandCost BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode);
diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp
index 17711a626..5fc5829f9 100644
--- a/src/clear_cmd.cpp
+++ b/src/clear_cmd.cpp
@@ -12,6 +12,7 @@
#include "command_func.h"
#include "landscape.h"
#include "genworld.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "water.h"
#include "core/random_func.hpp"
@@ -99,8 +100,28 @@ static void DrawClearLandFence(const TileInfo *ti)
EndSpriteCombine();
}
+static void DrawUndergroundTile_Clear(TileInfo *ti)
+{
+
+}
+
static void DrawTile_Clear(TileInfo *ti)
{
+ uint base_tile = TopTile(ti->tile);
+ uint underground_tile = DownTile(base_tile);
+
+ bool self_underground = IsUnderground(ti->tile);
+
+ bool have_canalization = IsTileType(base_tile, MP_HOUSE);
+ bool have_underground = !IsTileType(underground_tile, MP_CLEAR);
+
+ if (self_underground && !have_canalization)
+ DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
+
+ if (self_underground && have_canalization)
+ DrawGroundSprite(SPR_FLAT_GRASS_TILE + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
+
+ if (!self_underground)
switch (GetClearGround(ti->tile)) {
case CLEAR_GRASS:
DrawClearLandTile(ti, GetClearDensity(ti->tile));
@@ -125,6 +146,9 @@ static void DrawTile_Clear(TileInfo *ti)
break;
}
+ if (!self_underground && have_underground)
+ DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
+
DrawBridgeMiddle(ti);
}
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index 7f663fe86..63307d17d 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -26,6 +26,7 @@
#include "screenshot.h"
#include "genworld.h"
#include "strings_func.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "window_func.h"
#include "date_func.h"
@@ -1072,6 +1073,7 @@ DEF_CONSOLE_CMD(ConRestart)
}
/* Don't copy the _newgame pointers to the real pointers, so call SwitchToMode directly */
+ _settings_game.game_creation.layers = FindFirstBit(LayerCount());
_settings_game.game_creation.map_x = MapLogX();
_settings_game.game_creation.map_y = FindFirstBit(MapSizeY());
_switch_mode = SM_RESTARTGAME;
diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp
index 2489105c2..00f18699c 100644
--- a/src/genworld_gui.cpp
+++ b/src/genworld_gui.cpp
@@ -16,6 +16,8 @@
#include "window_func.h"
#include "date_func.h"
#include "sound_func.h"
+#include "map_type.h"
+#include "layer_type.h"
#include "fios.h"
#include "string_func.h"
#include "widgets/dropdown_type.h"
@@ -91,6 +93,7 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = {
/* Left column with labels. */
NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAPSIZE, STR_MAPGEN_MAPSIZE_TOOLTIP), SetFill(1, 1),
+ NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_UNDERGROUND_LAYER_COUNT, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_LAND_GENERATOR, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TERRAIN_TYPE, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_VARIETY, STR_NULL), SetFill(1, 1),
@@ -107,6 +110,7 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = {
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_BY, STR_NULL), SetPadding(1, 0, 0, 0), SetFill(1, 1),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_MAPSIZE_Y_PULLDOWN), SetDataTip(STR_JUST_INT, STR_MAPGEN_MAPSIZE_TOOLTIP), SetFill(1, 0),
EndContainer(),
+ NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_LAYER_COUNT_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_LANDSCAPE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TERRAIN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_VARIETY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
@@ -216,6 +220,7 @@ static const NWidgetPart _nested_heightmap_load_widgets[] = {
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_SIZE_LABEL, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAPSIZE, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_ROTATION, STR_NULL), SetFill(1, 1),
+ NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_UNDERGROUND_LAYER_COUNT, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_TOWNS, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_INDUSTRIES, STR_NULL), SetFill(1, 1),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_QUANTITY_OF_RIVERS, STR_NULL), SetFill(1, 1),
@@ -233,6 +238,7 @@ static const NWidgetPart _nested_heightmap_load_widgets[] = {
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_MAPSIZE_Y_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL), SetFill(1, 0),
EndContainer(),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_HEIGHTMAP_ROTATION_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
+ NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_LAYER_COUNT_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_INDUSTRY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_RIVER_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
@@ -306,11 +312,11 @@ static void LandscapeGenerationCallback(Window *w, bool confirmed)
if (confirmed) StartGeneratingLandscape((GenerateLandscapeWindowMode)w->window_number);
}
-static DropDownList BuildMapsizeDropDown()
+static DropDownList BuildBitListDropDown(uint min_bits, uint max_bits)
{
DropDownList list;
- for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) {
+ for (uint i = min_bits; i <= max_bits; i++) {
DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false);
item->SetParam(0, 1LL << i);
list.emplace_back(item);
@@ -346,6 +352,15 @@ static DropDownList BuildTownNameDropDown()
return list;
}
+static DropDownList BuildMapsizeDropDown()
+{
+ return BuildBitListDropDown(MIN_MAP_SIZE_BITS, MAX_MAP_SIZE_BITS);
+}
+
+static DropDownList BuildLayerDropDown()
+{
+ return BuildBitListDropDown(MIN_LAYER_COUNT_BITS, MAX_LAYER_COUNT_BITS);
+}
static const StringID _elevations[] = {STR_TERRAIN_TYPE_VERY_FLAT, STR_TERRAIN_TYPE_FLAT, STR_TERRAIN_TYPE_HILLY, STR_TERRAIN_TYPE_MOUNTAINOUS, STR_TERRAIN_TYPE_ALPINIST, STR_TERRAIN_TYPE_CUSTOM, INVALID_STRING_ID};
static const StringID _sea_lakes[] = {STR_SEA_LEVEL_VERY_LOW, STR_SEA_LEVEL_LOW, STR_SEA_LEVEL_MEDIUM, STR_SEA_LEVEL_HIGH, STR_SEA_LEVEL_CUSTOM, INVALID_STRING_ID};
@@ -392,6 +407,7 @@ struct GenerateLandscapeWindow : public Window {
case WID_GL_START_DATE_TEXT: SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); break;
case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break;
case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break;
+ case WID_GL_LAYER_COUNT_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.layers); break;
case WID_GL_HEIGHTMAP_HEIGHT_TEXT: SetDParam(0, _settings_newgame.game_creation.heightmap_height); break;
case WID_GL_SNOW_COVERAGE_TEXT: SetDParam(0, _settings_newgame.game_creation.snow_coverage); break;
case WID_GL_DESERT_COVERAGE_TEXT: SetDParam(0, _settings_newgame.game_creation.desert_coverage); break;
@@ -551,6 +567,11 @@ struct GenerateLandscapeWindow : public Window {
*size = maxdim(*size, GetStringBoundingBox(STR_JUST_INT));
break;
+ case WID_GL_LAYER_COUNT_PULLDOWN:
+ SetDParam(0, MAX_LAYER_COUNT);
+ *size = GetStringBoundingBox(STR_JUST_INT);
+ break;
+
case WID_GL_SNOW_COVERAGE_TEXT:
SetDParamMaxValue(0, MAX_TILE_HEIGHT);
*size = maxdim(*size, GetStringBoundingBox(STR_MAPGEN_SNOW_COVERAGE_TEXT));
@@ -647,6 +668,10 @@ struct GenerateLandscapeWindow : public Window {
ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_GL_MAPSIZE_Y_PULLDOWN);
break;
+ case WID_GL_LAYER_COUNT_PULLDOWN: // Mapsize Z
+ ShowDropDownList(this, BuildLayerDropDown(), _settings_newgame.game_creation.layers, WID_GL_LAYER_COUNT_PULLDOWN);
+ break;
+
case WID_GL_TOWN_PULLDOWN: // Number of towns
ShowDropDownMenu(this, _num_towns, _settings_newgame.difficulty.number_towns, WID_GL_TOWN_PULLDOWN, 0, 0);
break;
@@ -842,6 +867,7 @@ struct GenerateLandscapeWindow : public Window {
switch (widget) {
case WID_GL_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break;
case WID_GL_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break;
+ case WID_GL_LAYER_COUNT_PULLDOWN: _settings_newgame.game_creation.layers = index; break;
case WID_GL_RIVER_PULLDOWN: _settings_newgame.game_creation.amount_of_rivers = index; break;
case WID_GL_SMOOTHNESS_PULLDOWN: _settings_newgame.game_creation.tgen_smoothness = index; break;
case WID_GL_VARIETY_PULLDOWN: _settings_newgame.game_creation.variety = index; break;
@@ -1051,6 +1077,10 @@ struct CreateScenarioWindow : public Window
SetDParam(0, 1LL << _settings_newgame.game_creation.map_y);
break;
+ case WID_CS_LAYER_COUNT_PULLDOWN:
+ SetDParam(0, 1 << _settings_newgame.game_creation.layers);
+ break;
+
case WID_CS_FLAT_LAND_HEIGHT_TEXT:
SetDParam(0, _settings_newgame.game_creation.se_flat_world_height);
break;
@@ -1084,6 +1114,10 @@ struct CreateScenarioWindow : public Window
case WID_CS_MAPSIZE_X_PULLDOWN:
case WID_CS_MAPSIZE_Y_PULLDOWN:
SetDParamMaxValue(0, MAX_MAP_SIZE);
+ break;
+
+ case WID_CS_LAYER_COUNT_PULLDOWN:
+ SetDParam(0, MAX_LAYER_COUNT);
break;
case WID_CS_FLAT_LAND_HEIGHT_TEXT:
@@ -1117,6 +1151,10 @@ struct CreateScenarioWindow : public Window
ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_CS_MAPSIZE_Y_PULLDOWN);
break;
+ case WID_CS_LAYER_COUNT_PULLDOWN: // Mapsize Y
+ ShowDropDownList(this, BuildLayerDropDown(), _settings_newgame.game_creation.layers, WID_CS_LAYER_COUNT_PULLDOWN);
+ break;
+
case WID_CS_EMPTY_WORLD: // Empty world / flat world
StartGeneratingLandscape(GLWM_SCENARIO);
break;
@@ -1179,6 +1217,7 @@ struct CreateScenarioWindow : public Window
switch (widget) {
case WID_CS_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break;
case WID_CS_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break;
+ case WID_CS_LAYER_COUNT_PULLDOWN: _settings_newgame.game_creation.layers = index; break;
}
this->SetDirty();
}
@@ -1234,6 +1273,7 @@ static const NWidgetPart _nested_create_scenario_widgets[] = {
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_CS_MAPSIZE_X_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL), SetPadding(0, 4, 0, 0),
NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_BY, STR_NULL), SetPadding(1, 2, 0, 0),
NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_CS_MAPSIZE_Y_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL),
+ NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_CS_LAYER_COUNT_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL),
EndContainer(),
/* Date. */
NWidget(NWID_HORIZONTAL),
diff --git a/src/gfx_type.h b/src/gfx_type.h
index 932a6cb87..569282130 100644
--- a/src/gfx_type.h
+++ b/src/gfx_type.h
@@ -156,6 +156,7 @@ struct DrawPixelInfo {
void *dst_ptr;
int left, top, width, height;
int pitch;
+ int layer;
ZoomLevel zoom;
};
diff --git a/src/heightmap.cpp b/src/heightmap.cpp
index 0c726371c..a073c7cf4 100644
--- a/src/heightmap.cpp
+++ b/src/heightmap.cpp
@@ -14,6 +14,7 @@
#include "error.h"
#include "saveload/saveload.h"
#include "bmp.h"
+#include "layer_func.h"
#include "gfx_func.h"
#include "fios.h"
#include "fileio_func.h"
@@ -429,6 +430,9 @@ void FixSlopes()
width = MapSizeX();
height = MapSizeY();
+ /* Layers height correct */
+ FixUndergroundHeights();
+
/* Top and left edge */
for (row = 0; (uint)row < height; row++) {
for (col = 0; (uint)col < width; col++) {
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 41ee3ac06..8e37724a2 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -12,6 +12,7 @@
#include "industry.h"
#include "station_base.h"
#include "landscape.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "command_func.h"
#include "town.h"
@@ -1447,7 +1448,7 @@ static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTil
IndustryGfx gfx = GetTranslatedIndustryTileID(it.gfx);
TileIndex cur_tile = TileAddWrap(tile, it.ti.x, it.ti.y);
- if (!IsValidTile(cur_tile)) {
+ if (!IsValidTile(cur_tile) || IsUnderground(cur_tile)) {
return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
}
diff --git a/src/lang/english.txt b/src/lang/english.txt
index becf8d35c..aeefcb49f 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -478,6 +478,15 @@ STR_LANDSCAPING_MENU_LANDSCAPING :Landscaping
STR_LANDSCAPING_MENU_PLANT_TREES :Plant trees
STR_LANDSCAPING_MENU_PLACE_SIGN :Place sign
+STR_LANDSCAPING_MENU_SEPARATOR1 :
+STR_LANDSCAPING_MENU_UNDERGROUND :Special constructions
+STR_LANDSCAPING_MENU_SEPARATOR2 :
+STR_LANDSCAPING_MENU_LAYER_1 :Surface
+STR_LANDSCAPING_MENU_LAYER_2 :Undergroound (-1)
+STR_LANDSCAPING_MENU_LAYER_3 :Undergroound (-2)
+STR_LANDSCAPING_MENU_LAYER_4 :Undergroound (-3)
+############ range ends here
+
# Music menu
###length 1
STR_TOOLBAR_SOUND_MUSIC :Sound/music
@@ -5556,3 +5565,35 @@ STR_PLANE :{BLACK}{PLANE}
STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
+
+# underground
+
+# error
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND :{WHITE}Can't build this underground
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_OVER_GROUND :{WHITE}Can't build this on surface
+STR_ERROR_UNDERGROUND_CAN_T_TERRAFORM :{WHITE}Can't terraform underground
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_PART :{WHITE}Can't build escalator here...
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_TOP_PART :{WHITE}Can't build escalator top here...
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_BOTTOM_PART :{WHITE}Can't build escalator bottom here...
+
+# menus
+STR_UNDERGROUND_LAYER_COUNT :{BLACK}Layers count
+STR_UNDERGROUND_BUILD :{WHITE}Underground
+
+# underground land types
+STR_UNDERGROUND_FLAT :Cave
+STR_UNDERGROUND_SOLID :Ground
+STR_UNDERGROUND_ROCKS :Rocks
+STR_UNDERGROUND_HEAVY_ROCKS :Hard rocks
+
+# underground special build types
+STR_UNDERGROUND_ESCALATOR :Escalator
+STR_UNDERGROUND_CONNECT :Connect
+STR_UNDERGROUND_PIPE :Pipe
+
+# underground tool tip
+STR_UNDERGROUND_TOOLTIP_ESCALATOR :{BLACK}Build escalator (underground station connect with surface)
+STR_UNDERGROUND_TOOLTIP_CONNECT :{BLACK}Build connect (underground track connect with surface)
+STR_UNDERGROUND_TOOLTIP_PIPE :{BLACK}Pipe (xm... anything...)
+
+# end underground string
diff --git a/src/lang/russian.txt b/src/lang/russian.txt
index 534be43c1..c4c113f45 100644
--- a/src/lang/russian.txt
+++ b/src/lang/russian.txt
@@ -604,6 +604,16 @@ STR_LANDSCAPING_MENU_LANDSCAPING :Ландшаф
STR_LANDSCAPING_MENU_PLANT_TREES :Высадка деревьев
STR_LANDSCAPING_MENU_PLACE_SIGN :Поставить метку
+# Underground menu
+###length 7
+STR_LANDSCAPING_MENU_SEPARATOR1 :
+STR_LANDSCAPING_MENU_UNDERGROUND :Спец. конструкции
+STR_LANDSCAPING_MENU_SEPARATOR2 :
+STR_LANDSCAPING_MENU_LAYER_1 :Поверхность
+STR_LANDSCAPING_MENU_LAYER_2 :Подземелье (-1)
+STR_LANDSCAPING_MENU_LAYER_3 :Подземелье (-2)
+STR_LANDSCAPING_MENU_LAYER_4 :Подземелье (-3)
+
# Music menu
###length 1
STR_TOOLBAR_SOUND_MUSIC :Звук/музыка
@@ -5780,3 +5790,35 @@ STR_PLANE :{BLACK}{PLANE}
STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
+
+# underground
+
+# error
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND :{WHITE}Не может строиться под землей
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_OVER_GROUND :{WHITE}Не может строиться на поверхности
+STR_ERROR_UNDERGROUND_CAN_T_TERRAFORM :{WHITE}Изменение ландшафта недоступно
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_PART :{WHITE}Нельзя построить эскалатор...
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_TOP_PART :{WHITE}Нельзя построить верхнюю часть эскалатора...
+STR_ERROR_UNDERGROUND_CAN_T_BUILD_BOTTOM_PART :{WHITE}Нельзя построить нижнюю часть эскалатора...
+
+# menus
+STR_UNDERGROUND_LAYER_COUNT :{BLACK}Количество слоев
+STR_UNDERGROUND_BUILD :{WHITE}Подземелье
+
+# underground land types
+STR_UNDERGROUND_FLAT :Пещера
+STR_UNDERGROUND_SOLID :Грунт
+STR_UNDERGROUND_ROCKS :Скалы
+STR_UNDERGROUND_HEAVY_ROCKS :Твердые породы
+
+# underground special build types
+STR_UNDERGROUND_ESCALATOR :Эскалатор
+STR_UNDERGROUND_CONNECT :Соединение
+STR_UNDERGROUND_PIPE :Трубы
+
+# underground tool tip
+STR_UNDERGROUND_TOOLTIP_ESCALATOR :{BLACK}Постройка эскалатора (связь подземной станции с поверхностью)
+STR_UNDERGROUND_TOOLTIP_CONNECT :{BLACK}Постройка подъема (подземные рельсы выходят наружу)
+STR_UNDERGROUND_TOOLTIP_PIPE :{BLACK}Трубы (хм... что-то...)
+
+# end underground string
diff --git a/src/layer.cpp b/src/layer.cpp
new file mode 100644
index 000000000..fdf182e1f
--- /dev/null
+++ b/src/layer.cpp
@@ -0,0 +1,137 @@
+/* $Id: map.cpp 23740 2012-01-03 21:32:51Z $ */
+/*
+
+ ,
+(, , .)
+
+
+ :
+*-----------*
+| |
+| |
+| |
+*-----------*
+
+ :
+*---*---*---*
+| | | |
+| | | |
+| | | |
+*---*---*---*
+
+ :
+*---*
+| *---*
+| | *---*
+| | | |
+* | | |
+ * | |
+ *---*
+
+ 3 .
+
+ Y ( X)
+ 1, 2, 4, 8 .
+
+ 6464, 4- :
+ 64256, , :
+
+ X Y
+ 0--63 0--63
+ 0--63 64--127
+ 0--63 128--191
+ 0--63 192--255
+
+
+
+ MapSizeX (MapSizeY)
+
+
+ LayerSizeX x (LayerSizeY x LayerCount)
+
+ ""
+
+ MapX, MapY, MapZ
+
+ "3"
+
+ //
+ LayerCount = ... (1, 2, 4, .)
+ LayerSizeZ = ( 1)
+
+ //
+ MapSizeX == LayerSizeX
+ MapSizeY == LayerSizeY * LayerCount
+
+ //
+ LayerIndex = MapY / LayerSizeY
+
+ WorldX = MapX
+ WorldY = MapY - LayerIndex*LayerSizeY
+ WorldZ = MapZ + LayerIndex*LayerSizeZ
+*/
+
+/** @file map.cpp Base functions related to the map and distances on them. */
+
+#include "stdafx.h"
+#include "debug.h"
+#include "core/alloc_func.hpp"
+#include "void_map.h"
+#include "layer_func.h"
+#include "layer_type.h"
+#include "landscape.h"
+
+#if defined(_MSC_VER)
+/* Why the hell is that not in all MSVC headers?? */
+extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned);
+#endif
+
+uint _layer_size_x; ///< Size of the map along the X
+uint _layer_size_y; ///< Size of the map along the Y
+uint _layer_count; ///< The number of tiles on the map
+uint _layer_count_log;
+uint _layer_size; ///< Layer size (sizeX * sizeY)
+
+void InstallLayerSystem(uint size_x, uint size_y, uint layer_count)
+{
+ if (!IsInsideMM(layer_count, MIN_LAYER_COUNT, MAX_LAYER_COUNT))
+ error("invalid layer count");
+
+ _layer_size_x = size_x;
+ _layer_size_y = size_y;
+ _layer_size = size_x * size_y;
+ _layer_count = layer_count;
+ _layer_count_log = FindFirstBit(layer_count);
+}
+
+void FixUndergroundHeights()
+{
+ uint width = MapSizeX();
+ uint height = MapSizeY();
+
+ /* Layer correct */
+ for (uint row = 0; (uint)row < height; row++) {
+
+ /* */
+ if (!(row % LayerSizeY()))
+ for (uint x = 0; x < width; x++) MakeVoid(width * row + x);
+
+ for (uint col = 0; (uint)col < width; col++) {
+ uint tile = TileXY(row, col);
+ if (IsUnderground(tile))
+ SetTileHeight(tile, 0);
+ }
+ }
+}
+
+uint8 calculateLayer(const Viewport *vp)
+{
+ // ViewportDoDraw
+ // .
+ // .
+
+ Point pt = InverseRemapCoords(vp->virtual_left+(vp->virtual_width >> 1),vp->virtual_top+(vp->virtual_height >> 1));
+ TileIndex center = TileVirtXY(pt.x, pt.y);
+ return LayerIndex(center);
+}
+
diff --git a/src/layer_func.h b/src/layer_func.h
new file mode 100644
index 000000000..250e79a3d
--- /dev/null
+++ b/src/layer_func.h
@@ -0,0 +1,159 @@
+/* $Id: layer_func.h 2012-09-07 18:11:11 constructor $ */
+
+/*
+* . layer.cpp
+*/
+
+/** @file layer_func.h Functions related to layer in maps. */
+
+#ifndef LAYER_FUNC_H
+#define LAYER_FUNC_H
+
+#include "map_func.h"
+#include "viewport_type.h"
+
+/*
+*
+* ""
+* "1" ""
+*
+*/
+void InstallLayerSystem(uint size_x, uint size_y, uint layer_count);
+
+/* ""
+* ( -- ) */
+void FixUndergroundHeights();
+
+#define FOR_ALL_LAYERS(var) for (uint var = 0; var < LayerCount(); var++)
+
+
+/**
+ * Get the size of the layer along the X
+ * @return the number of tiles along the X of the layer
+ */
+static inline uint LayerSizeX()
+{
+ extern uint _map_size_x;
+ return _map_size_x;
+}
+
+/**
+ * Get the size of the layer along the Y
+ * @return the number of tiles along the Y of the layer
+ */
+static inline uint LayerSizeY()
+{
+ extern uint _layer_size_y;
+ return _layer_size_y;
+}
+
+/**
+ * Gets the maximum X coordinate within the map, including MP_VOID
+ * @return the maximum X coordinate
+ */
+static inline uint LayerMaxX()
+{
+ return LayerSizeX() - 1;
+}
+
+/**
+ * Gets the maximum Y coordinate within the map, including MP_VOID
+ * @return the maximum Y coordinate
+ */
+static inline uint LayerMaxY()
+{
+ return LayerSizeY() - 1;
+}
+
+/**
+ * Get the layer counts
+ * @return the number of layers
+ */
+static inline uint LayerCount()
+{
+ extern uint _layer_count;
+ return _layer_count;
+}
+
+/**
+ * Get the layer counts
+ * @return the number of layers
+ */
+static inline uint LayerCountLog()
+{
+ extern uint _layer_count_log;
+ return _layer_count_log;
+}
+
+/**
+ * Get the X component of a tile
+ * @param tile the tile to get the X component of
+ * @return the X component
+ */
+static inline uint LayerX(TileIndex tile)
+{
+ return tile & LayerMaxX();
+}
+
+/**
+ * Get the Y component of a tile
+ * @param tile the tile to get the Y component of
+ * @return the Y component
+ */
+static inline uint LayerY(TileIndex tile)
+{
+ return (tile >> MapLogX()) & LayerMaxY();
+}
+
+static inline uint LayerIndex(TileIndex tile)
+{
+ return (tile >> MapLogX()) / LayerSizeY();
+}
+
+static inline bool IsUnderground(TileIndex tile)
+{
+ return LayerIndex(tile) != 0;
+}
+
+/**
+* .
+* ,
+*/
+static inline uint LayerSize()
+{
+ extern uint _layer_size;
+ return _layer_size;
+}
+
+/**
+ * ( )
+ * @param tile the tile to get the Y component of
+ * @return the Y component
+ */
+static inline uint TopTile(TileIndex tile)
+{
+ uint layer = LayerIndex(tile);
+ return (tile - layer * LayerSize());
+}
+
+/* */
+static inline bool IsTopTile(TileIndex tile)
+{
+ return (tile < LayerSize());
+}
+
+/* . ( ??)
+*/
+static inline uint UpTile(TileIndex tile)
+{
+ return TILE_MASK(tile - LayerSize());
+}
+
+/* . ( ??)
+*/
+static inline uint DownTile(TileIndex tile)
+{
+ return TILE_MASK(tile + LayerSize());
+}
+
+#endif /* LAYER_FUNC_H */
diff --git a/src/layer_gui.h b/src/layer_gui.h
new file mode 100644
index 000000000..7b791b7e9
--- /dev/null
+++ b/src/layer_gui.h
@@ -0,0 +1,17 @@
+/* $Id: layer_func.h 2012-09-07 18:11:11 constructor $ */
+
+/*
+* . layer.cpp
+*/
+
+/** @file layer_gui.h Functions for visualisation map with support layers */
+
+#ifndef LAYER_GUI_H
+#define LAYER_GUI_H
+
+#include "layer_func.h"
+#include "viewport_type.h"
+
+uint8 calculateLayer(const Viewport *vp);
+
+#endif /* LAYER_GUI_H */
diff --git a/src/layer_type.h b/src/layer_type.h
new file mode 100644
index 000000000..53eafdde6
--- /dev/null
+++ b/src/layer_type.h
@@ -0,0 +1,22 @@
+/* $Id: layer_type.h 21493 2012-09-11 2:21:53Z constructor $ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file layer_type.h Types related to maps. */
+
+#ifndef LAYER_TYPE_H
+#define LAYER_TYPE_H
+
+/** Minimal and maximal layer counts */
+static const uint MIN_LAYER_COUNT_BITS = 0; ///< Minimal size of map is equal to 2 ^ MIN_LAYER_SIZE_BITS
+static const uint MAX_LAYER_COUNT_BITS = 3; ///< Maximal size of map is equal to 2 ^ MAX_LAYER_SIZE_BITS
+static const uint MIN_LAYER_COUNT = 1 << MIN_LAYER_COUNT_BITS; ///< Minimal layer count = 1
+static const uint MAX_LAYER_COUNT = 1 << MAX_LAYER_COUNT_BITS; ///< Maximal layer count = 8
+
+
+#endif /* LAYER_TYPE_H */
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index b2c4bfee4..60cadfb31 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -66,7 +66,7 @@ bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, HighLightStyl
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
w->SetDirty();
- if (w->IsWidgetLowered(widget)) {
+ if (w->IsWidgetLowered(widget) && mode == _thd.place_mode) {
ResetObjectToPlace();
return false;
}
diff --git a/src/map.cpp b/src/map.cpp
index 2a08d656e..e47332c1d 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -11,6 +11,7 @@
#include "debug.h"
#include "core/alloc_func.hpp"
#include "water_map.h"
+#include "layer_func.h"
#include "string_func.h"
#include "safeguards.h"
@@ -36,7 +37,7 @@ TileExtended *_me = nullptr; ///< Extended Tiles of the map
* @param size_x the width of the map along the NE/SW edge
* @param size_y the 'height' of the map along the SE/NW edge
*/
-void AllocateMap(uint size_x, uint size_y)
+void AllocateMap(uint size_x, uint size_y, uint layer_count)
{
/* Make sure that the map size is within the limits and that
* size of both axes is a power of 2. */
@@ -47,6 +48,10 @@ void AllocateMap(uint size_x, uint size_y)
error("Invalid map size");
}
+ /* , */
+ InstallLayerSystem(size_x, size_y, layer_count);
+ size_y *= layer_count;
+
Debug(map, 1, "Allocating map of size {}x{}", size_x, size_y);
_map_log_x = FindFirstBit(size_x);
diff --git a/src/map_func.h b/src/map_func.h
index 34cc5ee0e..e8ab9036e 100644
--- a/src/map_func.h
+++ b/src/map_func.h
@@ -41,7 +41,7 @@ extern Tile *_m;
*/
extern TileExtended *_me;
-void AllocateMap(uint size_x, uint size_y);
+void AllocateMap(uint size_x, uint size_y, uint layer_count);
/**
* Logarithm of the map size along the X side.
diff --git a/src/misc.cpp b/src/misc.cpp
index c46a1c11d..afbbc7506 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -8,6 +8,7 @@
/** @file misc.cpp Misc functions that shouldn't be here. */
#include "stdafx.h"
+#include "layer_func.h"
#include "landscape.h"
#include "news_func.h"
#include "ai/ai.hpp"
@@ -61,7 +62,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
* related to the new game we're about to start/load. */
UnInitWindowSystem();
- AllocateMap(size_x, size_y);
+ AllocateMap(size_x, size_y, 1 << _settings_game.game_creation.layers);
_pause_mode = PM_UNPAUSED;
_game_speed = 100;
diff --git a/src/rail.h b/src/rail.h
index a0bbb0109..553acf76f 100644
--- a/src/rail.h
+++ b/src/rail.h
@@ -158,7 +158,7 @@ public:
SpriteID signals[SIGTYPE_END][2][2]; ///< signal GUI sprites (type, variant, state)
} gui_sprites;
- struct {
+ struct Cursor {
CursorID rail_ns; ///< Cursor for building rail in N-S direction
CursorID rail_swne; ///< Cursor for building rail in X direction
CursorID rail_ew; ///< Cursor for building rail in E-W direction
diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp
index 07a36d6a3..e85313762 100644
--- a/src/saveload/map_sl.cpp
+++ b/src/saveload/map_sl.cpp
@@ -13,6 +13,7 @@
#include "compat/map_sl_compat.h"
#include "../map_func.h"
+#include "../layer_func.h"
#include "../core/bitmath_func.hpp"
#include "../fios.h"
#include <array>
@@ -21,10 +22,12 @@
static uint32 _map_dim_x;
static uint32 _map_dim_y;
+static uint32 _layer_count;
static const SaveLoad _map_desc[] = {
SLEG_CONDVAR("dim_x", _map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLEG_CONDVAR("dim_y", _map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION),
+ SLEG_CONDVAR("layer_count", _layer_count, SLE_UINT32, SLV_6, SL_MAX_VERSION),
};
struct MAPSChunkHandler : ChunkHandler {
@@ -36,6 +39,7 @@ struct MAPSChunkHandler : ChunkHandler {
_map_dim_x = MapSizeX();
_map_dim_y = MapSizeY();
+ _layer_count = LayerCount();
SlSetArrayIndex(0);
SlGlobList(_map_desc);
@@ -49,7 +53,7 @@ struct MAPSChunkHandler : ChunkHandler {
SlGlobList(slt);
if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many MAPS entries");
- AllocateMap(_map_dim_x, _map_dim_y);
+ AllocateMap(_map_dim_x, _map_dim_y/_layer_count, _layer_count);
}
void LoadCheck(size_t) const override
diff --git a/src/settings_table.cpp b/src/settings_table.cpp
index 4b94a8be7..bf6493391 100644
--- a/src/settings_table.cpp
+++ b/src/settings_table.cpp
@@ -17,6 +17,7 @@
#include "network/network_func.h"
#include "network/core/config.h"
#include "pathfinder/pathfinder_type.h"
+#include "layer_type.h"
#include "genworld.h"
#include "train.h"
#include "news_func.h"
diff --git a/src/settings_type.h b/src/settings_type.h
index 9da2655d6..52cd66359 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -171,6 +171,8 @@ struct GUISettings {
uint16 refresh_rate; ///< How often we refresh the screen (time between draw-ticks).
uint16 fast_forward_speed_limit; ///< Game speed to use when fast-forward is enabled.
+ uint32 layer_view_type; ///< ( )
+
uint16 console_backlog_timeout; ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity.
uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed.
@@ -306,6 +308,7 @@ struct GameCreationSettings {
Year ending_year; ///< scoring end date
uint8 map_x; ///< X size of map
uint8 map_y; ///< Y size of map
+ uint8 layers; ///< map layer count
byte land_generator; ///< the landscape generator
byte oil_refinery_limit; ///< distance oil refineries allowed from map edge
byte snow_line_height; ///< the configured snow line height (deduced from "snow_coverage")
diff --git a/src/station.cpp b/src/station.cpp
index 877f53d4e..a8de1488e 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -11,6 +11,7 @@
#include "company_func.h"
#include "company_base.h"
#include "roadveh.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "viewport_kdtree.h"
#include "date_func.h"
@@ -493,7 +494,7 @@ void StationRect::MakeEmpty()
/**
* Determines whether a given point (x, y) is within a certain distance of
* the station rectangle.
- * @note x and y are in Tile coordinates
+ * @note x and y are in Tile coordinates (in top layer)
* @param x X coordinate
* @param y Y coordinate
* @param distance The maximum distance a point may have (L1 norm)
@@ -512,8 +513,10 @@ bool StationRect::IsEmpty() const
CommandCost StationRect::BeforeAddTile(TileIndex tile, StationRectMode mode)
{
- int x = TileX(tile);
- int y = TileY(tile);
+ /* .
+ * */
+ int x = LayerX(tile);
+ int y = LayerY(tile);
if (this->IsEmpty()) {
/* we are adding the first station tile */
if (mode != ADD_TEST) {
@@ -566,18 +569,25 @@ CommandCost StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRect
*/
/* static */ bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a)
{
+ /* .
+ * */
TileArea ta(TileXY(left_a, top_a), TileXY(right_a, bottom_a));
- for (TileIndex tile : ta) {
- if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
- }
+ FOR_ALL_LAYERS(layer) {
+ ta.tile += layer * LayerSize();
+ for (TileIndex tile : ta) {
+ if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
+ }
+ }
return false;
}
bool StationRect::AfterRemoveTile(BaseStation *st, TileIndex tile)
{
- int x = TileX(tile);
- int y = TileY(tile);
+ /* .
+ * */
+ int x = LayerX(tile);
+ int y = LayerY(tile);
/* look if removed tile was on the bounding rect edge
* and try to reduce the rect by this edge
@@ -626,8 +636,13 @@ bool StationRect::AfterRemoveTile(BaseStation *st, TileIndex tile)
bool StationRect::AfterRemoveRect(BaseStation *st, TileArea ta)
{
- assert(this->PtInExtendedRect(TileX(ta.tile), TileY(ta.tile)));
- assert(this->PtInExtendedRect(TileX(ta.tile) + ta.w - 1, TileY(ta.tile) + ta.h - 1));
+ /* .
+ * */
+ int topx = LayerX(ta.tile);
+ int topy = LayerY(ta.tile);
+
+ assert(this->PtInExtendedRect(topx, topy));
+ assert(this->PtInExtendedRect(topx + ta.w - 1, topy + ta.h - 1));
bool empty = this->AfterRemoveTile(st, ta.tile);
if (ta.w != 1 || ta.h != 1) empty = empty || this->AfterRemoveTile(st, TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1));
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 5a91fa474..d833b0ef7 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -11,6 +11,7 @@
#include "aircraft.h"
#include "bridge_map.h"
#include "cmd_helper.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "viewport_kdtree.h"
#include "command_func.h"
@@ -104,21 +105,24 @@ bool IsHangar(TileIndex t)
* @return Succeeded command (if zero or one station found) or failed command (for two or more stations found).
*/
template <class T>
-CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st)
+CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, bool layers=false)
{
ta.Expand(1);
/* check around to see if there are any stations there owned by the company */
- for (TileIndex tile_cur : ta) {
- if (IsTileType(tile_cur, MP_STATION)) {
- StationID t = GetStationIndex(tile_cur);
- if (!T::IsValidID(t) || Station::Get(t)->owner != company) continue;
- if (closest_station == INVALID_STATION) {
- closest_station = t;
- } else if (closest_station != t) {
- return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
+ FOR_ALL_LAYERS(layer) {
+ for (TileIndex tile_cur : ta) {
+ if (IsTileType(tile_cur, MP_STATION)) {
+ StationID t = GetStationIndex(tile_cur);
+ if (!T::IsValidID(t) || Station::Get(t)->owner != company) continue;
+ if (closest_station == INVALID_STATION) {
+ closest_station = t;
+ } else if (closest_station != t) {
+ return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
+ }
}
}
+ if (!layers) break;
}
*st = (closest_station == INVALID_STATION) ? nullptr : T::Get(closest_station);
return CommandCost();
@@ -1073,11 +1077,13 @@ CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis a
TileArea cur_ta = st->train_station;
/* determine new size of train station region.. */
- int x = std::min(TileX(cur_ta.tile), TileX(new_ta.tile));
- int y = std::min(TileY(cur_ta.tile), TileY(new_ta.tile));
- new_ta.w = std::max(TileX(cur_ta.tile) + cur_ta.w, TileX(new_ta.tile) + new_ta.w) - x;
- new_ta.h = std::max(TileY(cur_ta.tile) + cur_ta.h, TileY(new_ta.tile) + new_ta.h) - y;
- new_ta.tile = TileXY(x, y);
+ /* , ("") .
+ * */
+ int topx = std::min(LayerX(cur_ta.tile), LayerX(new_ta.tile));
+ int topy = std::min(LayerY(cur_ta.tile), LayerY(new_ta.tile));
+ new_ta.w = std::max(LayerX(cur_ta.tile) + cur_ta.w, LayerX(new_ta.tile) + new_ta.w) - topx;
+ new_ta.h = std::max(LayerY(cur_ta.tile) + cur_ta.h, LayerY(new_ta.tile) + new_ta.h) - topy;
+ new_ta.tile = TileXY(topx, topy);
/* make sure the final size is not too big. */
if (new_ta.w > _settings_game.station.station_spread || new_ta.h > _settings_game.station.station_spread) {
@@ -1149,7 +1155,7 @@ void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const Station
* @return command cost with the error or 'okay'
*/
template <class T, StringID error_message>
-CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
+CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, bool layers=false)
{
assert(*st == nullptr);
bool check_surrounding = true;
@@ -1175,7 +1181,7 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
if (check_surrounding) {
/* Make sure there is no more than one other station around us that is owned by us. */
- CommandCost ret = GetStationAround(ta, existing_station, _current_company, st);
+ CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, layers);
if (ret.Failed()) return ret;
}
@@ -1194,9 +1200,9 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
* @param st 'return' pointer for the found station
* @return command cost with the error or 'okay'
*/
-static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
+static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st, bool layers=false)
{
- return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
+ return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, layers);
}
/**
@@ -1332,8 +1338,10 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp
byte numtracks_orig;
Track track;
+ TileIndex top_tile = TopTile(new_location.tile);
st->train_station = new_location;
- st->AddFacility(FACIL_TRAIN, new_location.tile);
+ st->train_station.tile = top_tile;
+ st->AddFacility(FACIL_TRAIN, tile_org);
st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
@@ -1711,11 +1719,16 @@ CommandCost RemoveRailStation(T *st, DoCommandFlag flags, Money removal_cost)
/* determine width and height of platforms */
TileArea ta = st->train_station;
- assert(ta.w != 0 && ta.h != 0);
+ /* TileArea is top finite area */
+ assert(IsTopTile(ta.tile));
+ assert(ta.IsFinite());
CommandCost cost(EXPENSES_CONSTRUCTION);
+ /* Check all layers */
+ FOR_ALL_LAYERS(layer)
/* clear all areas of the station */
- for (TileIndex tile : ta) {
+ for (TileIndex top_tile : ta) {
+ TileIndex tile = top_tile + layer * LayerSize();
/* only remove tiles that are actually train station tiles */
if (st->TileBelongsToRailStation(tile)) {
std::vector<T*> affected_stations; // dummy
@@ -2060,13 +2073,21 @@ CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width,
/* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
if (remove_road && (flags & DC_BANKRUPT)) return CMD_ERROR;
- TileArea roadstop_area(tile, width, height);
+ /* ( ) */
+ TileArea roadstop_area(TopTile(tile), width, height);
+
+ /* TileArea is top finite area */
+ assert(IsTopTile(roadstop_area.tile));
+ assert(roadstop_area.IsFinite());
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
bool had_success = false;
- for (TileIndex cur_tile : roadstop_area) {
+ /* Check all layers */
+ FOR_ALL_LAYERS(layer)
+ for (TileIndex top_tile : roadstop_area) {
+ TileIndex cur_tile = top_tile + layer * LayerSize();
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || GetRoadStopType(cur_tile) != stop_type) continue;
@@ -2230,6 +2251,10 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty
if (w > _settings_game.station.station_spread || h > _settings_game.station.station_spread) {
return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
}
+ /* can't make underground airport */
+ if (IsUnderground(tile)) {
+ return_cmd_error(STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND);
+ }
AirportTileTableIterator iter(as->table[layout], tile);
CommandCost cost = CheckFlatLandAirport(iter, flags);
diff --git a/src/station_gui.cpp b/src/station_gui.cpp
index 923387821..1816d4c8e 100644
--- a/src/station_gui.cpp
+++ b/src/station_gui.cpp
@@ -19,6 +19,7 @@
#include "strings_func.h"
#include "string_func.h"
#include "window_func.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "widgets/dropdown_func.h"
#include "station_base.h"
@@ -2212,20 +2213,27 @@ static const T *FindStationsNearby(TileArea ta, bool distant_join)
_deleted_stations_nearby.clear();
/* Check the inside, to return, if we sit on another station */
- for (TileIndex t : ta) {
- if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
+ FOR_ALL_LAYERS(layer) {
+ for (TileIndex tile : ta) {
+ TileIndex t = TopTile(tile) + layer * LayerSize();
+ if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t)))
+ {
+ if (t == tile) return T::GetByTile(t);
+ AddNearbyStation<T>(t, &ctx);
+ }
+ }
}
/* Look for deleted stations */
for (const BaseStation *st : BaseStation::Iterate()) {
if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
/* Include only within station spread (yes, it is strictly less than) */
- if (std::max(DistanceMax(ta.tile, st->xy), DistanceMax(TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1), st->xy)) < _settings_game.station.station_spread) {
+ if (std::max(DistanceMax(TopTile(ta.tile), TopTile(st->xy)), DistanceMax(TILE_ADDXY(TopTile(ta.tile), ta.w - 1, ta.h - 1), TopTile(st->xy))) < _settings_game.station.station_spread) {
_deleted_stations_nearby.push_back({st->xy, st->index});
/* Add the station when it's within where we're going to build */
- if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
- IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
+ if (IsInsideBS(LayerX(st->xy), LayerX(ctx.tile), ctx.w) &&
+ IsInsideBS(LayerY(st->xy), LayerY(ctx.tile), ctx.h)) {
AddNearbyStation<T>(st->xy, &ctx);
}
}
@@ -2238,8 +2246,11 @@ static const T *FindStationsNearby(TileArea ta, bool distant_join)
if (distant_join && std::min(ta.w, ta.h) >= _settings_game.station.station_spread) return nullptr;
uint max_dist = distant_join ? _settings_game.station.station_spread - std::min(ta.w, ta.h) : 1;
- TileIndex tile = TileAddByDir(ctx.tile, DIR_N);
- CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
+ FOR_ALL_LAYERS(layer) {
+ ctx.tile = TopTile(ctx.tile) + layer * LayerSize();
+ TileIndex tile = TileAddByDir(ctx.tile, DIR_N);
+ CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
+ }
return nullptr;
}
diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini
index 79fac7513..f4b7258a0 100644
--- a/src/table/settings/game_settings.ini
+++ b/src/table/settings/game_settings.ini
@@ -419,3 +419,12 @@ var = gui.sg_new_nonstop
from = SLV_22
to = SLV_93
def = false
+
+[SDT_VAR]
+base = GameSettings
+var = game_creation.layers
+type = SLE_UINT8
+flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
+def = 1
+min = MIN_LAYER_COUNT_BITS
+max = MAX_LAYER_COUNT_BITS
diff --git a/src/table/settings/gui_settings.ini b/src/table/settings/gui_settings.ini
index ad18f8318..5b1a63001 100644
--- a/src/table/settings/gui_settings.ini
+++ b/src/table/settings/gui_settings.ini
@@ -848,3 +848,11 @@ min = 1
max = 65535
cat = SC_EXPERT
+[SDTC_VAR]
+var = gui.layer_view_type
+type = SLE_UINT32
+flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
+def = 0
+min = 0
+max = UINT32_MAX
+proc = RedrawScreen
diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp
index 21df5ccbe..c6c43799a 100644
--- a/src/terraform_cmd.cpp
+++ b/src/terraform_cmd.cpp
@@ -8,6 +8,7 @@
/** @file terraform_cmd.cpp Commands related to terraforming. */
#include "stdafx.h"
+#include "layer_func.h"
#include "command_func.h"
#include "tunnel_map.h"
#include "bridge_map.h"
@@ -248,6 +249,10 @@ std::tuple<CommandCost, Money, TileIndex> CmdTerraformLand(DoCommandFlag flags,
if (z_N > z_min) tileh |= SLOPE_N;
if (pass == 0) {
+ /* Terrafrom enable only top layer */
+ if (IsUnderground(tile)) {
+ return_cmd_error(STR_ERROR_UNDERGROUND_CAN_T_TERRAFORM);
+ }
/* Check if bridge would take damage */
if (IsBridgeAbove(t)) {
int bridge_height = GetBridgeHeight(GetSouthernBridgeEnd(t));
diff --git a/src/tgp.cpp b/src/tgp.cpp
index 511b998e2..eef52853e 100644
--- a/src/tgp.cpp
+++ b/src/tgp.cpp
@@ -12,6 +12,7 @@
#include "clear_map.h"
#include "void_map.h"
#include "genworld.h"
+#include "layer_func.h"
#include "core/random_func.hpp"
#include "landscape_type.h"
@@ -167,6 +168,8 @@ struct HeightMap
int dim_x; //< height map size_x MapSizeX() + 1
int size_x; //< MapSizeX()
int size_y; //< MapSizeY()
+ int map_x; //< MapSizeX()
+ int map_y; //< MapSizeY()
/**
* Height map accessor
@@ -322,8 +325,11 @@ static inline bool AllocHeightMap()
{
assert(_height_map.h.empty());
- _height_map.size_x = MapSizeX();
- _height_map.size_y = MapSizeY();
+ _height_map.map_x = MapSizeX();
+ _height_map.map_y = MapSizeY();
+
+ _height_map.size_x = LayerSizeX();
+ _height_map.size_y = LayerSizeY();
/* Allocate memory block for height map row pointers */
size_t total_size = (_height_map.size_x + 1) * (_height_map.size_y + 1);
@@ -1004,8 +1010,8 @@ void GenerateTerrainPerlin()
/* First make sure the tiles at the north border are void tiles if needed. */
if (_settings_game.construction.freeform_edges) {
- for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0));
- for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y));
+ for (uint x = 0; x < _height_map.map_x; x++) MakeVoid(x);
+ for (uint y = 0; y < _height_map.map_y - 1; y++) MakeVoid(_height_map.size_x * y);
}
int max_height = H2I(TGPGetMaxHeight());
diff --git a/src/tile_map.h b/src/tile_map.h
index b6c715e8a..4bd584539 100644
--- a/src/tile_map.h
+++ b/src/tile_map.h
@@ -134,7 +134,7 @@ static inline void SetTileType(TileIndex tile, TileType type)
/* VOID tiles (and no others) are exactly allowed at the lower left and right
* edges of the map. If _settings_game.construction.freeform_edges is true,
* the upper edges of the map are also VOID tiles. */
- assert(IsInnerTile(tile) == (type != MP_VOID));
+ assert(IsInnerTile(tile) == (type != MP_VOID)); // was commented in <underground>
SB(_m[tile].type, 4, 4, type);
}
diff --git a/src/tilearea_type.h b/src/tilearea_type.h
index b6af998a7..8a9292ad6 100644
--- a/src/tilearea_type.h
+++ b/src/tilearea_type.h
@@ -44,6 +44,16 @@ struct OrthogonalTileArea {
this->h = 0;
}
+ inline bool IsEmpty() const
+ {
+ return (w==0 && h==0);
+ }
+
+ inline bool IsFinite() const
+ {
+ return (w!=0 && h!=0);
+ }
+
bool Intersects(const OrthogonalTileArea &ta) const;
bool Contains(TileIndex tile) const;
diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp
index 13c52f2b4..068a1962e 100644
--- a/src/toolbar_gui.cpp
+++ b/src/toolbar_gui.cpp
@@ -11,7 +11,10 @@
#include "gui.h"
#include "window_gui.h"
#include "window_func.h"
+#include "layer_gui.h"
+#include "layer_func.h"
#include "viewport_func.h"
+#include "landscape.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "rail_gui.h"
@@ -21,6 +24,7 @@
#include "vehicle_func.h"
#include "sound_func.h"
#include "terraform_gui.h"
+#include "underground_gui.h"
#include "strings_func.h"
#include "company_func.h"
#include "company_gui.h"
@@ -980,6 +984,48 @@ static CallBackFunction ToolbarForestClick(Window *w)
return CBF_NONE;
}
+bool ScrollWindowRel(ViewportData *viewport, int dx, int dy)
+{
+ Point pt = RemapCoords(dx, dy, 0);
+
+ viewport->dest_scrollpos_x += pt.x;
+ viewport->dest_scrollpos_y += pt.y;
+ return true;
+}
+
+void SelectLayer(ViewportData *viewport, int layer)
+{
+ if (layer < 0) layer = 0;
+ if (uint(layer) >= LayerCount()) layer = LayerCount() - 1;
+
+ int delta_layer = calculateLayer(viewport) - layer;
+ bool res = ScrollWindowRel(viewport, 0, -delta_layer * LayerSizeY() * TILE_SIZE);
+}
+
+void SelectLayerInMainWindow(int layer)
+{
+ Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+ SelectLayer(w->viewport, layer);
+ w->InvalidateData();
+}
+
+/**
+ * Handle click on the entry in the underground menu.
+ *
+ * @param index Menu entry clicked.
+ */
+static void ToMenuClickUnderground(int index)
+{
+ switch (index) {
+ case 4: ShowUndergroundToolbar(); break;
+
+ case 6: SelectLayerInMainWindow(index-6); break;
+ case 7: SelectLayerInMainWindow(index-6); break;
+ case 8: SelectLayerInMainWindow(index-6); break;
+ case 9: SelectLayerInMainWindow(index-6); break;
+ }
+}
+
/**
* Handle click on the entry in the landscaping menu.
*
@@ -993,6 +1039,7 @@ static CallBackFunction MenuClickForest(int index)
case 1: ShowBuildTreesToolbar(); break;
case 2: return SelectSignTool();
}
+ ToMenuClickUnderground(index);
return CBF_NONE;
}
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 405a83cce..450582941 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -12,6 +12,7 @@
#include "road_internal.h" /* Cleaning up road bits */
#include "road_cmd.h"
#include "landscape.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "viewport_kdtree.h"
#include "cmd_helper.h"
@@ -2328,6 +2329,9 @@ static inline bool CanBuildHouseHere(TileIndex tile, bool noslope)
Slope slope = GetTileSlope(tile);
if ((noslope && slope != SLOPE_FLAT) || IsSteepSlope(slope)) return false;
+ /* */
+ if (IsUnderground(tile)) return false;
+
/* at least one RoadTypes allow building the house here? */
if (!RoadTypesAllowHouseHere(tile)) return false;
@@ -2761,6 +2765,8 @@ CommandCost CmdRenameTown(DoCommandFlag flags, TownID town_id, const std::string
InvalidateWindowData(WC_TOWN_DIRECTORY, 0, TDIWD_FORCE_RESORT);
ClearAllStationCachedNames();
ClearAllIndustryCachedNames();
+ InvalidateWindowClassesData(WC_TOWN_VIEW);
+ InvalidateWindowClassesData(WC_INDUSTRY_VIEW);
UpdateAllStationVirtCoords();
}
return CommandCost();
diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp
index c98758062..bd112f31b 100644
--- a/src/tree_cmd.cpp
+++ b/src/tree_cmd.cpp
@@ -11,6 +11,7 @@
#include "clear_map.h"
#include "landscape.h"
#include "tree_map.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "command_func.h"
#include "town.h"
@@ -74,7 +75,8 @@ static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert)
case MP_CLEAR:
return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && GetRawClearGround(tile) != CLEAR_ROCKS &&
- (allow_desert || !IsClearGround(tile, CLEAR_DESERT));
+ (allow_desert || !IsClearGround(tile, CLEAR_DESERT))
+ && !IsUnderground(tile);
default: return false;
}
@@ -392,6 +394,10 @@ CommandCost CmdPlantTree(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
CommandCost cost(EXPENSES_OTHER);
if (start_tile >= MapSize()) return CMD_ERROR;
+
+ /* tree only top layer */
+ if (IsUnderground(start_tile)) return CMD_ERROR;
+
/* Check the tree type within the current climate */
if (tree_to_plant != TREE_INVALID && !IsInsideBS(tree_to_plant, _tree_base_by_landscape[_settings_game.game_creation.landscape], _tree_count_by_landscape[_settings_game.game_creation.landscape])) return CMD_ERROR;
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 543ac8b28..3e2d4206a 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -13,6 +13,7 @@
#include "stdafx.h"
#include "newgrf_object.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "cmd_helper.h"
#include "command_func.h"
@@ -303,6 +304,10 @@ CommandCost CmdBuildBridge(DoCommandFlag flags, TileIndex tile_end, TileIndex ti
return_cmd_error(STR_ERROR_CAN_T_START_AND_END_ON);
}
+ if (IsUnderground(tile_start) || IsUnderground(tile_end)) {
+ return_cmd_error(STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND);
+ }
+
Axis direction;
if (TileX(tile_start) == TileX(tile_end)) {
direction = AXIS_Y;
@@ -693,6 +698,12 @@ CommandCost CmdBuildTunnel(DoCommandFlag flags, TileIndex start_tile, TransportT
for (;;) {
end_tile += delta;
if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER);
+
+
+ if (IsUnderground(start_tile) || IsUnderground(end_tile)) {
+ return_cmd_error(STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND);
+ }
+
end_tileh = GetTileSlope(end_tile, &end_z);
if (start_z == end_z) break;
diff --git a/src/underground_gui.cpp b/src/underground_gui.cpp
new file mode 100644
index 000000000..05ac87bab
--- /dev/null
+++ b/src/underground_gui.cpp
@@ -0,0 +1,321 @@
+/* $Id: terraform_gui.cpp 23547 2011-12-16 18:21:13Z truebrain $ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file terraform_gui.cpp GUI related to terraforming the map. */
+
+#include "stdafx.h"
+#include "clear_map.h"
+#include "company_func.h"
+#include "company_base.h"
+#include "gui.h"
+#include "window_gui.h"
+#include "window_func.h"
+#include "layer_func.h"
+#include "viewport_func.h"
+#include "command_func.h"
+#include "signs_func.h"
+#include "sound_func.h"
+#include "base_station_base.h"
+#include "textbuf_gui.h"
+#include "genworld.h"
+#include "tree_map.h"
+#include "landscape_type.h"
+#include "tilehighlight_func.h"
+#include "strings_func.h"
+#include "newgrf_object.h"
+#include "newgrf_station.h"
+#include "object.h"
+#include "hotkeys.h"
+#include "engine_base.h"
+
+#include "widgets/underground_widget.h"
+
+#include "table/strings.h"
+#include "error.h"
+
+void ShowError(TileIndex tile, CommandCost res, uint32 cmd)
+{
+ int x = TileX(tile) * TILE_SIZE;
+ int y = TileY(tile) * TILE_SIZE;
+ StringID error_part1 = GB(cmd, 16, 16);
+
+ if (IsLocalCompany() && error_part1 != 0) {
+ ShowErrorMessage(error_part1, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackGRF(), res.GetTextRefStackSize(), res.GetTextRefStack());
+ }
+}
+
+
+/**
+ * Place a escalator.
+ * @param tile Position to place or start dragging a station.
+ */
+static void PlaceUnderground_Escalator(TileIndex tile)
+{
+ RailType railtype = RAILTYPE_RAIL;
+ Axis orientation = AXIS_X;
+ StationClassID station_class = STAT_CLASS_DFLT;
+ byte station_type = 0;
+
+ uint32 p1 = railtype | orientation << 4 | 1 << 8 | 1 << 16 | _ctrl_pressed << 24;
+ uint32 p2 = station_class | station_type << 8 | INVALID_STATION << 16;
+
+ int w = 1;
+ int h = 1;
+
+ uint top_tile = TopTile(tile);
+ uint base_tile = tile;
+ bool from_top = false; //
+
+ if (top_tile == base_tile)
+ {
+ from_top = true;
+ base_tile += LayerSize();
+ };
+
+ uint32 cmdS = CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_UNDERGROUND_CAN_T_BUILD_PART);
+ uint32 cmdT = CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_UNDERGROUND_CAN_T_BUILD_TOP_PART);
+ uint32 cmdB = CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_UNDERGROUND_CAN_T_BUILD_BOTTOM_PART);
+ CommandContainer cmdTop = { top_tile, p1, p2, cmdT, CcStation, "" };
+ CommandContainer cmdBase = { base_tile, p1, p2, cmdB, CcStation, "" };
+
+ DoCommandFlag flags = DC_AUTO | DC_NO_WATER;
+ CommandCost resTop;
+ CommandCost res;
+
+ // :
+ resTop=DoCommand(&cmdTop, flags | DC_QUERY_COST);
+ if (resTop.Failed())
+ {
+ ShowError(tile, resTop, cmdT);
+ return;
+ }
+
+ res=DoCommand(&cmdBase, flags | DC_QUERY_COST);
+ if (res.Failed())
+ {
+ ShowError(tile, res, cmdB);
+ return;
+ }
+
+ res.AddCost(resTop.GetCost());
+ if (_shift_pressed || !CheckCompanyHasMoney(res))
+ {
+ if (res.Failed()) ShowError(tile, res, cmdS);
+ else ShowEstimatedCostOrIncome(res.GetCost(), TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
+ return;
+ }
+
+ // .
+ // ,
+ CommandContainer *cmd1 = from_top ? &cmdTop : &cmdBase;
+ CommandContainer *cmd2 = from_top ? &cmdBase : &cmdTop;
+
+ // ( )
+ res=DoCommand(cmd1, flags | DC_EXEC);
+ assert(!res.Failed());
+
+ //
+ StationID station = GetStationIndex(cmd1->tile);
+ cmd2->p2 = station_class | station_type << 8 | station << 16;
+
+ // ( )
+ res=DoCommand(cmd2, flags | DC_EXEC);
+ assert(!res.Failed());
+}
+
+/** Underground toolbar managing class. */
+struct UndergroundToolbarWindow : Window {
+ int last_user_action; ///< Last started user action.
+
+ UndergroundToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
+ {
+ /* This is needed as we like to have the tree available on OnInit. */
+ this->CreateNestedTree(desc);
+ this->FinishInitNested(window_number);
+ this->last_user_action = WIDGET_LIST_END;
+ }
+
+ ~UndergroundToolbarWindow()
+ {
+ }
+
+ virtual void OnInit()
+ {
+ }
+
+ virtual void OnClick(Point pt, int widget, int click_count)
+ {
+ switch (widget) {
+ case WID_UT_BUILD_ESCALATOR: // WID_TT_BUILD_ESCALATOR
+ HandlePlacePushButton(this, WID_UT_BUILD_ESCALATOR, SPR_CURSOR_RAIL_STATION, HT_RECT);
+ this->last_user_action = widget;
+ break;
+
+ case WID_UT_DEMOLISH: // Demolish aka dynamite button
+ HandlePlacePushButton(this, WID_UT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL);
+ this->last_user_action = widget;
+ break;
+
+ default: NOT_REACHED();
+ }
+ }
+
+ virtual void OnTimeout()
+ {
+ }
+
+ virtual void OnPlaceObject(Point pt, TileIndex tile)
+ {
+ switch (this->last_user_action) {
+ case WID_UT_BUILD_ESCALATOR:
+ PlaceUnderground_Escalator(tile);
+ break;
+
+ case WID_UT_DEMOLISH: // Demolish aka dynamite button
+ PlaceProc_DemolishArea(tile);
+ break;
+
+ default: NOT_REACHED();
+ }
+ }
+
+ virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
+ {
+ VpSelectTilesWithMethod(pt.x, pt.y, select_method);
+ }
+
+ virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
+ {
+ Point pt = GetToolbarAlignedWindowPosition(sm_width);
+ pt.y += sm_height;
+ return pt;
+ }
+
+ virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
+ {
+ if (pt.x != -1) {
+ switch (select_proc) {
+ default: NOT_REACHED();
+ case DDSP_DEMOLISH_AREA:
+ GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
+ break;
+ }
+ }
+ }
+
+ virtual void OnPlaceObjectAbort()
+ {
+ CloseWindowById(WC_BUILD_OBJECT, 0);
+ this->RaiseButtons();
+ }
+
+ static HotkeyList hotkeys;
+};
+
+/**
+ * Handler for global hotkeys of the UndergroundToolbarWindow.
+ * @param hotkey Hotkey
+ * @return ES_HANDLED if hotkey was accepted.
+ */
+static EventState UndergroundToolbarGlobalHotkeys(int hotkey)
+{
+ if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED;
+/* TODO Window *w = ShowAIDebugWindow(INVALID_COMPANY);
+ if (w == NULL) return ES_NOT_HANDLED;
+ return w->OnHotkey(hotkey); */
+}
+
+EventState UndergroundToolbarGlobalHotkeys(uint16 key, uint16 keycode)
+{
+/* int num = CheckHotkeyMatch<UndergroundToolbarWindow>(_underground_hotkeys, keycode, NULL, true);
+ if (num == -1) return ES_NOT_HANDLED;
+ Window *w = ShowUndergroundToolbar(NULL); TODO
+ if (w == NULL) return ES_NOT_HANDLED;
+ return w->OnKeyPress(key, keycode);
+}
+ int num = CheckHotkeyMatch(underground_hotkeys, keycode, this);
+ if (num == -1) return ES_NOT_HANDLED;
+ this->OnClick(Point(), num, 1);
+ return ES_HANDLED;
++static EventState AIDebugGlobalHotkeys(int hotkey)
++{
++ if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED;
++ Window *w = ShowAIDebugWindow(INVALID_COMPANY);
++ if (w == NULL) return ES_NOT_HANDLED;
++ return w->OnHotkey(hotkey);
++}
+-EventState AIDebugGlobalHotkeys(uint16 key, uint16 keycode)
+-{
+- int num = CheckHotkeyMatch<AIDebugWindow>(_aidebug_hotkeys, keycode, NULL, true);
+- if (num == -1) return ES_NOT_HANDLED;
+- Window *w = ShowAIDebugWindow(INVALID_COMPANY);
+- if (w == NULL) return ES_NOT_HANDLED;
+- return w->OnKeyPress(key, keycode);
+-}
+- */
+ return ES_NOT_HANDLED;
+}
+
+static Hotkey underground_hotkeys[] = {
+ Hotkey('D' | WKC_GLOBAL_HOTKEY, "dynamite", WID_UT_DEMOLISH),
+ Hotkey('0', "placeescalator", WID_UT_BUILD_ESCALATOR),
+ HOTKEY_LIST_END
+};
+HotkeyList UndergroundToolbarWindow::hotkeys("undergroundtoolbar", underground_hotkeys, UndergroundToolbarGlobalHotkeys);
+
+static const NWidgetPart _nested_underground_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_UNDERGROUND_BUILD, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_UT_BUILD_ESCALATOR),
+ SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_RAIL_STATION, STR_UNDERGROUND_TOOLTIP_ESCALATOR),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(64, 22), EndContainer(),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_UT_DEMOLISH), SetMinimalSize(22, 22),
+ SetFill(0, 1), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
+ EndContainer(),
+};
+
+static WindowDesc _underground_desc(
+ WDP_MANUAL, "undergroundtoolbar", 0, 0,
+ WC_UNDERGROUND, WC_NONE,
+ WDF_CONSTRUCTION,
+ _nested_underground_widgets, lengthof(_nested_underground_widgets)
+);
+
+/**
+ * Show the toolbar for terraforming in the game.
+ * @param link The toolbar we might want to link to.
+ * @return The allocated toolbar.
+ */
+Window *ShowUndergroundToolbar(Window *link)
+{
+ if (!Company::IsValidID(_local_company)) return NULL;
+
+ Window *w;
+ if (link == NULL) {
+ w = AllocateWindowDescFront<UndergroundToolbarWindow>(&_underground_desc, 0);
+ return w;
+ }
+
+ /* Delete the terraform toolbar to place it again. */
+ CloseWindowById(WC_UNDERGROUND, 0, true);
+ w = AllocateWindowDescFront<UndergroundToolbarWindow>(&_underground_desc, 0);
+ /* Align the terraform toolbar under the main toolbar. */
+ w->top -= w->height;
+ w->SetDirty();
+ /* Put the linked toolbar to the left / right of it. */
+ link->left = w->left + (_current_text_dir == TD_RTL ? w->width : -link->width);
+ link->top = w->top;
+ link->SetDirty();
+
+ return w;
+}
diff --git a/src/underground_gui.h b/src/underground_gui.h
new file mode 100644
index 000000000..81b119545
--- /dev/null
+++ b/src/underground_gui.h
@@ -0,0 +1,19 @@
+/* $Id: underground_gui.h 21608 2012-09-08 1:13:14 constructor $ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file underground_gui.h GUI stuff related to terraforming. */
+
+#ifndef UNDERGROUND_GUI_H
+#define UNDERGROUND_GUI_H
+
+#include "window_type.h"
+
+Window *ShowUndergroundToolbar(Window *link = NULL);
+
+#endif /* UNDERGROUND_GUI_H */
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 120c4a259..0e338737c 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -13,6 +13,7 @@
#include "ship.h"
#include "spritecache.h"
#include "timetable.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "news_func.h"
#include "command_func.h"
@@ -1158,6 +1159,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi)
while (v != nullptr) {
+ if (LayerIndex(v->tile) == dpi->layer)
if (!(v->vehstatus & VS_HIDDEN) &&
l <= v->coord.right + xb &&
t <= v->coord.bottom + yb &&
diff --git a/src/viewport.cpp b/src/viewport.cpp
index 69db0aeab..d8fd85b5a 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -62,6 +62,8 @@
#include "stdafx.h"
#include "landscape.h"
+#include "layer_gui.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "station_base.h"
#include "waypoint_base.h"
@@ -1247,24 +1249,27 @@ static void ViewportAddLandscape()
int min_visible_height = viewport_y - (_vd.dpi.top + _vd.dpi.height);
bool tile_visible = min_visible_height <= 0;
- if (tile_type != MP_VOID) {
- /* Is tile with buildings visible? */
- if (min_visible_height < MAX_TILE_EXTENT_TOP) tile_visible = true;
-
- if (IsBridgeAbove(tile_info.tile)) {
- /* Is the bridge visible? */
- TileIndex bridge_tile = GetNorthernBridgeEnd(tile_info.tile);
- int bridge_height = ZOOM_LVL_BASE * (GetBridgePixelHeight(bridge_tile) - TilePixelHeight(tile_info.tile));
- if (min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP) tile_visible = true;
- }
+ /* */
+ if (LayerIndex(tile_info.tile) == _vd.dpi.layer) {
+ if (tile_type != MP_VOID) {
+ /* Is tile with buildings visible? */
+ if (min_visible_height < MAX_TILE_EXTENT_TOP) tile_visible = true;
+
+ if (IsBridgeAbove(tile_info.tile)) {
+ /* Is the bridge visible? */
+ TileIndex bridge_tile = GetNorthernBridgeEnd(tile_info.tile);
+ int bridge_height = ZOOM_LVL_BASE * (GetBridgePixelHeight(bridge_tile) - TilePixelHeight(tile_info.tile));
+ if (min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP) tile_visible = true;
+ }
- /* Would a higher bridge on a more southern tile be visible?
- * If yes, we need to loop over more rows to possibly find one. */
- if (min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
- } else {
- /* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
- * so we need to loop over more rows to possibly find one. */
- if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
+ /* Would a higher bridge on a more southern tile be visible?
+ * If yes, we need to loop over more rows to possibly find one. */
+ if (min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
+ } else {
+ /* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
+ * so we need to loop over more rows to possibly find one. */
+ if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
+ }
}
if (tile_visible) {
@@ -1738,6 +1743,9 @@ void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom
_vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
+ /* ( ) */
+ _vd.dpi.layer = calculateLayer(vp);
+
ViewportAddLandscape();
ViewportAddVehicles(&_vd.dpi);
diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp
index c02a1e9b4..be95c096e 100644
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -10,6 +10,7 @@
#include "stdafx.h"
#include "cmd_helper.h"
#include "landscape.h"
+#include "layer_func.h"
#include "viewport_func.h"
#include "command_func.h"
#include "town.h"
@@ -468,6 +469,11 @@ CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_t
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
+ /* can't make underground water */
+ if (IsUnderground(current_tile)) {
+ return_cmd_error(STR_ERROR_UNDERGROUND_CAN_T_BUILD_UNDER_GROUND);
+ }
+
bool water = IsWaterTile(current_tile);
/* Outside the editor, prevent building canals over your own or OWNER_NONE owned canals */
@@ -1103,6 +1109,9 @@ void DoFloodTile(TileIndex target)
{
assert(!IsTileType(target, MP_WATER));
+ /* */
+ if (IsUnderground(target)) return;
+
bool flooded = false; // Will be set to true if something is changed.
Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
@@ -1261,7 +1270,7 @@ void ConvertGroundTilesIntoWaterTiles()
for (TileIndex tile = 0; tile < MapSize(); ++tile) {
Slope slope = GetTileSlope(tile, &z);
- if (IsTileType(tile, MP_CLEAR) && z == 0) {
+ if (IsTileType(tile, MP_CLEAR) && z == 0 && !IsUnderground(tile)) {
/* Make both water for tiles at level 0
* and make shore, as that looks much better
* during the generation. */
diff --git a/src/widgets/genworld_widget.h b/src/widgets/genworld_widget.h
index 6644dc1aa..a059a2953 100644
--- a/src/widgets/genworld_widget.h
+++ b/src/widgets/genworld_widget.h
@@ -19,6 +19,7 @@ enum GenerateLandscapeWidgets {
WID_GL_MAPSIZE_X_PULLDOWN, ///< Dropdown 'map X size'.
WID_GL_MAPSIZE_Y_PULLDOWN, ///< Dropdown 'map Y size'.
+ WID_GL_LAYER_COUNT_PULLDOWN, ///< Dropdown 'map layer count'.
WID_GL_TOWN_PULLDOWN, ///< Dropdown 'No. of towns'.
WID_GL_TOWNNAME_DROPDOWN, ///< Dropdown 'Townnames'.
@@ -74,6 +75,7 @@ enum CreateScenarioWidgets {
WID_CS_RANDOM_WORLD, ///< Generate random land button
WID_CS_MAPSIZE_X_PULLDOWN, ///< Pull-down arrow for x map size.
WID_CS_MAPSIZE_Y_PULLDOWN, ///< Pull-down arrow for y map size.
+ WID_CS_LAYER_COUNT_PULLDOWN, ///< Pull-down arrow for map layer count.
WID_CS_START_DATE_DOWN, ///< Decrease start year (start earlier).
WID_CS_START_DATE_TEXT, ///< Clickable start date value.
WID_CS_START_DATE_UP, ///< Increase start year (start later).
diff --git a/src/widgets/underground_widget.h b/src/widgets/underground_widget.h
new file mode 100644
index 000000000..522c9db8b
--- /dev/null
+++ b/src/widgets/underground_widget.h
@@ -0,0 +1,21 @@
+/* $Id: terraform_widget.h 23600 2011-12-19 20:46:17Z truebrain $ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file terraform_widget.h Types related to the terraform widgets. */
+
+#ifndef WIDGETS_UNDERGROUND_WIDGET_H
+#define WIDGETS_UNDERGROUND_WIDGET_H
+
+/** Widgets of the #TerraformToolbarWindow class. */
+enum UndergroundToolbarWidgets {
+ WID_UT_BUILD_ESCALATOR, ///< Build escalator
+ WID_UT_DEMOLISH, ///< Demolish aka dynamite button.
+};
+
+#endif /* WIDGETS_UNDERGROUND_WIDGET_H */
diff --git a/src/window_type.h b/src/window_type.h
index 6eb9573b6..cfaa1e878 100644
--- a/src/window_type.h
+++ b/src/window_type.h
@@ -441,6 +441,12 @@ enum WindowClass {
WC_SCEN_LAND_GEN,
/**
+ * Underground (in game); %Window numbers:
+ * - 0 = #UndergroundToolbarWidgets
+ */
+ WC_UNDERGROUND,
+
+ /**
* Generate landscape (newgame); %Window numbers:
* - GLWM_SCENARIO = #CreateScenarioWidgets
* - #GenerateLandscapeWindowMode = #GenerateLandscapeWidgets