Add function to infer biome type to Maps module

This moves code intended to infer biome type currently living in a
couple of plugins into the Maps module, so that this code can be shared
more easily by multiple plugins, as discussed in #1392.
develop
Kelly Kinkade 2018-08-15 20:28:18 -05:00
parent e56cb2a25a
commit 59d70d6687
2 changed files with 403 additions and 0 deletions

@ -36,6 +36,7 @@ distribution.
#include "BitArray.h" #include "BitArray.h"
#include "modules/Materials.h" #include "modules/Materials.h"
#include "df/biome_type.h"
#include "df/block_flags.h" #include "df/block_flags.h"
#include "df/feature_type.h" #include "df/feature_type.h"
#include "df/flow_type.h" #include "df/flow_type.h"
@ -333,6 +334,12 @@ extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, d
DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2); DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2);
DFHACK_EXPORT bool canStepBetween(df::coord pos1, df::coord pos2); DFHACK_EXPORT bool canStepBetween(df::coord pos1, df::coord pos2);
DFHACK_EXPORT df::enums::biome_type::biome_type GetBiomeType(int world_coord_x, int world_coord_y);
DFHACK_EXPORT df::enums::biome_type::biome_type GetBiomeTypeWithRef(int world_coord_x, int world_coord_y, int world_ref_y_coord);
} }
} }
#endif #endif

