Merge branch 'develop' into cmake-cleanup

develop
lethosor 2019-07-20 19:19:40 -04:00
commit 88757d5c27
19 changed files with 1350 additions and 928 deletions

@ -152,6 +152,12 @@ Before you can build anything, you'll also need ``cmake``. It is advisable to
also get ``ccmake`` on distributions that split the cmake package into multiple
parts.
You will need pthread; most systems should have this already. Note that older
CMake versions may have trouble detecting pthread, so if you run into
pthread-related errors and pthread is installed, you may need to upgrade CMake,
either by downloading it from `cmake.org <https://cmake.org/download/>`_ or
through your package manager, if possible.
You also need zlib, libsdl (1.2, not sdl2, like DF), perl, and the XML::LibXML
and XML::LibXSLT perl packages (for the code generation parts). You should be
able to find them in your distro repositories.

@ -37,6 +37,15 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
================================================================================
# Future
## Misc Improvements
- `RemoteFortressReader`:
- added basic framework for controlling and reading the menus in DF, currently only supports the building menu.
- added support for reading item raws.
- added a check for wheather or not the game is currently saving or loading, for utilities to check if it's safe to read from DF.
- guestimate unit facing direction, and position within tiles.
- get unit age.
- get unit wounds.
## Internals
- Fixed some OpenGL build issues with `stonesense`

@ -36,6 +36,7 @@ distribution.
#include "BitArray.h"
#include "modules/Materials.h"
#include "df/biome_type.h"
#include "df/block_flags.h"
#include "df/feature_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 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

@ -46,6 +46,7 @@ using namespace std;
#include "modules/MapCache.h"
#include "modules/Maps.h"
#include "df/biome_type.h"
#include "df/block_burrow.h"
#include "df/block_burrow_link.h"
#include "df/block_square_event_grassst.h"
@ -711,3 +712,393 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
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 = 0;
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);
}

@ -111,6 +111,7 @@ macro(dfhack_plugin)
set_source_files_properties( ${PLUGIN_PROTO_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE )
list(APPEND PLUGIN_SOURCES ${PLUGIN_PROTO_HDRS})
list(APPEND PLUGIN_SOURCES ${PLUGIN_PROTO_SRCS})
list(APPEND PLUGIN_SOURCES ${PLUGIN_PROTOS})
if(UNIX)
set(PLUGIN_COMPILE_FLAGS "${PLUGIN_COMPILE_FLAGS} -include Export.h")

@ -1,7 +1,6 @@
project(embark-assistant)
# A list of source files
set(PROJECT_SRCS
biome_type.cpp
embark-assistant.cpp
finder_ui.cpp
help_ui.cpp
@ -12,7 +11,6 @@ set(PROJECT_SRCS
)
# A list of headers
set(PROJECT_HDRS
biome_type.h
defs.h
embark-assistant.h
finder_ui.h

@ -1,754 +0,0 @@
/* The code is copied from Ragundo's repo referenced below.
The changes are:
- The addition of a .h file reference.
- The simplification of the code using ofsub to remove the use of (and
.h reference to) that function (analysis of the code showed the
simplified code is the result, as the ofsub expressions will never be
true given the range of the values it can be passed in these functions).
- The change of the main function to take a separate y coordinate for
use in the tropicality determination to allow proper determination of
the tropicality of mid level tiles ("region tiles") referencing a
neighboring world tile's biome.
*/
/*
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.
*/
// You can always find the latest version of this plugin in Github
// https://github.com/ragundo/exportmaps
#include <utility>
#include "DataDefs.h"
#include <df/region_map_entry.h>
#include <df/world.h>
#include <df/world_data.h>
#include <df/biome_type.h>
#include "biome_type.h"
/*****************************************************************************
Local functions forward declaration
*****************************************************************************/
std::pair<bool, bool> check_tropicality(df::region_map_entry& region,
int a1
);
int get_lake_biome(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude
);
int get_ocean_biome(df::region_map_entry& region,
bool is_tropical_area_by_latitude
);
int get_desert_biome(df::region_map_entry& region);
int get_biome_grassland(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_savanna(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_shrubland(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_marsh(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_forest(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_swamp(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_desert_or_grassland_or_savanna(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
int get_biome_shrubland_or_marsh(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
);
/*****************************************************************************
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.
*****************************************************************************/
int get_biome_type(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;
// Begin the discrimination
if (region.flags.is_set(df::region_map_entry_flags::is_lake)) // is it a lake?
return get_lake_biome(region,
is_possible_tropical_area_by_latitude
);
// 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?
return get_ocean_biome(region,
is_possible_tropical_area_by_latitude
);
// 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 < 66)
{
if (region.vegetation < 33)
return get_biome_desert_or_grassland_or_savanna(region,
is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
world_coord_y,
world_coord_x
);
else // vegetation between 33 and 65
return get_biome_shrubland_or_marsh(region,
is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
world_coord_y,
world_coord_x
);
}
// Not a lake, mountain, ocean, glacier, tundra, desert, grassland or savanna
// vegetation >= 66
if (region.drainage >= 33)
return get_biome_forest(region,
is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
world_coord_y,
world_coord_x
);
// Not a lake, mountain, ocean, glacier, tundra, desert, grassland, savanna or forest
// vegetation >= 66, drainage < 33
return get_biome_swamp(region,
is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
world_coord_y,
world_coord_x);
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality_no_poles_world(df::region_map_entry& region,
int y_pos
)
{
bool is_possible_tropical_area_by_latitude = false;
bool is_tropical_area_by_latitude = false;
// If there're no poles, tropical area is determined by temperature
if (region.temperature >= 75)
is_possible_tropical_area_by_latitude = true;
is_tropical_area_by_latitude = region.temperature >= 85;
return std::pair<bool, bool>(is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude
);
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality_north_pole_only_world(df::region_map_entry& region,
int y_pos
)
{
int v6;
bool is_possible_tropical_area_by_latitude = false;
bool is_tropical_area_by_latitude = false;
df::world_data* wdata = df::global::world->world_data;
// Scale the smaller worlds to the big one
if (wdata->world_height == 17)
v6 = 16 * y_pos;
else if (wdata->world_height == 33)
v6 = 8 * y_pos;
else if (wdata->world_height == 65)
v6 = 4 * y_pos;
else if (wdata->world_height == 129)
v6 = 2 * y_pos;
else
v6 = y_pos;
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
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality_south_pole_only_world(df::region_map_entry& region,
int y_pos
)
{
int v6 = df::global::world->world_data->world_height - y_pos - 1;
bool is_possible_tropical_area_by_latitude = false;
bool is_tropical_area_by_latitude = false;
df::world_data* wdata = df::global::world->world_data;
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;
else
v6 *= 1;
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
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality_both_poles_world(df::region_map_entry& region,
int y_pos
)
{
int v6;
bool is_possible_tropical_area_by_latitude = false;
bool is_tropical_area_by_latitude = false;
df::world_data* wdata = df::global::world->world_data;
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;
else
v6 *= 1;
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
//
//----------------------------------------------------------------------------//
std::pair<bool, bool> check_tropicality(df::region_map_entry& region,
int y_pos
)
{
int flip_latitude = df::global::world->world_data->flip_latitude;
if (flip_latitude == -1) // NO POLES
return check_tropicality_no_poles_world(region,
y_pos
);
else if (flip_latitude == 0) // NORTH POLE ONLY
return check_tropicality_north_pole_only_world(region,
y_pos
);
else if (flip_latitude == 1) // SOUTH_POLE ONLY
return check_tropicality_south_pole_only_world(region,
y_pos
);
else if (flip_latitude == 2) // BOTH POLES
return check_tropicality_both_poles_world(region,
y_pos
);
return std::pair<bool, bool>(false, false);
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_parameter_percentage(int flip_latitude,
int y_pos,
int rainfall,
int world_height
)
{
int result;
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;
}
//----------------------------------------------------------------------------//
// Utility function
//
// return some unknow parameter as a percentage
//----------------------------------------------------------------------------//
int get_region_parameter(int y,
int x,
char a4
)
{
int result = 100;
if ((df::global::cur_season && *df::global::cur_season != 1) || !a4)
{
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];
return get_parameter_percentage(df::global::world->world_data->flip_latitude,
y,
region.rainfall,
world_height
);
}
}
return result;
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_lake_biome(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude
)
{
// 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 < 66)
{
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 // salinity >= 33
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
}
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_ocean_biome(df::region_map_entry& region,
bool is_tropical_area_by_latitude
)
{
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
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_desert_biome(df::region_map_entry& region)
{
if (region.drainage < 66)
{
if (region.drainage < 33)
return df::enums::biome_type::biome_type::DESERT_SAND; // 26
else // drainage between 33 and 65
return df::enums::biome_type::biome_type::DESERT_ROCK; // 25
}
// drainage >= 66
return df::enums::biome_type::biome_type::DESERT_BADLAND; // 24
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_grassland(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 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;
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_savanna(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) <= 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;
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_desert_or_grassland_or_savanna(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if (region.vegetation < 20)
{
if (region.vegetation < 10)
return get_desert_biome(region);
else // vegetation between 10 and 19
return get_biome_grassland(is_possible_tropical_area_by_latitude, is_tropical_area_by_latitude, y, x);
}
// vegetation between 20 and 32
return get_biome_savanna(is_possible_tropical_area_by_latitude, is_tropical_area_by_latitude, y, x);
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_shrubland(bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if (is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 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
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_marsh(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if (region.salinity < 66)
{
if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 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 && (get_region_parameter(y, x, 0) < 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
}
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_shrubland_or_marsh(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
if (region.drainage >= 33)
return get_biome_shrubland(is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
y,
x
);
// drainage < 33
return get_biome_marsh(region,
is_possible_tropical_area_by_latitude,
is_tropical_area_by_latitude,
y,
x
);
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_forest(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
int parameter = get_region_parameter(y, x, 0);
// 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
}
}
}
//----------------------------------------------------------------------------//
// Utility function
//
//----------------------------------------------------------------------------//
int get_biome_swamp(df::region_map_entry& region,
bool is_possible_tropical_area_by_latitude,
bool is_tropical_area_by_latitude,
int y,
int x
)
{
int parameter = get_region_parameter(y, x, 0);
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 // elevation between 100 and 149, vegetation >= 66, drainage < 33, not tropical area
{
if (region.salinity >= 66)
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_SALTWATER; // 4
else
return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_FRESHWATER; // 3
}
}

@ -1,7 +0,0 @@
// world_coord_x/y is the location of the tile "owning" the biome, while world_ref_coord_y is the
// location of the tile the biome appears on. They differ when a mid level tile ("region tile")
// refers to a neighboring tile for the biome parameters. The difference can affect the tropicality
// determination. Since Tropicality is determined by latitude, the x coordinate of the reference is
// omitted.
//
int get_biome_type(int world_coord_x, int world_coord_y, int world_ref_coord_y);

@ -8,6 +8,7 @@
#include <modules/Gui.h>
#include "modules/Materials.h"
#include "modules/Maps.h"
#include "DataDefs.h"
#include "df/builtin_mats.h"
@ -55,7 +56,6 @@
#include "df/world_site_type.h"
#include "df/world_underground_region.h"
#include "biome_type.h"
#include "defs.h"
#include "survey.h"
@ -586,7 +586,7 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
offset_count++;
results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id;
results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k);
results.biome[l] = DFHack::Maps::GetBiomeTypeWithRef(adjusted.x, adjusted.y, k);
temperature = world_data->region_map[adjusted.x][adjusted.y].temperature;
negative = temperature < 0;

@ -0,0 +1,96 @@
syntax = "proto2";
package DwarfControl;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
import "ui_sidebar_mode.proto";
import "RemoteFortressReader.proto";
// RPC GetSideMenu : EmptyMessage -> SidebarState
// RPC SetSideMenu : SidebarCommand -> EmptyMessage
enum BuildCategory
{
NotCategory = 0;
SiegeEngines = 1;
Traps = 2;
Workshops = 3;
Furnaces = 4;
Constructions = 5;
MachineComponents = 6;
Track = 7;
}
enum MenuAction
{
MenuNone = 0;
MenuSelect = 1;
MenuCancel = 2;
MenuSelectAll = 3;
}
enum BuildSelectorStage
{
StageNoMat = 0;
StagePlace = 1;
StageItemSelect = 2;
}
message SidebarState
{
optional proto.enums.ui_sidebar_mode.ui_sidebar_mode mode = 1;
repeated MenuItem menu_items = 2;
optional BuildSelector build_selector = 3;
}
message MenuItem
{
optional RemoteFortressReader.BuildingType building_type = 1;
optional int32 existing_count = 2;
optional BuildCategory build_category = 3;
}
message SidebarCommand
{
optional proto.enums.ui_sidebar_mode.ui_sidebar_mode mode = 1;
optional int32 menu_index = 2;
optional MenuAction action = 3;
optional RemoteFortressReader.Coord selection_coord = 4;
}
message BuiildReqChoice
{
optional int32 distance = 1;
optional string name = 2;
optional int32 num_candidates = 3;
optional int32 used_count = 4;
}
message BuildItemReq
{
//Put filter here = 1
optional int32 count_required = 2;
optional int32 count_max = 3;
optional int32 count_provided = 4;
}
message BuildSelector
{
optional RemoteFortressReader.BuildingType building_type = 1;
optional BuildSelectorStage stage = 2;
repeated BuiildReqChoice choices = 3;
optional int32 sel_index = 4;
repeated BuildItemReq requirements = 5;
optional int32 req_index = 6;
repeated string errors = 7;
optional int32 radius_x_low = 8;
optional int32 radius_y_low = 9;
optional int32 radius_x_high = 10;
optional int32 radius_y_high = 11;
optional RemoteFortressReader.Coord cursor = 12;
repeated int32 tiles = 13;
}

@ -42,6 +42,7 @@ import "ItemdefInstrument.proto";
// RPC MovementSelectCommand : IntMessage -> EmptyMessage
// RPC MiscMoveCommand : MiscMoveParams -> EmptyMessage
// RPC GetLanguage : EmptyMessage -> Language
// RPC GetGameValidity : EmptyMessage -> SingleBool
//We use shapes, etc, because the actual tiletypes may differ between DF versions.
enum TiletypeShape
@ -201,6 +202,14 @@ enum InventoryMode
Strapped = 10;
}
enum ArmorLayer
{
LAYER_UNDER = 0;
LAYER_OVER = 1;
LAYER_ARMOR = 2;
LAYER_COVER = 3;
}
message Coord
{
optional int32 x = 1;
@ -320,6 +329,31 @@ message Item
optional ArtImage image = 18;
}
message PlantTile
{
optional bool trunk = 1;
optional bool connection_east = 2;
optional bool connection_south = 3;
optional bool connection_west = 4;
optional bool connection_north = 5;
optional bool branches = 6;
optional bool twigs = 7;
optional TiletypeSpecial tile_type = 8;
}
message TreeInfo
{
optional Coord size = 1;
repeated PlantTile tiles = 2;
}
message PlantInstance
{
optional int32 plant_type = 1;
optional Coord pos = 2;
optional TreeInfo tree_info = 3;
}
message MapBlock
{
required int32 map_x = 1;
@ -371,6 +405,9 @@ message MaterialDefinition{
optional string name = 3;
optional ColorDefinition state_color = 4; //Simplifying colors to assume room temperature.
optional ItemdefInstrument.InstrumentDef instrument = 5;
optional int32 up_step = 6;
optional int32 down_step = 7;
optional ArmorLayer layer = 8;
}
message BuildingType
@ -426,6 +463,20 @@ message InventoryItem
{
optional InventoryMode mode = 1;
optional Item item = 2;
optional int32 body_part_id = 3;
}
message WoundPart
{
optional int32 global_layer_idx = 1;
optional int32 body_part_id = 2;
optional int32 layer_idx = 3;
}
message UnitWound
{
repeated WoundPart parts = 1;
optional bool severed_part = 2;
}
message UnitDefinition
@ -453,6 +504,9 @@ message UnitDefinition
optional float subpos_x = 21;
optional float subpos_y = 22;
optional float subpos_z = 23;
optional Coord facing = 24;
optional int32 age = 25;
repeated UnitWound wounds = 26;
}
message UnitList
@ -787,6 +841,7 @@ message CreatureRaw
optional int32 adultsize = 9;
repeated CasteRaw caste = 10;
repeated TissueRaw tissues = 11;
repeated bool flags = 12;
}
message CreatureRawList

@ -0,0 +1,63 @@
package proto.enums.ui_sidebar_mode;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
enum ui_sidebar_mode
{
Default = 0;
Squads = 1;
DesignateMine = 2;
DesignateRemoveRamps = 3;
DesignateUpStair = 4;
DesignateDownStair = 5;
DesignateUpDownStair = 6;
DesignateUpRamp = 7;
DesignateChannel = 8;
DesignateGatherPlants = 9;
DesignateRemoveDesignation = 10;
DesignateSmooth = 11;
DesignateCarveTrack = 12;
DesignateEngrave = 13;
DesignateCarveFortification = 14;
Stockpiles = 15;
Build = 16;
QueryBuilding = 17;
Orders = 18;
OrdersForbid = 19;
OrdersRefuse = 20;
OrdersWorkshop = 21;
OrdersZone = 22;
BuildingItems = 23;
ViewUnits = 24;
LookAround = 25;
DesignateItemsClaim = 26;
DesignateItemsForbid = 27;
DesignateItemsMelt = 28;
DesignateItemsUnmelt = 29;
DesignateItemsDump = 30;
DesignateItemsUndump = 31;
DesignateItemsHide = 32;
DesignateItemsUnhide = 33;
DesignateChopTrees = 34;
DesignateToggleEngravings = 35;
DesignateToggleMarker = 36;
Hotkeys = 37;
DesignateTrafficHigh = 38;
DesignateTrafficNormal = 39;
DesignateTrafficLow = 40;
DesignateTrafficRestricted = 41;
Zones = 42;
ZonesPenInfo = 43;
ZonesPitInfo = 44;
ZonesHospitalInfo = 45;
ZonesGatherInfo = 46;
DesignateRemoveConstruction = 47;
DepotAccess = 48;
NotesPoints = 49;
NotesRoutes = 50;
Burrows = 51;
Hauling = 52;
ArenaWeather = 53;
ArenaTrees = 54;
}

@ -4,27 +4,39 @@ set(PROJECT_SRCS
remotefortressreader.cpp
adventure_control.cpp
building_reader.cpp
dwarf_control.cpp
item_reader.cpp
)
# A list of headers
set(PROJECT_HDRS
adventure_control.h
building_reader.h
dwarf_control.h
item_reader.h
df_version_int.h
)
# proto files to include.
set(PROJECT_PROTO
${CMAKE_CURRENT_SOURCE_DIR}/../proto/RemoteFortressReader.pb.cc
${CMAKE_CURRENT_SOURCE_DIR}/../proto/AdventureControl.pb.cc
${CMAKE_CURRENT_SOURCE_DIR}/../proto/ItemdefInstrument.pb.cc
RemoteFortressReader
AdventureControl
ItemdefInstrument
DwarfControl
ui_sidebar_mode
)
set_source_files_properties(${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties(${PROJECT_PROTO} PROPERTIES GENERATED TRUE)
set(PLUGIN_PROTOS)
foreach(pbuf ${PROJECT_PROTO})
list(APPEND PLUGIN_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/../proto/${pbuf}.proto)
endforeach()
string(REPLACE ".proto" ".pb.cc" PLUGIN_PROTO_SRCS "${PLUGIN_PROTOS}")
string(REPLACE ".proto" ".pb.h" PLUGIN_PROTO_HDRS "${PLUGIN_PROTOS}")
set_source_files_properties(${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS} PROPERTIES GENERATED TRUE)
set_source_files_properties( ${PROJECT_HDRS} ${PLUGIN_PROTO_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
list(APPEND PROJECT_SRCS ${PROJECT_HDRS};${PROJECT_PROTO})
list(APPEND PROJECT_SRCS ${PROJECT_HDRS} ${PLUGIN_PROTOS} ${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS})
if(UNIX AND NOT APPLE)
set(PROJECT_LIBS ${PROJECT_LIBS} SDL)

@ -78,7 +78,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(-1);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt));
bld->set_name(ENUM_ATTR_STR(building_type, name, bt));
switch (bt)
{
case df::enums::building_type::NONE:
@ -101,6 +101,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(furnace_type, st));
bld->set_name(ENUM_ATTR_STR(furnace_type, name, st));
if (st == furnace_type::Custom)
{
@ -128,7 +129,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(shop_type, st));
bld->set_name(ENUM_KEY_STR(shop_type, st));
}
break;
case df::enums::building_type::Door:
@ -149,6 +150,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(workshop_type, st));
bld->set_name(ENUM_ATTR_STR(workshop_type, name, st));
if (st == workshop_type::Custom)
{
@ -190,7 +192,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(siegeengine_type, st));
bld->set_name(ENUM_KEY_STR(siegeengine_type, st));
}
break;
case df::enums::building_type::Trap:
@ -201,7 +203,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(trap_type, st));
bld->set_name(ENUM_KEY_STR(trap_type, st));
}
break;
case df::enums::building_type::AnimalTrap:
@ -224,7 +226,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(civzone_type, st));
bld->set_name(ENUM_KEY_STR(civzone_type, st));
}
break;
case df::enums::building_type::Weapon:
@ -241,7 +243,7 @@ DFHack::command_result GetBuildingDefList(DFHack::color_ostream &stream, const D
bld->mutable_building_type()->set_building_subtype(st);
bld->mutable_building_type()->set_building_custom(-1);
bld->set_id(ENUM_KEY_STR(building_type, bt) + "/" + ENUM_KEY_STR(construction_type, st));
bld->set_name(ENUM_KEY_STR(construction_type, st));
}
break;
case df::enums::building_type::Hatch:

@ -1,2 +1,2 @@
#pragma once
#define DF_VERSION_INT 44002
#define DF_VERSION_INT 44012

@ -0,0 +1,493 @@
#include "dwarf_control.h"
#include "DataDefs.h"
#include "df_version_int.h"
#include "df/build_req_choice_genst.h"
#include "df/build_req_choice_specst.h"
#include "df/build_req_choicest.h"
#include "df/building_def.h"
#include "df/building_def_furnacest.h"
#include "df/building_def_workshopst.h"
#include "df/job.h"
#include "df/job_list_link.h"
#include "df/interface_button_construction_building_selectorst.h"
#include "df/interface_button_construction_category_selectorst.h"
#include "df/ui.h"
#include "df/ui_build_selector.h"
#include "df/ui_sidebar_menus.h"
#include "df/viewscreen.h"
#include "df/world.h"
#include "modules/Buildings.h"
#include "modules/Gui.h"
#include "modules/Job.h"
#include "modules/MapCache.h"
#include "modules/Maps.h"
#include "modules/World.h"
#include "MiscUtils.h"
#include <queue>
using namespace DFHack;
using namespace RemoteFortressReader;
using namespace df::enums;
using namespace Gui;
using namespace df::global;
extern std::queue<interface_key::interface_key> keyQueue;
void GetBuildingSize(
int16_t type,
int16_t subtype,
int16_t custom,
int16_t &rad_x_low,
int16_t &rad_y_low,
int16_t &rad_x_high,
int16_t &rad_y_high
)
{
rad_x_low = 0;
rad_y_low = 0;
rad_x_high = 0;
rad_y_high = 0;
df::building_def* customBuilding = 0;
switch (type)
{
case building_type::FarmPlot:
case building_type::Bridge:
case building_type::RoadDirt:
case building_type::RoadPaved:
case building_type::Stockpile:
case building_type::Civzone:
case building_type::ScrewPump:
case building_type::Construction:
case building_type::AxleHorizontal:
case building_type::WaterWheel:
case building_type::Rollers:
{
bool widthOdd = world->building_width % 2;
rad_x_low = world->building_width / 2;
if(widthOdd)
rad_x_high = world->building_width / 2;
else
rad_x_high = (world->building_width / 2) - 1;
bool heightOdd = world->building_width % 2;
rad_y_low = world->building_height / 2;
if (widthOdd)
rad_y_high = world->building_height / 2;
else
rad_y_high = (world->building_height / 2) - 1;
}
return;
case building_type::Furnace:
if (subtype != furnace_type::Custom)
{
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
}
customBuilding = world->raws.buildings.furnaces[custom];
break;
case building_type::TradeDepot:
case building_type::Shop:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 2;
return;
case building_type::Workshop:
switch (subtype)
{
case workshop_type::Carpenters:
case workshop_type::Farmers:
case workshop_type::Masons:
case workshop_type::Craftsdwarfs:
case workshop_type::Jewelers:
case workshop_type::MetalsmithsForge:
case workshop_type::MagmaForge:
case workshop_type::Bowyers:
case workshop_type::Mechanics:
case workshop_type::Butchers:
case workshop_type::Leatherworks:
case workshop_type::Tanners:
case workshop_type::Clothiers:
case workshop_type::Fishery:
case workshop_type::Still:
case workshop_type::Loom:
case workshop_type::Kitchen:
case workshop_type::Ashery:
case workshop_type::Dyers:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
case workshop_type::Siege:
case workshop_type::Kennels:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 2;
return;
case workshop_type::Custom:
customBuilding = world->raws.buildings.workshops[custom];
break;
default:
return;
}
break;
case building_type::SiegeEngine:
case building_type::Wagon:
case building_type::Windmill:
rad_x_low = rad_y_low = rad_x_high = rad_y_high = 1;
return;
default:
return;
}
if (customBuilding)
{
rad_x_low = customBuilding->workloc_x;
rad_y_low = customBuilding->workloc_y;
rad_x_high = customBuilding->dim_x - rad_x_low - 1;
rad_y_high = customBuilding->dim_y - rad_y_low - 1;
return;
}
}
command_result SendDigCommand(color_ostream &stream, const DigCommand *in)
{
MapExtras::MapCache mc;
for (int i = 0; i < in->locations_size(); i++)
{
auto pos = in->locations(i);
auto des = mc.designationAt(DFCoord(pos.x(), pos.y(), pos.z()));
switch (in->designation())
{
case NO_DIG:
des.bits.dig = tile_dig_designation::No;
break;
case DEFAULT_DIG:
des.bits.dig = tile_dig_designation::Default;
break;
case UP_DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpDownStair;
break;
case CHANNEL_DIG:
des.bits.dig = tile_dig_designation::Channel;
break;
case RAMP_DIG:
des.bits.dig = tile_dig_designation::Ramp;
break;
case DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::DownStair;
break;
case UP_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpStair;
break;
default:
break;
}
mc.setDesignationAt(DFCoord(pos.x(), pos.y(), pos.z()), des);
#if DF_VERSION_INT >= 43005
//remove and job postings related.
for (df::job_list_link * listing = &(df::global::world->jobs.list); listing != NULL; listing = listing->next)
{
if (listing->item == NULL)
continue;
auto type = listing->item->job_type;
switch (type)
{
case job_type::CarveFortification:
case job_type::DetailWall:
case job_type::DetailFloor:
case job_type::Dig:
case job_type::CarveUpwardStaircase:
case job_type::CarveDownwardStaircase:
case job_type::CarveUpDownStaircase:
case job_type::CarveRamp:
case job_type::DigChannel:
case job_type::FellTree:
case job_type::GatherPlants:
case job_type::RemoveConstruction:
case job_type::CarveTrack:
{
if (listing->item->pos == DFCoord(pos.x(), pos.y(), pos.z()))
{
Job::removeJob(listing->item);
goto JOB_FOUND;
}
break;
}
default:
continue;
}
}
JOB_FOUND:
continue;
#endif
}
mc.WriteAll();
return CR_OK;
}
command_result SetPauseState(color_ostream &stream, const SingleBool *in)
{
DFHack::World::SetPauseState(in->value());
return CR_OK;
}
void CopyBuildMenu(DwarfControl::SidebarState * out)
{
auto menus = df::global::ui_sidebar_menus;
auto build_selector = df::global::ui_build_selector;
if (build_selector->building_type == -1)
for (size_t i = 0; i < menus->building.choices_visible.size(); i++)
{
auto menu_item = menus->building.choices_visible[i];
auto send_item = out->add_menu_items();
STRICT_VIRTUAL_CAST_VAR(building, df::interface_button_construction_building_selectorst, menu_item);
if (building)
{
auto send_bld = send_item->mutable_building_type();
send_bld->set_building_type(building->building_type);
send_bld->set_building_subtype(building->building_subtype);
send_bld->set_building_custom(building->custom_type);
send_item->set_existing_count(building->existing_count);
}
STRICT_VIRTUAL_CAST_VAR(sub_category, df::interface_button_construction_category_selectorst, menu_item);
if (sub_category)
{
send_item->set_build_category((DwarfControl::BuildCategory)sub_category->category_id);
}
}
else
{
auto send_selector = out->mutable_build_selector();
auto send_bld = send_selector->mutable_building_type();
send_bld->set_building_type(build_selector->building_type);
send_bld->set_building_subtype(build_selector->building_subtype);
send_bld->set_building_custom(build_selector->custom_type);
send_selector->set_stage((DwarfControl::BuildSelectorStage)build_selector->stage);
for (size_t i = 0; i < build_selector->errors.size(); i++)
{
if (build_selector->errors[i])
send_selector->add_errors(*build_selector->errors[i]);
}
for (size_t i = 0; i < build_selector->choices.size(); i++)
{
auto choice = build_selector->choices[i];
auto send_choice = send_selector->add_choices();
send_choice->set_distance(choice->distance);
std::string name;
choice->getName(&name);
send_choice->set_name(name);
send_choice->set_num_candidates(choice->getNumCandidates());
send_choice->set_used_count(choice->getUsedCount());
}
int16_t x_low, y_low, x_high, y_high;
GetBuildingSize(build_selector->building_type, build_selector->building_subtype, build_selector->custom_type, x_low, y_low, x_high, y_high);
send_selector->set_radius_x_low(x_low);
send_selector->set_radius_y_low(y_low);
send_selector->set_radius_x_high(x_high);
send_selector->set_radius_y_high(y_high);
if (build_selector->stage >= 1)
{
auto send_cursor = send_selector->mutable_cursor();
send_cursor->set_x(cursor->x);
send_cursor->set_y(cursor->y);
send_cursor->set_z(cursor->z);
}
for (int y = 0; y < (y_low + y_high + 1); y++)
for (int x = 0; x < (x_low + x_high + 1); x++)
{
send_selector->add_tiles(build_selector->tiles[x][y]);
}
}
}
command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out)
{
auto ui = df::global::ui;
out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)ui->main.mode);
auto mode = ui->main.mode;
switch (mode)
{
case ui_sidebar_mode::Default:
break;
case ui_sidebar_mode::Squads:
break;
case ui_sidebar_mode::DesignateMine:
break;
case ui_sidebar_mode::DesignateRemoveRamps:
break;
case ui_sidebar_mode::DesignateUpStair:
break;
case ui_sidebar_mode::DesignateDownStair:
break;
case ui_sidebar_mode::DesignateUpDownStair:
break;
case ui_sidebar_mode::DesignateUpRamp:
break;
case ui_sidebar_mode::DesignateChannel:
break;
case ui_sidebar_mode::DesignateGatherPlants:
break;
case ui_sidebar_mode::DesignateRemoveDesignation:
break;
case ui_sidebar_mode::DesignateSmooth:
break;
case ui_sidebar_mode::DesignateCarveTrack:
break;
case ui_sidebar_mode::DesignateEngrave:
break;
case ui_sidebar_mode::DesignateCarveFortification:
break;
case ui_sidebar_mode::Stockpiles:
break;
case ui_sidebar_mode::Build:
CopyBuildMenu(out);
break;
case ui_sidebar_mode::QueryBuilding:
break;
case ui_sidebar_mode::Orders:
break;
case ui_sidebar_mode::OrdersForbid:
break;
case ui_sidebar_mode::OrdersRefuse:
break;
case ui_sidebar_mode::OrdersWorkshop:
break;
case ui_sidebar_mode::OrdersZone:
break;
case ui_sidebar_mode::BuildingItems:
break;
case ui_sidebar_mode::ViewUnits:
break;
case ui_sidebar_mode::LookAround:
break;
case ui_sidebar_mode::DesignateItemsClaim:
break;
case ui_sidebar_mode::DesignateItemsForbid:
break;
case ui_sidebar_mode::DesignateItemsMelt:
break;
case ui_sidebar_mode::DesignateItemsUnmelt:
break;
case ui_sidebar_mode::DesignateItemsDump:
break;
case ui_sidebar_mode::DesignateItemsUndump:
break;
case ui_sidebar_mode::DesignateItemsHide:
break;
case ui_sidebar_mode::DesignateItemsUnhide:
break;
case ui_sidebar_mode::DesignateChopTrees:
break;
case ui_sidebar_mode::DesignateToggleEngravings:
break;
case ui_sidebar_mode::DesignateToggleMarker:
break;
case ui_sidebar_mode::Hotkeys:
break;
case ui_sidebar_mode::DesignateTrafficHigh:
break;
case ui_sidebar_mode::DesignateTrafficNormal:
break;
case ui_sidebar_mode::DesignateTrafficLow:
break;
case ui_sidebar_mode::DesignateTrafficRestricted:
break;
case ui_sidebar_mode::Zones:
break;
case ui_sidebar_mode::ZonesPenInfo:
break;
case ui_sidebar_mode::ZonesPitInfo:
break;
case ui_sidebar_mode::ZonesHospitalInfo:
break;
case ui_sidebar_mode::ZonesGatherInfo:
break;
case ui_sidebar_mode::DesignateRemoveConstruction:
break;
case ui_sidebar_mode::DepotAccess:
break;
case ui_sidebar_mode::NotesPoints:
break;
case ui_sidebar_mode::NotesRoutes:
break;
case ui_sidebar_mode::Burrows:
break;
case ui_sidebar_mode::Hauling:
break;
case ui_sidebar_mode::ArenaWeather:
break;
case ui_sidebar_mode::ArenaTrees:
break;
default:
break;
}
return CR_OK;
}
command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in)
{
auto ui = df::global::ui;
if (in->has_mode())
{
ui_sidebar_mode::ui_sidebar_mode set_mode = (ui_sidebar_mode::ui_sidebar_mode)in->mode();
if (ui->main.mode != set_mode)
{
ui->main.mode = ui_sidebar_mode::Default;
switch (set_mode)
{
case ui_sidebar_mode::Build:
keyQueue.push(interface_key::D_BUILDING);
break;
default:
ui->main.mode = set_mode;
break;
}
}
}
switch (ui->main.mode)
{
case ui_sidebar_mode::Build:
if (in->has_action())
{
int index = 0;
if (in->has_menu_index())
index = in->menu_index();
if(ui_build_selector->building_type == -1)
df::global::ui_sidebar_menus->building.cursor = index;
if (ui_build_selector->stage == 2)
{
ui_build_selector->sel_index = index;
}
}
if (ui_build_selector->stage == 1)
{
if (in->has_selection_coord())
{
df::global::cursor->x = in->selection_coord().x();
df::global::cursor->y = in->selection_coord().y();
df::global::cursor->z = in->selection_coord().z();
getCurViewscreen()->feed_key(interface_key::CURSOR_LEFT);
getCurViewscreen()->feed_key(interface_key::CURSOR_RIGHT);
}
}
break;
default:
break;
}
auto viewScreen = getCurViewscreen();
if (in->has_action())
{
switch (in->action())
{
case DwarfControl::MenuSelect:
keyQueue.push(interface_key::SELECT);
break;
case DwarfControl::MenuCancel:
keyQueue.push(interface_key::LEAVESCREEN);
break;
default:
break;
}
}
return CR_OK;
}

@ -0,0 +1,15 @@
#ifndef DWARF_CONTROL_H
#define DWARF_CONTROL_H
#include "RemoteClient.h"
#include "RemoteFortressReader.pb.h"
#include "DwarfControl.pb.h"
DFHack::command_result SendDigCommand(DFHack::color_ostream &stream, const RemoteFortressReader::DigCommand *in);
DFHack::command_result SetPauseState(DFHack::color_ostream &stream, const RemoteFortressReader::SingleBool *in);
DFHack::command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out);
DFHack::command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in);
#endif // !DWARF_CONTROL_H

@ -26,7 +26,13 @@
#include "df/item_statuest.h"
#include "df/item_threadst.h"
#include "df/item_toolst.h"
#include "df/itemdef_armorst.h"
#include "df/itemdef_glovesst.h"
#include "df/itemdef_helmst.h"
#include "df/itemdef_instrumentst.h"
#include "df/itemdef_pantsst.h"
#include "df/itemdef_shieldst.h"
#include "df/itemdef_shoesst.h"
#include "df/itemdef_toolst.h"
#include "df/itemimprovement.h"
#include "df/itemimprovement_art_imagest.h"
@ -658,13 +664,70 @@ DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::
reg->set_pitch_range_max(instrument->registers[j]->pitch_range_max);
}
send_instrument->set_description(DF2UTF(instrument->description));
break;
}
break;
case df::enums::item_type::TOOL:
{
VIRTUAL_CAST_VAR(tool, df::itemdef_toolst, item);
mat_def->set_name(DF2UTF(tool->name));
}
break;
case df::enums::item_type::ARMOR:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_armorst, item))
{
mat_def->set_up_step(armor->ubstep);
mat_def->set_down_step(armor->lbstep);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::SHOES:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_shoesst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::SHIELD:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_shieldst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
}
}
break;
case df::enums::item_type::HELM:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_helmst, item))
{
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::GLOVES:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_glovesst, item))
{
mat_def->set_up_step(armor->upstep);
mat_def->set_down_step(10000);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
case df::enums::item_type::PANTS:
{
if (VIRTUAL_CAST_VAR(armor, df::itemdef_pantsst, item))
{
mat_def->set_down_step(armor->lbstep);
mat_def->set_layer((ArmorLayer)armor->props.layer);
}
}
break;
default:
break;
}

@ -1,5 +1,5 @@
#include "df_version_int.h"
#define RFR_VERSION "0.19.1"
#define RFR_VERSION "0.20.2"
#include <cstdio>
#include <time.h>
@ -63,7 +63,7 @@
#include "df/flow_guide_item_cloudst.h"
#include "df/graphic.h"
#include "df/historical_figure.h"
#include "df/identity.h"
#include "df/job.h"
#include "df/job_type.h"
#include "df/job_item.h"
@ -92,7 +92,10 @@
#include "df/ui.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/unit_wound.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include "df/vehicle.h"
#include "df/world.h"
#include "df/world_data.h"
@ -117,6 +120,7 @@
#include "adventure_control.h"
#include "building_reader.h"
#include "dwarf_control.h"
#include "item_reader.h"
using namespace DFHack;
@ -161,13 +165,11 @@ static command_result GetPlantRaws(color_ostream &stream, const EmptyMessage *in
static command_result GetPartialPlantRaws(color_ostream &stream, const ListRequest *in, PlantRawList *out);
static command_result CopyScreen(color_ostream &stream, const EmptyMessage *in, ScreenCapture *out);
static command_result PassKeyboardEvent(color_ostream &stream, const KeyboardEvent *in);
static command_result SendDigCommand(color_ostream &stream, const DigCommand *in);
static command_result SetPauseState(color_ostream & stream, const SingleBool * in);
static command_result GetPauseState(color_ostream & stream, const EmptyMessage * in, SingleBool * out);
static command_result GetVersionInfo(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::VersionInfo * out);
static command_result GetReports(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::Status * out);
static command_result GetLanguage(color_ostream & stream, const EmptyMessage * in, RemoteFortressReader::Language * out);
static command_result GetGameValidity(color_ostream &stream, const EmptyMessage * in, SingleBool *out);
void CopyBlock(df::map_block * DfBlock, RemoteFortressReader::MapBlock * NetBlock, MapExtras::MapCache * MC, DFCoord pos);
@ -208,54 +210,6 @@ command_result loadArtImageChunk(color_ostream &out, vector <string> & parameter
return CR_OK;
}
command_result dump_bp_mods(color_ostream &out, vector <string> & parameters)
{
remove("bp_appearance_mods.csv");
ofstream output;
output.open("bp_appearance_mods.csv");
output << "Race Index;Race;Caste;Bodypart Token;Bodypart Name;Tissue Layer;Modifier Type;Range\n";
for (size_t creatureIndex = 0; creatureIndex < world->raws.creatures.all.size(); creatureIndex++)
{
auto creatureRaw = world->raws.creatures.all[creatureIndex];
for (size_t casteIndex = 0; casteIndex < creatureRaw->caste.size(); casteIndex++)
{
df::caste_raw *casteRaw = creatureRaw->caste[casteIndex];
for (size_t partIndex = 0; partIndex < casteRaw->bp_appearance.part_idx.size(); partIndex++)
{
output << creatureIndex << ";";
output << creatureRaw->creature_id << ";";
output << casteRaw->caste_id << ";";
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->token << ";";
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->name_singular[0]->c_str() << ";";
int layer = casteRaw->bp_appearance.layer_idx[partIndex];
if (layer < 0)
output << "N/A;";
else
output << casteRaw->body_info.body_parts[casteRaw->bp_appearance.part_idx[partIndex]]->layers[layer]->layer_name << ";";
output << ENUM_KEY_STR(appearance_modifier_type, casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->type) << ";";
auto appMod = casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]];
#if DF_VERSION_INT > 34011
if (appMod->growth_rate > 0)
{
output << appMod->growth_min << " - " << appMod->growth_max << "\n";
}
else
#endif
{
output << casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->ranges[0] << " - ";
output << casteRaw->bp_appearance.modifiers[casteRaw->bp_appearance.modifier_idx[partIndex]]->ranges[6] << "\n";
}
}
}
}
output.close();
return CR_OK;
}
command_result RemoteFortressReader_version(color_ostream &out, vector<string> &parameters)
{
out.print(RFR_VERSION);
@ -267,16 +221,6 @@ DFHACK_PLUGIN_IS_ENABLED(enableUpdates);
// Mandatory init function. If you have some global state, create it here.
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands)
{
//// Fill the command list with your commands.
commands.push_back(PluginCommand(
"dump-bp-mods", "Dump bodypart mods for debugging",
dump_bp_mods, false, /* true means that the command can't be used from non-interactive user interface */
// Extended help string. Used by CR_WRONG_USAGE and the help command:
" This command does nothing at all.\n"
"Example:\n"
" isoworldremote\n"
" Does nothing.\n"
));
commands.push_back(PluginCommand("RemoteFortressReader_version", "List the loaded RemoteFortressReader version", RemoteFortressReader_version, false, "This is used for plugin version checking."));
commands.push_back(PluginCommand(
"load-art-image-chunk",
@ -329,6 +273,9 @@ DFhackCExport RPCService *plugin_rpcconnect(color_ostream &)
svc->addFunction("MovementSelectCommand", MovementSelectCommand, SF_ALLOW_REMOTE);
svc->addFunction("MiscMoveCommand", MiscMoveCommand, SF_ALLOW_REMOTE);
svc->addFunction("GetLanguage", GetLanguage, SF_ALLOW_REMOTE);
svc->addFunction("GetSideMenu", GetSideMenu, SF_ALLOW_REMOTE);
svc->addFunction("SetSideMenu", SetSideMenu, SF_ALLOW_REMOTE);
svc->addFunction("GetGameValidity", GetGameValidity, SF_ALLOW_REMOTE);
return svc;
}
@ -1506,7 +1453,7 @@ static command_result GetBlockList(color_ostream &stream, const BlockRequest *in
goto ItsAir;
}
}
ItsAir:
ItsAir:
if (block->flows.size() > 0)
nonAir = true;
if (nonAir || firstBlock)
@ -1556,9 +1503,9 @@ static command_result GetBlockList(color_ostream &stream, const BlockRequest *in
segment_passed = 0;
// 'rotate' directions
int buffer = di;
int filename = di;
di = -dj;
dj = buffer;
dj = filename;
// increase segment length if necessary
if (dj == 0) {
@ -1702,6 +1649,24 @@ static command_result GetUnitList(color_ostream &stream, const EmptyMessage *in,
return GetUnitListInside(stream, NULL, out);
}
float lerp(float a, float b, float f)
{
return a + f * (b - a);
}
void GetWounds(df::unit_wound * wound, UnitWound * send_wound)
{
for (size_t i = 0; i < wound->parts.size(); i++)
{
auto part = wound->parts[i];
auto send_part = send_wound->add_parts();
send_part->set_global_layer_idx(part->global_layer_idx);
send_part->set_body_part_id(part->body_part_id);
send_part->set_layer_idx(part->layer_idx);
}
send_wound->set_severed_part(wound->flags.bits.severed_part);
}
static command_result GetUnitListInside(color_ostream &stream, const BlockRequest *in, UnitList *out)
{
auto world = df::global::world;
@ -1724,6 +1689,25 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
if (unit->pos.y < in->min_y() * 16 || unit->pos.y >= in->max_y() * 16)
continue;
}
using df::global::cur_year;
using df::global::cur_year_tick;
int year_ticks = 403200;
int birth_time = unit->birth_year * year_ticks + unit->birth_time;
int cur_time = *cur_year * year_ticks + *cur_year_tick;
if (unit->curse_year >= 0)
{
if (auto identity = Units::getIdentity(unit))
{
if (identity->histfig_id < 0)
birth_time = identity->birth_year * year_ticks + identity->birth_second;
}
}
send_unit->set_age(cur_time - birth_time);
ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color());
send_unit->set_flags1(unit->flags1.whole);
send_unit->set_flags2(unit->flags2.whole);
@ -1806,6 +1790,7 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
auto inventory_item = unit->inventory[j];
auto sent_item = send_unit->add_inventory();
sent_item->set_mode((InventoryMode)inventory_item->mode);
sent_item->set_body_part_id(inventory_item->body_part_id);
CopyItem(sent_item->mutable_item(), inventory_item->item);
}
@ -1821,8 +1806,51 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
send_unit->set_subpos_x(item->pos_x / 100000.0);
send_unit->set_subpos_y(item->pos_y / 100000.0);
send_unit->set_subpos_z(item->pos_z / 140000.0);
auto facing = send_unit->mutable_facing();
facing->set_x(item->speed_x);
facing->set_y(item->speed_x);
facing->set_z(item->speed_x);
break;
}
}
else
{
for (size_t i = 0; i < unit->actions.size(); i++)
{
auto action = unit->actions[i];
switch (action->type)
{
case unit_action_type::Move:
if (unit->path.path.x.size() > 0)
{
send_unit->set_subpos_x(lerp(0, unit->path.path.x[0] - unit->pos.x, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
send_unit->set_subpos_y(lerp(0, unit->path.path.y[0] - unit->pos.y, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
send_unit->set_subpos_z(lerp(0, unit->path.path.z[0] - unit->pos.z, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init));
}
break;
case unit_action_type::Job:
{
auto facing = send_unit->mutable_facing();
facing->set_x(action->data.job.x - unit->pos.x);
facing->set_y(action->data.job.y - unit->pos.y);
facing->set_z(action->data.job.z - unit->pos.z);
}
default:
break;
}
}
if (unit->path.path.x.size() > 0)
{
auto facing = send_unit->mutable_facing();
facing->set_x(unit->path.path.x[0] - unit->pos.x);
facing->set_y(unit->path.path.y[0] - unit->pos.y);
facing->set_z(unit->path.path.z[0] - unit->pos.z);
}
}
for (size_t i = 0; i < unit->body.wounds.size(); i++)
{
GetWounds(unit->body.wounds[i], send_unit->add_wounds());
}
}
return CR_OK;
}
@ -1847,6 +1875,14 @@ static command_result GetViewInfo(color_ostream &stream, const EmptyMessage *in,
}
#endif
auto dims = Gui::getDwarfmodeViewDims();
x += dims.map_x1;
y += dims.map_y1;
w = dims.map_x2 - dims.map_x1;
h = dims.map_y2 - dims.map_y1;
out->set_view_pos_x(x);
out->set_view_pos_y(y);
out->set_view_pos_z(z);
@ -2793,6 +2829,10 @@ static command_result GetPartialCreatureRaws(color_ostream &stream, const ListRe
CopyMat(send_tissue->mutable_material(), orig_tissue->mat_type, orig_tissue->mat_index);
}
FOR_ENUM_ITEMS(creature_raw_flags, flag)
{
send_creature->add_flags(orig_creature->flags.is_set(flag));
}
}
return CR_OK;
@ -2893,94 +2933,26 @@ static command_result PassKeyboardEvent(color_ostream &stream, const KeyboardEve
return CR_OK;
}
static command_result SendDigCommand(color_ostream &stream, const DigCommand *in)
{
MapExtras::MapCache mc;
for (int i = 0; i < in->locations_size(); i++)
{
auto pos = in->locations(i);
auto des = mc.designationAt(DFCoord(pos.x(), pos.y(), pos.z()));
switch (in->designation())
{
case NO_DIG:
des.bits.dig = tile_dig_designation::No;
break;
case DEFAULT_DIG:
des.bits.dig = tile_dig_designation::Default;
break;
case UP_DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpDownStair;
break;
case CHANNEL_DIG:
des.bits.dig = tile_dig_designation::Channel;
break;
case RAMP_DIG:
des.bits.dig = tile_dig_designation::Ramp;
break;
case DOWN_STAIR_DIG:
des.bits.dig = tile_dig_designation::DownStair;
break;
case UP_STAIR_DIG:
des.bits.dig = tile_dig_designation::UpStair;
break;
default:
break;
}
mc.setDesignationAt(DFCoord(pos.x(), pos.y(), pos.z()), des);
#if DF_VERSION_INT >= 43005
//remove and job postings related.
for (df::job_list_link * listing = &(world->jobs.list); listing != NULL; listing = listing->next)
{
if (listing->item == NULL)
continue;
auto type = listing->item->job_type;
switch (type)
{
case df::enums::job_type::CarveFortification:
case df::enums::job_type::DetailWall:
case df::enums::job_type::DetailFloor:
case df::enums::job_type::Dig:
case df::enums::job_type::CarveUpwardStaircase:
case df::enums::job_type::CarveDownwardStaircase:
case df::enums::job_type::CarveUpDownStaircase:
case df::enums::job_type::CarveRamp:
case df::enums::job_type::DigChannel:
case df::enums::job_type::FellTree:
case df::enums::job_type::GatherPlants:
case df::enums::job_type::RemoveConstruction:
case df::enums::job_type::CarveTrack:
{
if (listing->item->pos == DFCoord(pos.x(), pos.y(), pos.z()))
{
Job::removeJob(listing->item);
goto JOB_FOUND;
}
break;
}
default:
continue;
}
}
JOB_FOUND:
continue;
#endif
}
mc.WriteAll();
return CR_OK;
}
static command_result SetPauseState(color_ostream &stream, const SingleBool *in)
static command_result GetPauseState(color_ostream &stream, const EmptyMessage *in, SingleBool *out)
{
DFHack::World::SetPauseState(in->value());
out->set_value(World::ReadPauseState());
return CR_OK;
}
static command_result GetPauseState(color_ostream &stream, const EmptyMessage *in, SingleBool *out)
static command_result GetGameValidity(color_ostream &stream, const EmptyMessage * in, SingleBool *out)
{
out->set_value(World::ReadPauseState());
auto viewScreen = Gui::getCurViewscreen();
if (strict_virtual_cast<df::viewscreen_loadgamest>(viewScreen))
{
out->set_value(false);
return CR_OK;
}
else if (strict_virtual_cast<df::viewscreen_savegamest>(viewScreen))
{
out->set_value(false);
return CR_OK;
}
out->set_value(true);
return CR_OK;
}