@ -46,6 +46,7 @@ using namespace std;
#include "modules/MapCache.h" #include "modules/MapCache.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "df/biome_type.h"
#include "df/block_burrow.h" #include "df/block_burrow.h"
#include "df/block_burrow_link.h" #include "df/block_burrow_link.h"
#include "df/block_square_event_grassst.h" #include "df/block_square_event_grassst.h"
@ -711,3 +712,398 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
return false; return false;
} }
/* The code below is a heavily refactored version of code found at
https://github.com/ragundo/exportmaps/blob/master/cpp/df_utils/biome_type.cpp.
*/
/*
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
namespace {
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality(df::region_map_entry& region,
int y_pos
)
{
int flip_latitude = df::global::world->world_data->flip_latitude;
bool is_possible_tropical_area_by_latitude = false;
bool is_tropical_area_by_latitude = false;
if (flip_latitude == -1) // NO POLES
{
// If there're no poles, tropical area is determined by temperature
is_possible_tropical_area_by_latitude = region.temperature >= 75;
is_tropical_area_by_latitude = region.temperature >= 85;
}
else
{
int v6;
df::world_data* wdata = df::global::world->world_data;
if (flip_latitude == 0) // NORTH POLE ONLY
{
v6 = y_pos;
}
else if (flip_latitude == 1) // SOUTH_POLE ONLY
{
v6 = df::global::world->world_data->world_height - y_pos - 1;
}
else if (flip_latitude == 2) // BOTH POLES
{
if (y_pos < wdata->world_height / 2)
v6 = 2 * y_pos;
else
{
v6 = wdata->world_height + 2 * (wdata->world_height / 2 - y_pos) - 1;
if (v6 < 0)
v6 = 0;
if (v6 >= wdata->world_height)
v6 = wdata->world_height - 1;
}
}
if (wdata->world_height == 17)
v6 *= 16;
else if (wdata->world_height == 33)
v6 *= 8;
else if (wdata->world_height == 65)
v6 *= 4;
else if (wdata->world_height == 129)
v6 *= 2;
is_possible_tropical_area_by_latitude = v6 > 170;
is_tropical_area_by_latitude = v6 >= 200;
}
return std::pair<bool, bool>(is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude
);
}
//----------------------------------------------------------------------------//
// Utility function
//
// return some unknow parameter as a percentage
//----------------------------------------------------------------------------//
int get_region_parameter(int y,
int x
)
{
int world_height = df::global::world->world_data->world_height;
if (world_height > 65) // Medium and large worlds
{
// access to region 2D array
df::region_map_entry& region = df::global::world->world_data->region_map[x][y];
int flip_latitude = df::global::world->world_data->flip_latitude;
int rainfall = region.rainfall;
int result;
int y_pos = y;
int ypos = y_pos;
if (flip_latitude == -1) // NO POLES
return 100;
else if (flip_latitude == 1) // SOUTH POLE
ypos = world_height - y_pos - 1;
else if (flip_latitude == 2) // NORTH & SOUTH POLE
{
if (ypos < world_height / 2)
ypos *= 2;
else
{
ypos = world_height + 2 * (world_height / 2 - ypos) - 1;
if (ypos < 0)
ypos = 0;
if (ypos >= world_height)
ypos = world_height - 1;
}
}
int latitude; // 0 - 256 (size of a large world)
switch (world_height)
{
case 17: // Pocket world
latitude = 16 * ypos;
break;
case 33: // Smaller world
latitude = 8 * ypos;
break;
case 65: // Small world
latitude = 4 * ypos;
break;
case 129: // Medium world
latitude = 2 * ypos;
break;
default: // Large world
latitude = ypos;
break;
}
// latitude > 220
if ((latitude - 171) > 49)
return 100;
// Latitude between 191 and 200
if ((latitude > 190) && (latitude < 201))
return 0;
// Latitude between 201 and 220
if ((latitude > 190) && (latitude >= 201))
result = rainfall + 16 * (latitude - 207);
else
// Latitude between 0 and 190
result = (16 * (184 - latitude) - rainfall);
if (result < 0)
return 0;
if (result > 100)
return 100;
return result;
}
return 100;
}
}
/*****************************************************************************
Module main function.
Return the biome type, given a position coordinate expressed in world_tiles
The world ref coordinates are used for tropicality determination and may refer
to a tile neighboring the "official" one.
*****************************************************************************/
df::enums::biome_type::biome_type Maps::GetBiomeTypeWithRef(int world_coord_x,
int world_coord_y,
int world_ref_coord_y
)
{
// Biome is per region, so get the region where this biome exists
df::region_map_entry& region = df::global::world->world_data->region_map[world_coord_x][world_coord_y];
// Check if the y reference position coordinate belongs to a tropical area
std::pair<bool, bool> p = check_tropicality(region,
world_ref_coord_y
);
bool is_possible_tropical_area_by_latitude = p.first;
bool is_tropical_area_by_latitude = p.second;
int parameter = get_region_parameter(world_coord_y, world_coord_x);
// Begin the discrimination
if (region.flags.is_set(df::region_map_entry_flags::is_lake)) // is it a lake?
{
// salinity values tell us the lake type
// greater than 66 is a salt water lake
// between 33 and 65 is a brackish water lake
// less than 33 is a fresh water lake
if (region.salinity < 33)
if (is_possible_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::LAKE_TROPICAL_FRESHWATER; // 39
else
return df::enums::biome_type::biome_type::LAKE_TEMPERATE_FRESHWATER; // 36
else if (region.salinity < 66)
if (is_possible_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::LAKE_TROPICAL_BRACKISHWATER; // 40
else
return df::enums::biome_type::biome_type::LAKE_TEMPERATE_BRACKISHWATER; // 37
else // salinity >= 66
if (is_possible_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::LAKE_TROPICAL_SALTWATER;// 41
else
return df::enums::biome_type::biome_type::LAKE_TEMPERATE_SALTWATER; // 38
}
// Not a lake. Check elevation
// Elevation greater then 149 means a mountain biome
// Elevation below 100 means a ocean biome
// Elevation between 100 and 149 are land biomes
if (region.elevation >= 150) // is it a mountain?
return df::enums::biome_type::biome_type::MOUNTAIN; // 0
if (region.elevation < 100) // is it a ocean?
{
if (is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::OCEAN_TROPICAL; // 27
else if (region.temperature <= -5)
return df::enums::biome_type::biome_type::OCEAN_ARCTIC; // 29
else
return df::enums::biome_type::biome_type::OCEAN_TEMPERATE; // 28
}
// land biome. Elevation between 100 and 149
if (region.temperature <= -5)
{
if (region.drainage < 75)
return df::enums::biome_type::biome_type::TUNDRA; // 2
else
return df::enums::biome_type::biome_type::GLACIER; // 1
}
// Not a lake, mountain, ocean, glacier or tundra
// Vegetation determines the biome type
if (region.vegetation < 10)
{
if (region.drainage < 33)
return df::enums::biome_type::biome_type::DESERT_SAND; // 26
else if (region.drainage < 66)
return df::enums::biome_type::biome_type::DESERT_ROCK; // 25
else // drainage >= 66
return df::enums::biome_type::biome_type::DESERT_BADLAND; // 24
}
else if (region.vegetation < 20)
{
if ((is_possible_tropical_area_by_latitude && (parameter < 66)) || is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::GRASSLAND_TROPICAL; // 21
else
return df::enums::biome_type::biome_type::GRASSLAND_TEMPERATE; //18;
}
else if (region.vegetation < 33)
{
// vegetation between 20 and 32
if ((is_possible_tropical_area_by_latitude && (parameter <= 6)) || is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::SAVANNA_TROPICAL; // 22
else
return df::enums::biome_type::biome_type::SAVANNA_TEMPERATE; //19;
}
else if (region.vegetation < 66)
{
if (region.drainage >= 33)
{
if (is_possible_tropical_area_by_latitude && (parameter < 66 || is_tropical_area_by_latitude))
return df::enums::biome_type::biome_type::SHRUBLAND_TROPICAL; // 23
else
return df::enums::biome_type::biome_type::SHRUBLAND_TEMPERATE; // 20
}
// drainage < 33
{
if (region.salinity < 66)
{
if ((is_possible_tropical_area_by_latitude && (parameter < 66)) || is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::MARSH_TROPICAL_FRESHWATER; // 10
else
return df::enums::biome_type::biome_type::MARSH_TEMPERATE_FRESHWATER; // 5
}
else // drainage < 33, salinity >= 66
{
if ((is_possible_tropical_area_by_latitude && (parameter < 66)) || is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::MARSH_TROPICAL_SALTWATER; // 11
else
return df::enums::biome_type::biome_type::MARSH_TEMPERATE_SALTWATER; // 6
}
}
}
// Not a lake, mountain, ocean, glacier, tundra, desert, grassland or savanna
// vegetation >= 66
else if (region.drainage >= 33)
{
// drainage >= 33, not tropical area
if (!is_possible_tropical_area_by_latitude)
{
if ((region.rainfall < 75) || (region.temperature < 65))
{
if (region.temperature >= 10)
return df::enums::biome_type::biome_type::FOREST_TEMPERATE_CONIFER; // 13
else
return df::enums::biome_type::biome_type::FOREST_TAIGA; // 12
}
else
return df::enums::biome_type::biome_type::FOREST_TEMPERATE_BROADLEAF; // 14
}
else // drainage >= 33, tropical area
{
if (((parameter < 66) || is_tropical_area_by_latitude) && (region.rainfall < 75))
return df::enums::biome_type::biome_type::FOREST_TROPICAL_CONIFER; // 15
if (parameter < 66)
return df::enums::biome_type::biome_type::FOREST_TROPICAL_DRY_BROADLEAF; // 16
if (is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::FOREST_TROPICAL_MOIST_BROADLEAF; // 17
else
{
if ((region.rainfall < 75) || (region.temperature < 65))
{
if (region.temperature >= 10)
return df::enums::biome_type::biome_type::FOREST_TEMPERATE_CONIFER; // 13
else
return df::enums::biome_type::biome_type::FOREST_TAIGA; // 12
}
else
return df::enums::biome_type::biome_type::FOREST_TEMPERATE_BROADLEAF; // 14
}
}
}
// Not a lake, mountain, ocean, glacier, tundra, desert, grassland, savanna or forest
// vegetation >= 66, drainage < 33
else if (is_possible_tropical_area_by_latitude)
{
if (region.salinity < 66)
{
if ((parameter < 66) || is_tropical_area_by_latitude)
return df::enums::biome_type::biome_type::SWAMP_TROPICAL_FRESHWATER; // 7
else
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_FRESHWATER;// 3
}
else // elevation between 100 and 149, vegetation >= 66, drainage < 33, salinity >= 66
{
if ((parameter < 66) || is_tropical_area_by_latitude)
if (region.drainage < 10)
return df::enums::biome_type::biome_type::SWAMP_MANGROVE; //9
else // drainage >= 10
return df::enums::biome_type::biome_type::SWAMP_TROPICAL_SALTWATER; // 8
else
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_SALTWATER; // 4
}
}
else if (region.salinity >= 66)
// elevation between 100 and 149, vegetation >= 66, drainage < 33, not tropical area
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_SALTWATER; // 4
else
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_FRESHWATER; // 3
}
/*****************************************************************************
Module main function.
Return the biome type, given a position coordinate expressed in world_tiles
*****************************************************************************/
df::enums::biome_type::biome_type Maps::GetBiomeType(int world_coord_x, int world_coord_y)
{
return Maps::GetBiomeTypeWithRef(world_coord_x, world_coord_y, world_coord_y);
}