Merge remote-tracking branch 'remotes/DFHack/develop' into RemoteServerUnsafe
# Conflicts: # plugins/proto/RemoteFortressReader.proto # plugins/remotefortressreader/remotefortressreader.cpp # scriptsdevelop
commit
7b19c9b8f0
@ -0,0 +1,14 @@
|
||||
###############
|
||||
Removed scripts
|
||||
###############
|
||||
|
||||
The following scripts were removed for various reasons.
|
||||
|
||||
.. contents::
|
||||
:depth: 2
|
||||
|
||||
.. _warn-stuck-trees:
|
||||
|
||||
warn-stuck-trees
|
||||
================
|
||||
The corresponding DF bug, :bug:`9252` was fixed in DF 0.44.01.
|
@ -0,0 +1,43 @@
|
||||
#include "Error.h"
|
||||
#include "MiscUtils.h"
|
||||
|
||||
using namespace DFHack::Error;
|
||||
|
||||
inline std::string safe_str(const char *s)
|
||||
{
|
||||
return s ? s : "(NULL)";
|
||||
}
|
||||
|
||||
NullPointer::NullPointer(const char *varname)
|
||||
:All("NULL pointer: " + safe_str(varname)),
|
||||
varname(varname)
|
||||
{}
|
||||
|
||||
InvalidArgument::InvalidArgument(const char *expr)
|
||||
:All("Invalid argument; expected: " + safe_str(expr)),
|
||||
expr(expr)
|
||||
{}
|
||||
|
||||
VTableMissing::VTableMissing(const char *name)
|
||||
:All("Missing vtable address: " + safe_str(name)),
|
||||
name(name)
|
||||
{}
|
||||
|
||||
SymbolsXmlParse::SymbolsXmlParse(const char* desc, int id, int row, int col)
|
||||
:AllSymbols(stl_sprintf("error %d: %s, at row %d col %d", id, desc, row, col)),
|
||||
desc(safe_str(desc)), id(id), row(row), col(col)
|
||||
{}
|
||||
|
||||
SymbolsXmlBadAttribute::SymbolsXmlBadAttribute(const char *attr)
|
||||
:AllSymbols("attribute is either missing or invalid: " + safe_str(attr)),
|
||||
attr(safe_str(attr))
|
||||
{}
|
||||
|
||||
SymbolsXmlNoRoot::SymbolsXmlNoRoot()
|
||||
:AllSymbols("no root element")
|
||||
{}
|
||||
|
||||
SymbolsXmlUnderspecifiedEntry::SymbolsXmlUnderspecifiedEntry(const char *where)
|
||||
:AllSymbols("Underspecified symbol file entry, each entry needs to set both the name attribute and have a value. parent: " + safe_str(where)),
|
||||
where(safe_str(where))
|
||||
{}
|
@ -0,0 +1,48 @@
|
||||
PROJECT (embark-assistant)
|
||||
# A list of source files
|
||||
SET(PROJECT_SRCS
|
||||
biome_type.cpp
|
||||
embark-assistant.cpp
|
||||
finder_ui.cpp
|
||||
help_ui.cpp
|
||||
matcher.cpp
|
||||
overlay.cpp
|
||||
screen.cpp
|
||||
survey.cpp
|
||||
)
|
||||
# A list of headers
|
||||
SET(PROJECT_HDRS
|
||||
biome_type.h
|
||||
defs.h
|
||||
embark-assistant.h
|
||||
finder_ui.h
|
||||
help_ui.h
|
||||
matcher.h
|
||||
overlay.h
|
||||
screen.h
|
||||
survey.h
|
||||
)
|
||||
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_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})
|
||||
|
||||
# option to use a thread for no particular reason
|
||||
#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON)
|
||||
#linux
|
||||
IF(UNIX)
|
||||
add_definitions(-DLINUX_BUILD)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra linux libs here
|
||||
${PROJECT_LIBS}
|
||||
)
|
||||
# windows
|
||||
ELSE(UNIX)
|
||||
SET(PROJECT_LIBS
|
||||
# add any extra windows libs here
|
||||
${PROJECT_LIBS}
|
||||
$(NOINHERIT)
|
||||
)
|
||||
ENDIF(UNIX)
|
||||
# this makes sure all the stuff is put in proper places and linked to dfhack
|
||||
DFHACK_PLUGIN(embark-assistant ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
|
@ -0,0 +1,754 @@
|
||||
/* 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
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// 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);
|
@ -0,0 +1,256 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using std::array;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace defs {
|
||||
// Survey types
|
||||
//
|
||||
enum class river_sizes {
|
||||
None,
|
||||
Brook,
|
||||
Stream,
|
||||
Minor,
|
||||
Medium,
|
||||
Major
|
||||
};
|
||||
|
||||
struct mid_level_tile {
|
||||
bool aquifer = false;
|
||||
bool clay = false;
|
||||
bool sand = false;
|
||||
bool flux = false;
|
||||
int8_t soil_depth;
|
||||
int8_t offset;
|
||||
int16_t elevation;
|
||||
bool river_present = false;
|
||||
int16_t river_elevation = 100;
|
||||
int8_t biome_offset;
|
||||
uint8_t savagery_level; // 0 - 2
|
||||
uint8_t evilness_level; // 0 - 2
|
||||
std::vector<bool> metals;
|
||||
std::vector<bool> economics;
|
||||
std::vector<bool> minerals;
|
||||
};
|
||||
|
||||
typedef std::array<std::array<mid_level_tile, 16>, 16> mid_level_tiles;
|
||||
// typedef mid_level_tile mid_level_tiles[16][16];
|
||||
|
||||
struct region_tile_datum {
|
||||
bool surveyed = false;
|
||||
uint16_t aquifer_count = 0;
|
||||
uint16_t clay_count = 0;
|
||||
uint16_t sand_count = 0;
|
||||
uint16_t flux_count = 0;
|
||||
uint8_t min_region_soil = 10;
|
||||
uint8_t max_region_soil = 0;
|
||||
bool waterfall = false;
|
||||
|
||||
river_sizes river_size;
|
||||
int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used
|
||||
int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used
|
||||
uint8_t biome_count;
|
||||
bool evil_weather[10];
|
||||
bool evil_weather_possible;
|
||||
bool evil_weather_full;
|
||||
bool reanimating[10];
|
||||
bool reanimating_possible;
|
||||
bool reanimating_full;
|
||||
bool thralling[10];
|
||||
bool thralling_possible;
|
||||
bool thralling_full;
|
||||
uint16_t savagery_count[3];
|
||||
uint16_t evilness_count[3];
|
||||
std::vector<bool> metals;
|
||||
std::vector<bool> economics;
|
||||
std::vector<bool> minerals;
|
||||
};
|
||||
|
||||
struct geo_datum {
|
||||
uint8_t soil_size = 0;
|
||||
bool top_soil_only = true;
|
||||
bool top_soil_aquifer_only = true;
|
||||
bool aquifer_absent = true;
|
||||
bool clay_absent = true;
|
||||
bool sand_absent = true;
|
||||
bool flux_absent = true;
|
||||
std::vector<bool> possible_metals;
|
||||
std::vector<bool> possible_economics;
|
||||
std::vector<bool> possible_minerals;
|
||||
};
|
||||
|
||||
typedef std::vector<geo_datum> geo_data;
|
||||
|
||||
struct sites {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
char type;
|
||||
};
|
||||
|
||||
struct site_infos {
|
||||
bool aquifer;
|
||||
bool aquifer_full;
|
||||
uint8_t min_soil;
|
||||
uint8_t max_soil;
|
||||
bool flat;
|
||||
bool waterfall;
|
||||
bool clay;
|
||||
bool sand;
|
||||
bool flux;
|
||||
std::vector<uint16_t> metals;
|
||||
std::vector<uint16_t> economics;
|
||||
std::vector<uint16_t> minerals;
|
||||
// Could add savagery, evilness, and biomes, but DF provides those easily.
|
||||
};
|
||||
|
||||
typedef std::vector<sites> site_lists;
|
||||
|
||||
typedef std::vector<std::vector<region_tile_datum>> world_tile_data;
|
||||
|
||||
typedef bool mlt_matches[16][16];
|
||||
// An embark region match is indicated by marking the top left corner
|
||||
// tile as a match. Thus, the bottom and right side won't show matches
|
||||
// unless the appropriate dimension has a width of 1.
|
||||
|
||||
struct matches {
|
||||
bool preliminary_match;
|
||||
bool contains_match;
|
||||
mlt_matches mlt_match;
|
||||
};
|
||||
|
||||
typedef std::vector<std::vector<matches>> match_results;
|
||||
|
||||
// matcher types
|
||||
//
|
||||
enum class evil_savagery_values : int8_t {
|
||||
NA = -1,
|
||||
All,
|
||||
Present,
|
||||
Absent
|
||||
};
|
||||
|
||||
enum class evil_savagery_ranges : int8_t {
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
};
|
||||
|
||||
enum class aquifer_ranges : int8_t {
|
||||
NA = -1,
|
||||
All,
|
||||
Present,
|
||||
Partial,
|
||||
Not_All,
|
||||
Absent
|
||||
};
|
||||
|
||||
enum class river_ranges : int8_t {
|
||||
NA = -1,
|
||||
None,
|
||||
Brook,
|
||||
Stream,
|
||||
Minor,
|
||||
Medium,
|
||||
Major
|
||||
};
|
||||
|
||||
enum class yes_no_ranges : int8_t {
|
||||
NA = -1,
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
|
||||
enum class all_present_ranges : int8_t {
|
||||
All,
|
||||
Present
|
||||
};
|
||||
enum class present_absent_ranges : int8_t {
|
||||
NA = -1,
|
||||
Present,
|
||||
Absent
|
||||
};
|
||||
|
||||
enum class soil_ranges : int8_t {
|
||||
NA = -1,
|
||||
None,
|
||||
Very_Shallow,
|
||||
Shallow,
|
||||
Deep,
|
||||
Very_Deep
|
||||
};
|
||||
|
||||
/* // Future possible enhancement
|
||||
enum class freezing_ranges : int8_t {
|
||||
NA = -1,
|
||||
Permanent,
|
||||
At_Least_Partial,
|
||||
Partial,
|
||||
At_Most_Partial,
|
||||
Never
|
||||
};
|
||||
*/
|
||||
|
||||
struct finders {
|
||||
uint16_t x_dim;
|
||||
uint16_t y_dim;
|
||||
evil_savagery_values savagery[static_cast<int8_t>(evil_savagery_ranges::High) + 1];
|
||||
evil_savagery_values evilness[static_cast<int8_t>(evil_savagery_ranges::High) + 1];
|
||||
aquifer_ranges aquifer;
|
||||
river_ranges min_river;
|
||||
river_ranges max_river;
|
||||
yes_no_ranges waterfall;
|
||||
yes_no_ranges flat;
|
||||
present_absent_ranges clay;
|
||||
present_absent_ranges sand;
|
||||
present_absent_ranges flux;
|
||||
soil_ranges soil_min;
|
||||
all_present_ranges soil_min_everywhere;
|
||||
soil_ranges soil_max;
|
||||
/*freezing_ranges freezing;*/
|
||||
yes_no_ranges evil_weather; // Will probably blow up with the magic release arcs...
|
||||
yes_no_ranges reanimation;
|
||||
yes_no_ranges thralling;
|
||||
int8_t biome_count_min; // N/A(-1), 1-9
|
||||
int8_t biome_count_max; // N/A(-1), 1-9
|
||||
int8_t region_type_1; // N/A(-1), df::world_region_type
|
||||
int8_t region_type_2; // N/A(-1), df::world_region_type
|
||||
int8_t region_type_3; // N/A(-1), df::world_region_type
|
||||
int8_t biome_1; // N/A(-1), df::biome_type
|
||||
int8_t biome_2; // N/A(-1), df::biome_type
|
||||
int8_t biome_3; // N/A(-1), df::biome_type
|
||||
int16_t metal_1; // N/A(-1), 0-max_inorganic;
|
||||
int16_t metal_2; // N/A(-1), 0-max_inorganic;
|
||||
int16_t metal_3; // N/A(-1), 0-max_inorganic;
|
||||
int16_t economic_1; // N/A(-1), 0-max_inorganic;
|
||||
int16_t economic_2; // N/A(-1), 0-max_inorganic;
|
||||
int16_t economic_3; // N/A(-1), 0-max_inorganic;
|
||||
int16_t mineral_1; // N/A(-1), 0-max_inorganic;
|
||||
int16_t mineral_2; // N/A(-1), 0-max_inorganic;
|
||||
int16_t mineral_3; // N/A(-1), 0-max_inorganic;
|
||||
};
|
||||
|
||||
struct match_iterators {
|
||||
bool active;
|
||||
uint16_t x; // x position of focus when iteration started so we can return it.
|
||||
uint16_t y; // y
|
||||
uint16_t i;
|
||||
uint16_t k;
|
||||
bool x_right;
|
||||
bool y_down;
|
||||
bool inhibit_x_turn;
|
||||
bool inhibit_y_turn;
|
||||
uint16_t count;
|
||||
finders finder;
|
||||
};
|
||||
|
||||
typedef void(*find_callbacks) (embark_assist::defs::finders finder);
|
||||
}
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <modules/Gui.h>
|
||||
#include <modules/Screen.h>
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/coord2d.h"
|
||||
#include "df/inorganic_flags.h"
|
||||
#include "df/inorganic_raw.h"
|
||||
#include "df/interfacest.h"
|
||||
#include "df/viewscreen.h"
|
||||
#include "df/viewscreen_choose_start_sitest.h"
|
||||
#include "df/world.h"
|
||||
#include "df/world_data.h"
|
||||
#include "df/world_geo_biome.h"
|
||||
#include "df/world_raws.h"
|
||||
|
||||
#include "defs.h"
|
||||
#include "embark-assistant.h"
|
||||
#include "finder_ui.h"
|
||||
#include "matcher.h"
|
||||
#include "overlay.h"
|
||||
#include "survey.h"
|
||||
|
||||
DFHACK_PLUGIN("embark-assistant");
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using namespace Gui;
|
||||
|
||||
REQUIRE_GLOBAL(world);
|
||||
|
||||
namespace embark_assist {
|
||||
namespace main {
|
||||
struct states {
|
||||
embark_assist::defs::geo_data geo_summary;
|
||||
embark_assist::defs::world_tile_data survey_results;
|
||||
embark_assist::defs::site_lists region_sites;
|
||||
embark_assist::defs::site_infos site_info;
|
||||
embark_assist::defs::match_results match_results;
|
||||
embark_assist::defs::match_iterators match_iterator;
|
||||
uint16_t max_inorganic;
|
||||
};
|
||||
|
||||
static states *state = nullptr;
|
||||
|
||||
void embark_update ();
|
||||
void shutdown();
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void embark_update() {
|
||||
auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0);
|
||||
embark_assist::defs::mid_level_tiles mlt;
|
||||
embark_assist::survey::initiate(&mlt);
|
||||
|
||||
embark_assist::survey::survey_mid_level_tile(&state->geo_summary,
|
||||
&state->survey_results,
|
||||
&mlt);
|
||||
|
||||
embark_assist::survey::survey_embark(&mlt, &state->site_info, false);
|
||||
embark_assist::overlay::set_embark(&state->site_info);
|
||||
|
||||
embark_assist::survey::survey_region_sites(&state->region_sites);
|
||||
embark_assist::overlay::set_sites(&state->region_sites);
|
||||
|
||||
embark_assist::overlay::set_mid_level_tile_match(state->match_results.at(screen->location.region_pos.x).at(screen->location.region_pos.y).mlt_match);
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void match() {
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
|
||||
uint16_t count = embark_assist::matcher::find(&state->match_iterator,
|
||||
&state->geo_summary,
|
||||
&state->survey_results,
|
||||
&state->match_results);
|
||||
|
||||
embark_assist::overlay::match_progress(count, &state->match_results, !state->match_iterator.active);
|
||||
|
||||
if (!state->match_iterator.active) {
|
||||
auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0);
|
||||
embark_assist::overlay::set_mid_level_tile_match(state->match_results.at(screen->location.region_pos.x).at(screen->location.region_pos.y).mlt_match);
|
||||
}
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void clear_match() {
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
if (state->match_iterator.active) {
|
||||
embark_assist::matcher::move_cursor(state->match_iterator.x, state->match_iterator.y);
|
||||
}
|
||||
embark_assist::survey::clear_results(&state->match_results);
|
||||
embark_assist::overlay::clear_match_results();
|
||||
embark_assist::main::state->match_iterator.active = false;
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void find(embark_assist::defs::finders finder) {
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
|
||||
state->match_iterator.x = embark_assist::survey::get_last_pos().x;
|
||||
state->match_iterator.y = embark_assist::survey::get_last_pos().y;
|
||||
state->match_iterator.finder = finder;
|
||||
embark_assist::overlay::initiate_match();
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void shutdown() {
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
embark_assist::survey::shutdown();
|
||||
embark_assist::finder_ui::shutdown();
|
||||
embark_assist::overlay::shutdown();
|
||||
delete state;
|
||||
state = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
|
||||
command_result embark_assistant (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
//=======================================================================================
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"embark-assistant", "Embark site selection support.",
|
||||
embark_assistant, true, /* 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 starts the embark-assist plugin that provides embark site\n"
|
||||
" selection help. It has to be called while the pre-embark screen is\n"
|
||||
" displayed and shows extended (and correct(?)) resource information for\n"
|
||||
" the embark rectangle as well as normally undisplayed sites in the\n"
|
||||
" current embark region. It also has a site selection tool with more\n"
|
||||
" options than DF's vanilla search tool. For detailed help invoke the\n"
|
||||
" in game info screen. Requires 42 lines to display properly.\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
|
||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case DFHack::SC_UNKNOWN:
|
||||
break;
|
||||
|
||||
case DFHack::SC_WORLD_LOADED:
|
||||
break;
|
||||
|
||||
case DFHack::SC_WORLD_UNLOADED:
|
||||
case DFHack::SC_MAP_LOADED:
|
||||
if (embark_assist::main::state) {
|
||||
embark_assist::main::shutdown();
|
||||
}
|
||||
break;
|
||||
|
||||
case DFHack::SC_MAP_UNLOADED:
|
||||
break;
|
||||
|
||||
case DFHack::SC_VIEWSCREEN_CHANGED:
|
||||
break;
|
||||
|
||||
case DFHack::SC_CORE_INITIALIZED:
|
||||
break;
|
||||
|
||||
case DFHack::SC_BEGIN_UNLOAD:
|
||||
break;
|
||||
|
||||
case DFHack::SC_PAUSED:
|
||||
break;
|
||||
|
||||
case DFHack::SC_UNPAUSED:
|
||||
break;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================================================
|
||||
|
||||
command_result embark_assistant(color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
if (!parameters.empty())
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
CoreSuspender suspend;
|
||||
|
||||
auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0);
|
||||
if (!screen) {
|
||||
out.printerr("This plugin works only in the embark site selection phase.\n");
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
df::world_data *world_data = world->world_data;
|
||||
|
||||
if (embark_assist::main::state) {
|
||||
out.printerr("You can't invoke the embark assistant while it's already active.\n");
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
embark_assist::main::state = new embark_assist::main::states;
|
||||
|
||||
embark_assist::main::state->match_iterator.active = false;
|
||||
|
||||
// Find the end of the normal inorganic definitions.
|
||||
embark_assist::main::state->max_inorganic = 0;
|
||||
for (uint16_t i = 0; i < world->raws.inorganics.size(); i++) {
|
||||
if (world->raws.inorganics[i]->flags.is_set(df::inorganic_flags::GENERATED)) embark_assist::main::state->max_inorganic = i;
|
||||
}
|
||||
embark_assist::main::state->max_inorganic++; // To allow it to be used as size() replacement
|
||||
|
||||
if (!embark_assist::overlay::setup(plugin_self,
|
||||
embark_assist::main::embark_update,
|
||||
embark_assist::main::match,
|
||||
embark_assist::main::clear_match,
|
||||
embark_assist::main::find,
|
||||
embark_assist::main::shutdown,
|
||||
embark_assist::main::state->max_inorganic)) {
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
embark_assist::survey::setup(embark_assist::main::state->max_inorganic);
|
||||
embark_assist::main::state->geo_summary.resize(world_data->geo_biomes.size());
|
||||
embark_assist::main::state->survey_results.resize(world->worldgen.worldgen_parms.dim_x);
|
||||
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
embark_assist::main::state->survey_results[i].resize(world->worldgen.worldgen_parms.dim_y);
|
||||
|
||||
for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) {
|
||||
embark_assist::main::state->survey_results[i][k].surveyed = false;
|
||||
embark_assist::main::state->survey_results[i][k].aquifer_count = 0;
|
||||
embark_assist::main::state->survey_results[i][k].clay_count = 0;
|
||||
embark_assist::main::state->survey_results[i][k].sand_count = 0;
|
||||
embark_assist::main::state->survey_results[i][k].flux_count = 0;
|
||||
embark_assist::main::state->survey_results[i][k].min_region_soil = 10;
|
||||
embark_assist::main::state->survey_results[i][k].max_region_soil = 0;
|
||||
embark_assist::main::state->survey_results[i][k].waterfall = false;
|
||||
embark_assist::main::state->survey_results[i][k].river_size = embark_assist::defs::river_sizes::None;
|
||||
|
||||
for (uint8_t l = 1; l < 10; l++) {
|
||||
embark_assist::main::state->survey_results[i][k].biome_index[l] = -1;
|
||||
embark_assist::main::state->survey_results[i][k].biome[l] = -1;
|
||||
embark_assist::main::state->survey_results[i][k].evil_weather[l] = false;
|
||||
embark_assist::main::state->survey_results[i][k].reanimating[l] = false;
|
||||
embark_assist::main::state->survey_results[i][k].thralling[l] = false;
|
||||
}
|
||||
|
||||
for (uint8_t l = 0; l < 2; l++) {
|
||||
embark_assist::main::state->survey_results[i][k].savagery_count[l] = 0;
|
||||
embark_assist::main::state->survey_results[i][k].evilness_count[l] = 0;
|
||||
}
|
||||
embark_assist::main::state->survey_results[i][k].metals.resize(embark_assist::main::state->max_inorganic);
|
||||
embark_assist::main::state->survey_results[i][k].economics.resize(embark_assist::main::state->max_inorganic);
|
||||
embark_assist::main::state->survey_results[i][k].minerals.resize(embark_assist::main::state->max_inorganic);
|
||||
}
|
||||
}
|
||||
|
||||
embark_assist::survey::high_level_world_survey(&embark_assist::main::state->geo_summary,
|
||||
&embark_assist::main::state->survey_results);
|
||||
|
||||
embark_assist::main::state->match_results.resize(world->worldgen.worldgen_parms.dim_x);
|
||||
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
embark_assist::main::state->match_results[i].resize(world->worldgen.worldgen_parms.dim_y);
|
||||
}
|
||||
|
||||
embark_assist::survey::clear_results(&embark_assist::main::state->match_results);
|
||||
embark_assist::survey::survey_region_sites(&embark_assist::main::state->region_sites);
|
||||
embark_assist::overlay::set_sites(&embark_assist::main::state->region_sites);
|
||||
|
||||
embark_assist::defs::mid_level_tiles mlt;
|
||||
embark_assist::survey::survey_mid_level_tile(&embark_assist::main::state->geo_summary, &embark_assist::main::state->survey_results, &mlt);
|
||||
embark_assist::survey::survey_embark(&mlt, &embark_assist::main::state->site_info, false);
|
||||
embark_assist::overlay::set_embark(&embark_assist::main::state->site_info);
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1 @@
|
||||
#pragma once
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace finder_ui {
|
||||
void init(DFHack::Plugin *plugin_self, embark_assist::defs::find_callbacks find_callback, uint16_t max_inorganic);
|
||||
void activate();
|
||||
void shutdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,314 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <modules/Gui.h>
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "help_ui.h"
|
||||
#include "screen.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace embark_assist{
|
||||
namespace help_ui {
|
||||
enum class pages {
|
||||
Intro,
|
||||
General,
|
||||
Finder,
|
||||
Caveats
|
||||
};
|
||||
|
||||
class ViewscreenHelpUi : public dfhack_viewscreen
|
||||
{
|
||||
public:
|
||||
ViewscreenHelpUi();
|
||||
|
||||
void feed(std::set<df::interface_key> *input);
|
||||
|
||||
void render();
|
||||
|
||||
std::string getFocusString() { return "Help UI"; }
|
||||
|
||||
private:
|
||||
pages current_page = pages::Intro;
|
||||
};
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void ViewscreenHelpUi::feed(std::set<df::interface_key> *input) {
|
||||
if (input->count(df::interface_key::LEAVESCREEN))
|
||||
{
|
||||
input->clear();
|
||||
Screen::dismiss(this);
|
||||
return;
|
||||
}
|
||||
else if (input->count(df::interface_key::CHANGETAB)) {
|
||||
switch (current_page) {
|
||||
case pages::Intro:
|
||||
current_page = pages::General;
|
||||
break;
|
||||
|
||||
case pages::General:
|
||||
current_page = pages::Finder;
|
||||
break;
|
||||
|
||||
case pages::Finder:
|
||||
current_page = pages::Caveats;
|
||||
break;
|
||||
|
||||
case pages::Caveats:
|
||||
current_page = pages::Intro;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (input->count(df::interface_key::SEC_CHANGETAB)) {
|
||||
switch (current_page) {
|
||||
case pages::Intro:
|
||||
current_page = pages::Caveats;
|
||||
break;
|
||||
|
||||
case pages::General:
|
||||
current_page = pages::Intro;
|
||||
break;
|
||||
|
||||
case pages::Finder:
|
||||
current_page = pages::General;
|
||||
break;
|
||||
|
||||
case pages::Caveats:
|
||||
current_page = pages::Intro;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
void ViewscreenHelpUi::render() {
|
||||
color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
auto screen_size = DFHack::Screen::getWindowSize();
|
||||
Screen::Pen pen(' ', COLOR_WHITE);
|
||||
Screen::Pen site_pen = Screen::Pen(' ', COLOR_YELLOW, COLOR_BLACK, false);
|
||||
Screen::Pen pen_lr(' ', COLOR_LIGHTRED);
|
||||
|
||||
std::vector<std::string> help_text;
|
||||
|
||||
Screen::clear();
|
||||
|
||||
switch (current_page) {
|
||||
case pages::Intro:
|
||||
Screen::drawBorder("Embark Assistant Help/Info Introduction Page");
|
||||
|
||||
help_text.push_back("Embark Assistant is used on the embark selection screen to provide information");
|
||||
help_text.push_back("to help selecting a suitable embark site. It provides three services:");
|
||||
help_text.push_back("- Display of normally invisible sites overlayed on the region map.");
|
||||
help_text.push_back("- Embark rectangle resources. More detailed and correct than vanilla DF.");
|
||||
help_text.push_back("- Site find search. Richer set of selection criteria than the vanilla");
|
||||
help_text.push_back(" DF Find that Embark Assistant suppresses (by using the same key).");
|
||||
help_text.push_back("");
|
||||
help_text.push_back("The functionality requires a screen height of at least 42 lines to display");
|
||||
help_text.push_back("correctly (that's the height of the Finder screen), as fitting everything");
|
||||
help_text.push_back("onto a standard 80*25 screen would be too challenging. The help is adjusted");
|
||||
help_text.push_back("to fit into onto an 80*42 screen.");
|
||||
help_text.push_back("This help/info is split over several screens, and you can move between them");
|
||||
help_text.push_back("using the TAB/Shift-TAB keys, and leave the help from any screen using ESC.");
|
||||
help_text.push_back("");
|
||||
help_text.push_back("When the Embark Assistant is started it provides site information (if any)");
|
||||
help_text.push_back("as an overlay over the region map. Beneath that you will find a summary of");
|
||||
help_text.push_back("the resources in the current embark rectangle (explained in more detail on");
|
||||
help_text.push_back("the the next screen).");
|
||||
help_text.push_back("On the right side the command keys the Embark Assistant uses are listed");
|
||||
help_text.push_back("(this partially overwrites the DFHack Embark Tools information until the");
|
||||
help_text.push_back("screen is resized). It can also be mentioned that the DF 'f'ind key help");
|
||||
help_text.push_back("at the bottom of the screen is masked as the functionality is overridden by");
|
||||
help_text.push_back("the Embark Assistant.");
|
||||
help_text.push_back("Main screen control keys used by the Embark Assistant:");
|
||||
help_text.push_back("i: Info/Help. Brings up this display.");
|
||||
help_text.push_back("f: Brings up the Find Embark screen. See the Find page for more information.");
|
||||
help_text.push_back("c: Clears the results of a Find operation, and also cancels an operation if");
|
||||
help_text.push_back(" one is under way.");
|
||||
help_text.push_back("q: Quits the Embark Assistant and brings you back to the vanilla DF interface.");
|
||||
help_text.push_back(" It can be noted that the Embark Assistant automatically cancels itself");
|
||||
help_text.push_back(" when DF leaves the embark screen either through <ESC>Abort Game or by");
|
||||
help_text.push_back(" embarking.");
|
||||
help_text.push_back("Below this a Matching World Tiles count is displayed. It shows the number");
|
||||
help_text.push_back("of World Tiles that have at least one embark matching the Find criteria.");
|
||||
|
||||
break;
|
||||
|
||||
case pages::General:
|
||||
Screen::drawBorder("Embark Assistant Help/Info General Page");
|
||||
|
||||
help_text.push_back("The Embark Assistant overlays the region map with characters indicating sites");
|
||||
help_text.push_back("normally not displayed by DF. The following key is used:");
|
||||
help_text.push_back("C: Camp");
|
||||
help_text.push_back("c: Cave. Only displayed if the DF worldgen parameter does not display caves.");
|
||||
help_text.push_back("i: Important Location. The author doesn't actually know what those are.");
|
||||
help_text.push_back("l: Lair");
|
||||
help_text.push_back("L: Labyrinth");
|
||||
help_text.push_back("M: Monument. The author is unsure how/if this is broken down further.");
|
||||
help_text.push_back("S: Shrine");
|
||||
help_text.push_back("V: Vault");
|
||||
help_text.push_back("The Embark info below the region map differs from the vanilla DF display in a");
|
||||
help_text.push_back("few respects. Firstly, it shows resources in the embark rectangle, rather than");
|
||||
help_text.push_back("DF's display of resources in the region DF currently displays. Secondly, the");
|
||||
help_text.push_back("DF display doesn't take elevation based soil erosion or the magma sea depth");
|
||||
help_text.push_back("into consideration, so it can display resources that actually are cut away.");
|
||||
help_text.push_back("(It can be noted that the DFHack Sand indicator does take these elements into");
|
||||
help_text.push_back("account).");
|
||||
help_text.push_back("The info the Embark Assistant displays is:");
|
||||
help_text.push_back("Sand, if present");
|
||||
help_text.push_back("Clay, if present");
|
||||
help_text.push_back("Min and Max soil depth in the embark rectangle.");
|
||||
help_text.push_back("Flat indicator if all the tiles in the embark have the same elevation.");
|
||||
help_text.push_back("Aquifer indicator, color coded as blue if all tiles have an aquifer and light");
|
||||
help_text.push_back("blue if some, but not all, tiles have one.");
|
||||
help_text.push_back("Waterfall, if the embark has river elevation differences.");
|
||||
help_text.push_back("Flux, if present");
|
||||
help_text.push_back("A list of all metals present in the embark.");
|
||||
help_text.push_back("A list of all economic minerals present in the embark. Both clays and flux");
|
||||
help_text.push_back("stones are economic, so they show up here as well.");
|
||||
help_text.push_back("In addition to the above, the Find functionality can also produce blinking");
|
||||
help_text.push_back("overlays over the region map and the middle world map to indicate where");
|
||||
help_text.push_back("matching embarks are found. The region display marks the top left corner of");
|
||||
help_text.push_back("a matching embark rectangle as a matching tile.");
|
||||
|
||||
break;
|
||||
|
||||
case pages::Finder:
|
||||
Screen::drawBorder("Embark Assistant Help/Info Find Page");
|
||||
|
||||
help_text.push_back("The Embark Assist Finder page is brought up with the f command key.");
|
||||
help_text.push_back("The top of the Finder page lists the command keys available on the page:");
|
||||
help_text.push_back("4/6 or horizontal arrow keys to move between the element and element values.");
|
||||
help_text.push_back("8/2 or vertical arrow keys to move up/down in the current list.");
|
||||
help_text.push_back("ENTER to select a value in the value list, entering it among the selections.");
|
||||
help_text.push_back("f to activate the Find functionality using the values in the middle column.");
|
||||
help_text.push_back("ESC to leave the screen without activating a Find operation.");
|
||||
help_text.push_back("The X and Y dimensions are those of the embark to search for. Unlike DF");
|
||||
help_text.push_back("itself these parameters are initiated to match the actual embark rectangle");
|
||||
help_text.push_back("when a new search is initiated (prior results are cleared.");
|
||||
help_text.push_back("The 6 Savagery and Evilness parameters takes some getting used to. They");
|
||||
help_text.push_back("allow for searching for embarks with e.g. has both Good and Evil tiles.");
|
||||
help_text.push_back("All as a parameter means every embark tile has to have this value.");
|
||||
help_text.push_back("Present means at least one embark tile has to have this value.");
|
||||
help_text.push_back("Absent means the feature mustn't exist in any of the embark tiles.");
|
||||
help_text.push_back("N/A means no restrictions are applied.");
|
||||
help_text.push_back("The Aquifer criterion introduces some new parameters:");
|
||||
help_text.push_back("Partial means at least one tile has to have an aquifer, but it also has");
|
||||
help_text.push_back("to be absent from at least one tile. Not All means an aquifer is tolerated");
|
||||
help_text.push_back("as long as at least one tile doesn't have one, but it doesn't have to have");
|
||||
help_text.push_back("any at all.");
|
||||
help_text.push_back("Min/Max rivers should be self explanatory. The Yes and No values of");
|
||||
help_text.push_back("Waterfall, Flat, etc. means one has to be Present and Absent respectivey.");
|
||||
help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil");
|
||||
help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and");
|
||||
help_text.push_back("and Present.");
|
||||
help_text.push_back("The parameters for biomes, regions, etc. all require that the required");
|
||||
help_text.push_back("feature is Present in the embark, and entering the same value multiple");
|
||||
help_text.push_back("times does nothing (the first match ticks all requirements off). It can be");
|
||||
help_text.push_back("noted that all the Economic materials are found in the much longer Mineral");
|
||||
help_text.push_back("list. Note that Find is a fairly time consuming task (is it is in vanilla).");
|
||||
break;
|
||||
|
||||
case pages::Caveats:
|
||||
Screen::drawBorder("Embark Assistant Help/Info Caveats Page");
|
||||
|
||||
help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough");
|
||||
help_text.push_back("world tile match to find tiles that may have matching embarks. This results");
|
||||
help_text.push_back("in an overlay of inverted yellow X on top of the middle world map. Then");
|
||||
help_text.push_back("those tiles are scanned in detail, one feature shell (16*16 world tile");
|
||||
help_text.push_back("block) at a time, and the results are displayed as green inverted X on");
|
||||
help_text.push_back("the same map (replacing or erasing the yellow ones). region map overlay");
|
||||
help_text.push_back("data is generated as well.");
|
||||
help_text.push_back("");
|
||||
help_text.push_back("Caveats & technical stuff:");
|
||||
help_text.push_back("- The Find searching uses simulated cursor movement input to DF to get it");
|
||||
help_text.push_back(" to load feature shells and detailed region data, and this costs the");
|
||||
help_text.push_back(" least when done one feature shell at a time.");
|
||||
help_text.push_back("- The search strategy causes detailed region data to update surveyed");
|
||||
help_text.push_back(" world info, and this can cause a subsequent search to generate a smaller");
|
||||
help_text.push_back(" set of preliminary matches (yellow tiles) than a previous search.");
|
||||
help_text.push_back(" However, this is a bug only if it causes the search to fail to find");
|
||||
help_text.push_back(" actual existing matches.");
|
||||
help_text.push_back("- The site info is deduced by the author, so there may be errors and");
|
||||
help_text.push_back(" there are probably site types that end up not being identified.");
|
||||
help_text.push_back("- Aquifer indications are based on the author's belief that they occur");
|
||||
help_text.push_back(" whenever an aquifer supporting layer is present at a depth of 3 or");
|
||||
help_text.push_back(" more.");
|
||||
help_text.push_back("- The biome determination logic comes from code provided by Ragundo,");
|
||||
help_text.push_back(" with only marginal changes by the author. References can be found in");
|
||||
help_text.push_back(" the source file.");
|
||||
help_text.push_back("- Thralling is determined by weather material interactions causing");
|
||||
help_text.push_back(" blinking, which the author believes is one of 4 thralling changes.");
|
||||
help_text.push_back("- The geo information is gathered by code which is essentially a");
|
||||
help_text.push_back(" copy of parts of prospector's code adapted for this plugin.");
|
||||
help_text.push_back("- Clay determination is made by finding the reaction MAKE_CLAY_BRICKS.");
|
||||
help_text.push_back(" Flux determination is made by finding the reaction PIG_IRON_MAKING.");
|
||||
help_text.push_back("- Right world map overlay not implemented as author has failed to");
|
||||
help_text.push_back(" emulate the sizing logic exactly.");
|
||||
help_text.push_back("Version 0.1 2017-08-30");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Add control keys to first line.
|
||||
embark_assist::screen::paintString(pen_lr, 1, 1, "TAB/Shift-TAB");
|
||||
embark_assist::screen::paintString(pen, 14, 1, ":Next/Previous Page");
|
||||
embark_assist::screen::paintString(pen_lr, 34, 1, "ESC");
|
||||
embark_assist::screen::paintString(pen, 37, 1, ":Leave Info/Help");
|
||||
|
||||
for (uint16_t i = 0; i < help_text.size(); i++) {
|
||||
embark_assist::screen::paintString(pen, 1, 2 + i, help_text[i]);
|
||||
}
|
||||
|
||||
switch (current_page) {
|
||||
case pages::Intro:
|
||||
embark_assist::screen::paintString(pen_lr, 1, 26, "i");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 27, "f");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 28, "c");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 30, "q");
|
||||
break;
|
||||
|
||||
case pages::General:
|
||||
embark_assist::screen::paintString(site_pen, 1, 4, "C");
|
||||
embark_assist::screen::paintString(site_pen, 1, 5, "c");
|
||||
embark_assist::screen::paintString(site_pen, 1, 6, "i");
|
||||
embark_assist::screen::paintString(site_pen, 1, 7, "l");
|
||||
embark_assist::screen::paintString(site_pen, 1, 8, "L");
|
||||
embark_assist::screen::paintString(site_pen, 1, 9, "M");
|
||||
embark_assist::screen::paintString(site_pen, 1, 10, "S");
|
||||
embark_assist::screen::paintString(site_pen, 1, 11, "V");
|
||||
break;
|
||||
|
||||
case pages::Finder:
|
||||
embark_assist::screen::paintString(pen_lr, 1, 4, "4/6");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 5, "8/2");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 6, "ENTER");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 7, "f");
|
||||
embark_assist::screen::paintString(pen_lr, 1, 8, "ESC");
|
||||
break;
|
||||
|
||||
case pages::Caveats:
|
||||
break;
|
||||
}
|
||||
dfhack_viewscreen::render();
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
ViewscreenHelpUi::ViewscreenHelpUi() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
// Exported operations
|
||||
//===============================================================================
|
||||
|
||||
void embark_assist::help_ui::init(DFHack::Plugin *plugin_self) {
|
||||
Screen::show(new embark_assist::help_ui::ViewscreenHelpUi(), plugin_self);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace help_ui {
|
||||
void init(DFHack::Plugin *plugin_self);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace matcher {
|
||||
void move_cursor(uint16_t x, uint16_t y);
|
||||
|
||||
// Used to iterate over the whole world to generate a map of world tiles
|
||||
// that contain matching embarks.
|
||||
//
|
||||
uint16_t find(embark_assist::defs::match_iterators *iterator,
|
||||
embark_assist::defs::geo_data *geo_summary,
|
||||
embark_assist::defs::world_tile_data *survey_results,
|
||||
embark_assist::defs::match_results *match_results);
|
||||
}
|
||||
}
|
@ -0,0 +1,439 @@
|
||||
#include <modules/Gui.h>
|
||||
|
||||
#include "df/coord2d.h"
|
||||
#include "df/inorganic_raw.h"
|
||||
#include "df/dfhack_material_category.h"
|
||||
#include "df/interface_key.h"
|
||||
#include "df/viewscreen.h"
|
||||
#include "df/viewscreen_choose_start_sitest.h"
|
||||
#include "df/world.h"
|
||||
#include "df/world_raws.h"
|
||||
|
||||
#include "finder_ui.h"
|
||||
#include "help_ui.h"
|
||||
#include "overlay.h"
|
||||
#include "screen.h"
|
||||
|
||||
using df::global::world;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace overlay {
|
||||
DFHack::Plugin *plugin_self;
|
||||
const Screen::Pen empty_pen = Screen::Pen('\0', COLOR_YELLOW, COLOR_BLACK, false);
|
||||
const Screen::Pen yellow_x_pen = Screen::Pen('X', COLOR_BLACK, COLOR_YELLOW, false);
|
||||
const Screen::Pen green_x_pen = Screen::Pen('X', COLOR_BLACK, COLOR_GREEN, false);
|
||||
|
||||
struct display_strings {
|
||||
Screen::Pen pen;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
typedef Screen::Pen *pen_column;
|
||||
|
||||
struct states {
|
||||
int blink_count = 0;
|
||||
bool show = true;
|
||||
|
||||
bool matching = false;
|
||||
bool match_active = false;
|
||||
|
||||
embark_update_callbacks embark_update;
|
||||
match_callbacks match_callback;
|
||||
clear_match_callbacks clear_match_callback;
|
||||
embark_assist::defs::find_callbacks find_callback;
|
||||
shutdown_callbacks shutdown_callback;
|
||||
|
||||
Screen::Pen site_grid[16][16];
|
||||
uint8_t current_site_grid = 0;
|
||||
|
||||
std::vector<display_strings> embark_info;
|
||||
|
||||
Screen::Pen region_match_grid[16][16];
|
||||
|
||||
pen_column *world_match_grid = nullptr;
|
||||
uint16_t match_count = 0;
|
||||
|
||||
uint16_t max_inorganic;
|
||||
};
|
||||
|
||||
static states *state = nullptr;
|
||||
|
||||
//====================================================================
|
||||
|
||||
/* // Attempt to replicate the DF logic for sizing the right world map. This
|
||||
// code seems to compute the values correctly, but the author hasn't been
|
||||
// able to apply them at the same time as DF does to 100%.
|
||||
// DF seems to round down on 0.5 values.
|
||||
df::coord2d world_dimension_size(uint16_t available_screen, uint16_t map_size) {
|
||||
uint16_t result;
|
||||
|
||||
for (uint16_t factor = 1; factor < 17; factor++) {
|
||||
result = map_size / factor;
|
||||
if ((map_size - result * factor) * 2 != factor) {
|
||||
result = (map_size + factor / 2) / factor;
|
||||
}
|
||||
|
||||
if (result <= available_screen) {
|
||||
return {result, factor};
|
||||
}
|
||||
}
|
||||
return{16, 16}; // Should never get here.
|
||||
}
|
||||
*/
|
||||
//====================================================================
|
||||
|
||||
class ViewscreenOverlay : public df::viewscreen_choose_start_sitest
|
||||
{
|
||||
public:
|
||||
typedef df::viewscreen_choose_start_sitest interpose_base;
|
||||
|
||||
void send_key(const df::interface_key &key)
|
||||
{
|
||||
std::set< df::interface_key > keys;
|
||||
keys.insert(key);
|
||||
this->feed(&keys);
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
|
||||
{
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
if (input->count(df::interface_key::CUSTOM_Q)) {
|
||||
state->shutdown_callback();
|
||||
return;
|
||||
|
||||
}
|
||||
else if (input->count(df::interface_key::SETUP_LOCAL_X_MUP) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_X_MDOWN) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_Y_MUP) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_Y_MDOWN) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_X_UP) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_X_DOWN) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_Y_UP) ||
|
||||
input->count(df::interface_key::SETUP_LOCAL_Y_DOWN)) {
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
state->embark_update();
|
||||
}
|
||||
else if (input->count(df::interface_key::CUSTOM_C)) {
|
||||
state->match_active = false;
|
||||
state->matching = false;
|
||||
state->clear_match_callback();
|
||||
}
|
||||
else if (input->count(df::interface_key::CUSTOM_F)) {
|
||||
if (!state->match_active && !state->matching) {
|
||||
embark_assist::finder_ui::init(embark_assist::overlay::plugin_self, state->find_callback, state->max_inorganic);
|
||||
}
|
||||
}
|
||||
else if (input->count(df::interface_key::CUSTOM_I)) {
|
||||
embark_assist::help_ui::init(embark_assist::overlay::plugin_self);
|
||||
}
|
||||
else {
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
auto current_screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0);
|
||||
int16_t x = current_screen->location.region_pos.x;
|
||||
int16_t y = current_screen->location.region_pos.y;
|
||||
auto width = Screen::getWindowSize().x;
|
||||
auto height = Screen::getWindowSize().y;
|
||||
|
||||
state->blink_count++;
|
||||
if (state->blink_count == 35) {
|
||||
state->blink_count = 0;
|
||||
state->show = !state->show;
|
||||
}
|
||||
|
||||
if (state->matching) state->show = true;
|
||||
|
||||
Screen::drawBorder("Embark Assistant");
|
||||
|
||||
Screen::Pen pen_lr(' ', COLOR_LIGHTRED);
|
||||
Screen::Pen pen_w(' ', COLOR_WHITE);
|
||||
|
||||
Screen::paintString(pen_lr, width - 28, 20, "i", false);
|
||||
Screen::paintString(pen_w, width - 27, 20, ":Embark Assistant Info", false);
|
||||
Screen::paintString(pen_lr, width - 28, 21, "f", false);
|
||||
Screen::paintString(pen_w, width - 27, 21, ":Find Embark ", false);
|
||||
Screen::paintString(pen_lr, width - 28, 22, "c", false);
|
||||
Screen::paintString(pen_w, width - 27, 22, ":Cancel/Clear Find", false);
|
||||
Screen::paintString(pen_lr, width - 28, 23, "q", false);
|
||||
Screen::paintString(pen_w, width - 27, 23, ":Quit Embark Assistant", false);
|
||||
Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles", false);
|
||||
Screen::paintString(empty_pen, width - 7, 25, to_string(state->match_count), false);
|
||||
|
||||
if (height > 25) { // Mask the vanilla DF find help as it's overridden.
|
||||
Screen::paintString(pen_w, 50, height - 2, " ", false);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
for (uint8_t k = 0; k < 16; k++) {
|
||||
if (state->site_grid[i][k].ch) {
|
||||
Screen::paintTile(state->site_grid[i][k], i + 1, k + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = 0; i < state->embark_info.size(); i++) {
|
||||
embark_assist::screen::paintString(state->embark_info[i].pen, 1, i + 19, state->embark_info[i].text, false);
|
||||
}
|
||||
|
||||
if (state->show) {
|
||||
int16_t left_x = x - (width / 2 - 7 - 18 + 1) / 2;
|
||||
int16_t right_x;
|
||||
int16_t top_y = y - (height - 8 - 2 + 1) / 2;
|
||||
int16_t bottom_y;
|
||||
|
||||
if (left_x < 0) { left_x = 0; }
|
||||
|
||||
if (top_y < 0) { top_y = 0; }
|
||||
|
||||
right_x = left_x + width / 2 - 7 - 18;
|
||||
bottom_y = top_y + height - 8 - 2;
|
||||
|
||||
if (right_x >= world->worldgen.worldgen_parms.dim_x) {
|
||||
right_x = world->worldgen.worldgen_parms.dim_x - 1;
|
||||
left_x = right_x - (width / 2 - 7 - 18);
|
||||
}
|
||||
|
||||
if (bottom_y >= world->worldgen.worldgen_parms.dim_y) {
|
||||
bottom_y = world->worldgen.worldgen_parms.dim_y - 1;
|
||||
top_y = bottom_y - (height - 8 - 2);
|
||||
}
|
||||
|
||||
if (left_x < 0) { left_x = 0; }
|
||||
|
||||
if (top_y < 0) { top_y = 0; }
|
||||
|
||||
|
||||
for (uint16_t i = left_x; i <= right_x; i++) {
|
||||
for (uint16_t k = top_y; k <= bottom_y; k++) {
|
||||
if (state->world_match_grid[i][k].ch) {
|
||||
Screen::paintTile(state->world_match_grid[i][k], i - left_x + 18, k - top_y + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
for (uint8_t k = 0; k < 16; k++) {
|
||||
if (state->region_match_grid[i][k].ch) {
|
||||
Screen::paintTile(state->region_match_grid[i][k], i + 1, k + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* // Stuff for trying to replicate the DF right world map sizing logic. Close, but not there.
|
||||
Screen::Pen pen(' ', COLOR_YELLOW);
|
||||
// Boundaries of the top level world map
|
||||
Screen::paintString(pen, width / 2 - 5, 2, "X", false); // Marks UL corner of right world map. Constant
|
||||
// Screen::paintString(pen, width - 30, 2, "X", false); // Marks UR corner of right world map area.
|
||||
// Screen::paintString(pen, width / 2 - 5, height - 8, "X", false); // BL corner of right world map area.
|
||||
// Screen::paintString(pen, width - 30, height - 8, "X", false); // BR corner of right world map area.
|
||||
|
||||
uint16_t l_width = width - 30 - (width / 2 - 5) + 1; // Horizontal space available for right world map.
|
||||
uint16_t l_height = height - 8 - 2 + 1; // Vertical space available for right world map.
|
||||
df::coord2d size_factor_x = world_dimension_size(l_width, world->worldgen.worldgen_parms.dim_x);
|
||||
df::coord2d size_factor_y = world_dimension_size(l_height, world->worldgen.worldgen_parms.dim_y);
|
||||
|
||||
Screen::paintString(pen, width / 2 - 5 + size_factor_x.x - 1, 2, "X", false);
|
||||
Screen::paintString(pen, width / 2 - 5, 2 + size_factor_y.x - 1, "X", false);
|
||||
Screen::paintString(pen, width / 2 - 5 + size_factor_x.x - 1, 2 + size_factor_y.x - 1, "X", false);
|
||||
*/
|
||||
}
|
||||
|
||||
if (state->matching) {
|
||||
embark_assist::overlay::state->match_callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(embark_assist::overlay::ViewscreenOverlay, feed);
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(embark_assist::overlay::ViewscreenOverlay, render);
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
bool embark_assist::overlay::setup(DFHack::Plugin *plugin_self,
|
||||
embark_update_callbacks embark_update_callback,
|
||||
match_callbacks match_callback,
|
||||
clear_match_callbacks clear_match_callback,
|
||||
embark_assist::defs::find_callbacks find_callback,
|
||||
shutdown_callbacks shutdown_callback,
|
||||
uint16_t max_inorganic)
|
||||
{
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
state = new(states);
|
||||
|
||||
embark_assist::overlay::plugin_self = plugin_self;
|
||||
embark_assist::overlay::state->embark_update = embark_update_callback;
|
||||
embark_assist::overlay::state->match_callback = match_callback;
|
||||
embark_assist::overlay::state->clear_match_callback = clear_match_callback;
|
||||
embark_assist::overlay::state->find_callback = find_callback;
|
||||
embark_assist::overlay::state->shutdown_callback = shutdown_callback;
|
||||
embark_assist::overlay::state->max_inorganic = max_inorganic;
|
||||
embark_assist::overlay::state->match_active = false;
|
||||
|
||||
state->world_match_grid = new pen_column[world->worldgen.worldgen_parms.dim_x];
|
||||
if (!state->world_match_grid) {
|
||||
return false; // Out of memory
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
state->world_match_grid[i] = new Screen::Pen[world->worldgen.worldgen_parms.dim_y];
|
||||
if (!state->world_match_grid[i]) { // Out of memory.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
clear_match_results();
|
||||
|
||||
return INTERPOSE_HOOK(embark_assist::overlay::ViewscreenOverlay, feed).apply(true) &&
|
||||
INTERPOSE_HOOK(embark_assist::overlay::ViewscreenOverlay, render).apply(true);
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::set_sites(embark_assist::defs::site_lists *site_list) {
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
for (uint8_t k = 0; k < 16; k++) {
|
||||
state->site_grid[i][k] = empty_pen;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < site_list->size(); i++) {
|
||||
state->site_grid[site_list->at(i).x][site_list->at(i).y].ch = site_list->at(i).type;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::initiate_match() {
|
||||
embark_assist::overlay::state->matching = true;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::match_progress(uint16_t count, embark_assist::defs::match_results *match_results, bool done) {
|
||||
// color_ostream_proxy out(Core::getInstance().getConsole());
|
||||
state->matching = !done;
|
||||
state->match_count = count;
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) {
|
||||
if (match_results->at(i).at(k).preliminary_match) {
|
||||
state->world_match_grid[i][k] = yellow_x_pen;
|
||||
|
||||
} else if (match_results->at(i).at(k).contains_match) {
|
||||
state->world_match_grid[i][k] = green_x_pen;
|
||||
}
|
||||
else {
|
||||
state->world_match_grid[i][k] = empty_pen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_info) {
|
||||
state->embark_info.clear();
|
||||
|
||||
if (site_info->sand) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_YELLOW), "Sand" });
|
||||
}
|
||||
|
||||
if (site_info->clay) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_RED), "Clay" });
|
||||
}
|
||||
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Soil " + std::to_string(site_info->min_soil) + " - " + std::to_string(site_info->max_soil) });
|
||||
|
||||
if (site_info->flat) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Flat" });
|
||||
}
|
||||
|
||||
if (site_info->aquifer) {
|
||||
if (site_info->aquifer_full) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Aquifer" });
|
||||
|
||||
}
|
||||
else {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Aquifer" });
|
||||
}
|
||||
}
|
||||
|
||||
if (site_info->waterfall) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Waterfall" });
|
||||
}
|
||||
|
||||
if (site_info->flux) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), "Flux" });
|
||||
}
|
||||
|
||||
for (auto const& i : site_info->metals) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_GREY), world->raws.inorganics[i]->id });
|
||||
}
|
||||
|
||||
for (auto const& i : site_info->economics) {
|
||||
state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), world->raws.inorganics[i]->id });
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::set_mid_level_tile_match(embark_assist::defs::mlt_matches mlt_matches) {
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
for (uint8_t k = 0; k < 16; k++) {
|
||||
if (mlt_matches[i][k]) {
|
||||
state->region_match_grid[i][k] = green_x_pen;
|
||||
|
||||
}
|
||||
else {
|
||||
state->region_match_grid[i][k] = empty_pen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::clear_match_results() {
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) {
|
||||
state->world_match_grid[i][k] = empty_pen;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
for (uint8_t k = 0; k < 16; k++) {
|
||||
state->region_match_grid[i][k] = empty_pen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
||||
void embark_assist::overlay::shutdown() {
|
||||
if (state &&
|
||||
state->world_match_grid) {
|
||||
INTERPOSE_HOOK(ViewscreenOverlay, render).remove();
|
||||
INTERPOSE_HOOK(ViewscreenOverlay, feed).remove();
|
||||
|
||||
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
|
||||
delete[] state->world_match_grid[i];
|
||||
}
|
||||
|
||||
delete[] state->world_match_grid;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
state->embark_info.clear();
|
||||
delete state;
|
||||
state = nullptr;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/viewscreen_choose_start_sitest.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using df::global::enabler;
|
||||
using df::global::gps;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace overlay {
|
||||
typedef void(*embark_update_callbacks)();
|
||||
typedef void(*match_callbacks)();
|
||||
typedef void(*clear_match_callbacks)();
|
||||
typedef void(*shutdown_callbacks)();
|
||||
|
||||
bool setup(DFHack::Plugin *plugin_self,
|
||||
embark_update_callbacks embark_update_callback,
|
||||
match_callbacks match_callback,
|
||||
clear_match_callbacks clear_match_callback,
|
||||
embark_assist::defs::find_callbacks find_callback,
|
||||
shutdown_callbacks shutdown_callback,
|
||||
uint16_t max_inorganic);
|
||||
|
||||
void set_sites(embark_assist::defs::site_lists *site_list);
|
||||
void initiate_match();
|
||||
void match_progress(uint16_t count, embark_assist::defs::match_results *match_results, bool done);
|
||||
void set_embark(embark_assist::defs::site_infos *site_info);
|
||||
void set_mid_level_tile_match(embark_assist::defs::mlt_matches mlt_matches);
|
||||
void clear_match_results();
|
||||
void shutdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#include "screen.h"
|
||||
|
||||
namespace embark_assist {
|
||||
namespace screen {
|
||||
}
|
||||
}
|
||||
|
||||
bool embark_assist::screen::paintString(const DFHack::Screen::Pen &pen, int x, int y, const std::string &text, bool map) {
|
||||
auto screen_size = DFHack::Screen::getWindowSize();
|
||||
|
||||
if (y < 1 || y + 1 >= screen_size.y || x < 1)
|
||||
{
|
||||
return false; // Won't paint outside of the screen or on the frame
|
||||
}
|
||||
|
||||
if (x + text.length() - 1 < screen_size.x - 2) {
|
||||
DFHack::Screen::paintString(pen, x, y, text, map);
|
||||
}
|
||||
else if (x < screen_size.x - 2) {
|
||||
DFHack::Screen::paintString(pen, x, y, text.substr(0, screen_size.x - 2 - x + 1), map);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#include "modules/Screen.h"
|
||||
|
||||
namespace embark_assist {
|
||||
namespace screen {
|
||||
bool paintString(const DFHack::Screen::Pen &pen, int x, int y, const std::string &text, bool map = false);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/coord2d.h"
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
namespace embark_assist {
|
||||
namespace survey {
|
||||
void setup(uint16_t max_inorganic);
|
||||
|
||||
df::coord2d get_last_pos();
|
||||
|
||||
void initiate(embark_assist::defs::mid_level_tiles *mlt);
|
||||
|
||||
void clear_results(embark_assist::defs::match_results *match_results);
|
||||
|
||||
void high_level_world_survey(embark_assist::defs::geo_data *geo_summary,
|
||||
embark_assist::defs::world_tile_data *survey_results);
|
||||
|
||||
void survey_mid_level_tile(embark_assist::defs::geo_data *geo_summary,
|
||||
embark_assist::defs::world_tile_data *survey_results,
|
||||
embark_assist::defs::mid_level_tiles *mlt);
|
||||
|
||||
df::coord2d apply_offset(uint16_t x, uint16_t y, int8_t offset);
|
||||
|
||||
void survey_region_sites(embark_assist::defs::site_lists *site_list);
|
||||
|
||||
void survey_embark(embark_assist::defs::mid_level_tiles *mlt,
|
||||
embark_assist::defs::site_infos *site_info,
|
||||
bool use_cache);
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package AdventureControl;
|
||||
|
||||
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
import "RemoteFortressReader.proto";
|
||||
|
||||
enum AdvmodeMenu
|
||||
{
|
||||
Default = 0;
|
||||
Look = 1;
|
||||
ConversationAddress = 2;
|
||||
ConversationSelect = 3;
|
||||
ConversationSpeak = 4;
|
||||
Inventory = 5;
|
||||
Drop = 6;
|
||||
ThrowItem = 7;
|
||||
Wear = 8;
|
||||
Remove = 9;
|
||||
Interact = 10;
|
||||
Put = 11;
|
||||
PutContainer = 12;
|
||||
Eat = 13;
|
||||
ThrowAim = 14;
|
||||
Fire = 15;
|
||||
Get = 16;
|
||||
Unk17 = 17;
|
||||
CombatPrefs = 18;
|
||||
Companions = 19;
|
||||
MovementPrefs = 20;
|
||||
SpeedPrefs = 21;
|
||||
InteractAction = 22;
|
||||
MoveCarefully = 23;
|
||||
Announcements = 24;
|
||||
UseBuilding = 25;
|
||||
Travel = 26;
|
||||
Unk27 = 27;
|
||||
Unk28 = 28;
|
||||
SleepConfirm = 29;
|
||||
SelectInteractionTarget = 30;
|
||||
Unk31 = 31;
|
||||
Unk32 = 32;
|
||||
FallAction = 33;
|
||||
ViewTracks = 34;
|
||||
Jump = 35;
|
||||
Unk36 = 36;
|
||||
AttackConfirm = 37;
|
||||
AttackType = 38;
|
||||
AttackBodypart = 39;
|
||||
AttackStrike = 40;
|
||||
Unk41 = 41;
|
||||
Unk42 = 42;
|
||||
DodgeDirection = 43;
|
||||
Unk44 = 44;
|
||||
Unk45 = 45;
|
||||
Build = 46;
|
||||
}
|
||||
|
||||
enum CarefulMovementType
|
||||
{
|
||||
DEFAULT_MOVEMENT = 0;
|
||||
RELEASE_ITEM_HOLD = 1;
|
||||
RELEASE_TILE_HOLD = 2;
|
||||
ATTACK_CREATURE = 3;
|
||||
HOLD_TILE = 4;
|
||||
MOVE = 5;
|
||||
CLIMB = 6;
|
||||
HOLD_ITEM = 7;
|
||||
BUILDING_INTERACT = 8;
|
||||
ITEM_INTERACT = 9;
|
||||
ITEM_INTERACT_GUIDE = 10;
|
||||
ITEM_INTERACT_RIDE = 11;
|
||||
ITEM_INTERACT_PUSH = 12;
|
||||
}
|
||||
|
||||
enum MiscMoveType
|
||||
{
|
||||
SET_CLIMB = 0;
|
||||
SET_STAND = 1;
|
||||
SET_CANCEL = 2;
|
||||
}
|
||||
|
||||
message MoveCommandParams
|
||||
{
|
||||
optional RemoteFortressReader.Coord direction = 1;
|
||||
}
|
||||
|
||||
message MovementOption
|
||||
{
|
||||
optional RemoteFortressReader.Coord dest = 1;
|
||||
optional RemoteFortressReader.Coord source = 2;
|
||||
optional RemoteFortressReader.Coord grab = 3;
|
||||
optional CarefulMovementType movement_type = 4;
|
||||
}
|
||||
|
||||
message MenuContents
|
||||
{
|
||||
optional AdvmodeMenu current_menu = 1;
|
||||
repeated MovementOption movements = 2;
|
||||
}
|
||||
|
||||
message MiscMoveParams
|
||||
{
|
||||
optional MiscMoveType type = 1;
|
||||
}
|
@ -0,0 +1,317 @@
|
||||
#include "adventure_control.h"
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "df/adventure_movement_attack_creaturest.h"
|
||||
#include "df/adventure_movement_building_interactst.h"
|
||||
#include "df/adventure_movement_climbst.h"
|
||||
#include "df/adventure_movement_hold_itemst.h"
|
||||
#include "df/adventure_movement_hold_tilest.h"
|
||||
#include "df/adventure_movement_optionst.h"
|
||||
#include "df/ui_advmode.h"
|
||||
#include "df/viewscreen.h"
|
||||
|
||||
#include "modules/Gui.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
using namespace AdventureControl;
|
||||
using namespace df::enums;
|
||||
using namespace DFHack;
|
||||
using namespace Gui;
|
||||
|
||||
std::queue<interface_key::interface_key> keyQueue;
|
||||
|
||||
void KeyUpdate()
|
||||
{
|
||||
if (!keyQueue.empty())
|
||||
{
|
||||
getCurViewscreen()->feed_key(keyQueue.front());
|
||||
keyQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void SetCoord(df::coord in, RemoteFortressReader::Coord *out)
|
||||
{
|
||||
out->set_x(in.x);
|
||||
out->set_y(in.y);
|
||||
out->set_z(in.z);
|
||||
}
|
||||
|
||||
command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParams *in)
|
||||
{
|
||||
auto viewScreen = getCurViewscreen();
|
||||
if (!in->has_direction())
|
||||
return CR_WRONG_USAGE;
|
||||
if (!df::global::ui_advmode->menu == ui_advmode_menu::Default)
|
||||
return CR_OK;
|
||||
auto dir = in->direction();
|
||||
switch (dir.x())
|
||||
{
|
||||
case -1:
|
||||
switch (dir.y())
|
||||
{
|
||||
case -1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NW_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NW);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NW_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_W_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_W);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_W_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SW_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SW);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SW_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
switch (dir.y())
|
||||
{
|
||||
case -1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_N_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_N);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_N_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_DOWN);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_S_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_S);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_S_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (dir.y())
|
||||
{
|
||||
case -1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NE_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NE);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_NE_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_E_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_E);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_E_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (dir.z())
|
||||
{
|
||||
case -1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SE_DOWN);
|
||||
break;
|
||||
case 0:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SE);
|
||||
break;
|
||||
case 1:
|
||||
viewScreen->feed_key(interface_key::A_MOVE_SE_UP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParams *in)
|
||||
{
|
||||
if (!in->has_direction())
|
||||
return CR_WRONG_USAGE;
|
||||
if (!df::global::ui_advmode->menu == ui_advmode_menu::Default)
|
||||
return CR_OK;
|
||||
auto dir = in->direction();
|
||||
keyQueue.push(interface_key::A_JUMP);
|
||||
int x = dir.x();
|
||||
int y = dir.y();
|
||||
if (x > 0)
|
||||
{
|
||||
for (int i = 0; i < x; i++)
|
||||
{
|
||||
keyQueue.push(interface_key::CURSOR_RIGHT);
|
||||
}
|
||||
}
|
||||
if (x < 0)
|
||||
{
|
||||
for (int i = 0; i > x; i--)
|
||||
{
|
||||
keyQueue.push(interface_key::CURSOR_LEFT);
|
||||
}
|
||||
}
|
||||
if (y > 0)
|
||||
{
|
||||
for (int i = 0; i < y; i++)
|
||||
{
|
||||
keyQueue.push(interface_key::CURSOR_DOWN);
|
||||
}
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
for (int i = 0; i > y; i--)
|
||||
{
|
||||
keyQueue.push(interface_key::CURSOR_UP);
|
||||
}
|
||||
}
|
||||
keyQueue.push(interface_key::SELECT);
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, MenuContents *out)
|
||||
{
|
||||
auto advUi = df::global::ui_advmode;
|
||||
|
||||
if (advUi == NULL)
|
||||
return CR_FAILURE;
|
||||
|
||||
out->set_current_menu((AdvmodeMenu)advUi->menu);
|
||||
|
||||
//Fixme: Needs a proper way to control it, but for now, this is the only way to allow Armok Vision to keep going without the user needing to switch to DF.
|
||||
if (advUi->menu == ui_advmode_menu::FallAction)
|
||||
{
|
||||
getCurViewscreen()->feed_key(interface_key::OPTION1);
|
||||
}
|
||||
|
||||
switch (advUi->menu)
|
||||
{
|
||||
case ui_advmode_menu::MoveCarefully:
|
||||
for (size_t i = 0; i < advUi->movements.size(); i++)
|
||||
{
|
||||
auto movement = advUi->movements[i];
|
||||
auto send_movement = out->add_movements();
|
||||
SetCoord(movement->source, send_movement->mutable_source());
|
||||
SetCoord(movement->dest, send_movement->mutable_dest());
|
||||
|
||||
STRICT_VIRTUAL_CAST_VAR(climbMovement, df::adventure_movement_climbst, movement);
|
||||
if (climbMovement)
|
||||
{
|
||||
SetCoord(climbMovement->grab, send_movement->mutable_grab());
|
||||
send_movement->set_movement_type(CarefulMovementType::CLIMB);
|
||||
}
|
||||
STRICT_VIRTUAL_CAST_VAR(holdTileMovement, df::adventure_movement_hold_tilest, movement);
|
||||
if (holdTileMovement)
|
||||
{
|
||||
SetCoord(holdTileMovement->grab, send_movement->mutable_grab());
|
||||
send_movement->set_movement_type(CarefulMovementType::HOLD_TILE);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in)
|
||||
{
|
||||
if (!(df::global::ui_advmode->menu == ui_advmode_menu::MoveCarefully))
|
||||
return CR_OK;
|
||||
int choice = in->value();
|
||||
int page = choice / 5;
|
||||
int select = choice % 5;
|
||||
for (int i = 0; i < page; i++)
|
||||
{
|
||||
keyQueue.push(interface_key::SECONDSCROLL_PAGEDOWN);
|
||||
}
|
||||
keyQueue.push((interface_key::interface_key)(interface_key::OPTION1 + select));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMoveParams *in)
|
||||
{
|
||||
if (!df::global::ui_advmode->menu == ui_advmode_menu::Default)
|
||||
return CR_OK;
|
||||
|
||||
auto type = in->type();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AdventureControl::SET_CLIMB:
|
||||
getCurViewscreen()->feed_key(interface_key::A_HOLD);
|
||||
break;
|
||||
case AdventureControl::SET_STAND:
|
||||
getCurViewscreen()->feed_key(interface_key::A_STANCE);
|
||||
break;
|
||||
case AdventureControl::SET_CANCEL:
|
||||
getCurViewscreen()->feed_key(interface_key::LEAVESCREEN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef ADVENTURE_CONTROL_H
|
||||
#define ADVENTURE_CONTROL_H
|
||||
|
||||
#include "RemoteClient.h"
|
||||
#include "AdventureControl.pb.h"
|
||||
|
||||
DFHack::command_result MoveCommand(DFHack::color_ostream &stream, const AdventureControl::MoveCommandParams *in);
|
||||
DFHack::command_result JumpCommand(DFHack::color_ostream &stream, const AdventureControl::MoveCommandParams *in);
|
||||
DFHack::command_result MenuQuery(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, AdventureControl::MenuContents *out);
|
||||
DFHack::command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in);
|
||||
DFHack::command_result MiscMoveCommand(DFHack::color_ostream &stream, const AdventureControl::MiscMoveParams *in);
|
||||
|
||||
void KeyUpdate();
|
||||
|
||||
|
||||
#endif // !ADVENTURE_CONTROL_H
|
@ -1,2 +1,2 @@
|
||||
#pragma once
|
||||
#define DF_VERSION_INT 43005
|
||||
#define DF_VERSION_INT 44002
|
||||
|
@ -0,0 +1,551 @@
|
||||
#include "item_reader.h"
|
||||
#include "Core.h"
|
||||
#include "VersionInfo.h"
|
||||
|
||||
#include "df/art_image.h"
|
||||
#include "df/art_image_chunk.h"
|
||||
#include "df/art_image_element.h"
|
||||
#include "df/art_image_element_creaturest.h"
|
||||
#include "df/art_image_element_itemst.h"
|
||||
#include "df/art_image_element_plantst.h"
|
||||
#include "df/art_image_element_shapest.h"
|
||||
#include "df/art_image_element_treest.h"
|
||||
#include "df/art_image_element_type.h"
|
||||
#include "df/art_image_property.h"
|
||||
#include "df/art_image_property_intransitive_verbst.h"
|
||||
#include "df/art_image_property_transitive_verbst.h"
|
||||
#include "df/art_image_ref.h"
|
||||
#include "df/descriptor_shape.h"
|
||||
#include "df/item_type.h"
|
||||
#include "df/item_constructed.h"
|
||||
#include "df/item_gemst.h"
|
||||
#include "df/item_smallgemst.h"
|
||||
#include "df/item_statuest.h"
|
||||
#include "df/item_threadst.h"
|
||||
#include "df/item_toolst.h"
|
||||
#include "df/itemimprovement.h"
|
||||
#include "df/itemimprovement_art_imagest.h"
|
||||
#include "df/itemimprovement_bandsst.h"
|
||||
#include "df/itemimprovement_coveredst.h"
|
||||
#include "df/itemimprovement_illustrationst.h"
|
||||
#include "df/itemimprovement_itemspecificst.h"
|
||||
#include "df/itemimprovement_sewn_imagest.h"
|
||||
#include "df/itemimprovement_specific_type.h"
|
||||
#include "df/itemimprovement_threadst.h"
|
||||
#include "df/itemdef.h"
|
||||
#include "df/map_block.h"
|
||||
#include "df/vehicle.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include "modules/Items.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Materials.h"
|
||||
#include "MiscUtils.h"
|
||||
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using namespace RemoteFortressReader;
|
||||
using namespace std;
|
||||
using namespace df::global;
|
||||
|
||||
|
||||
void CopyImage(const df::art_image * image, ArtImage * netImage)
|
||||
{
|
||||
auto id = netImage->mutable_id();
|
||||
id->set_mat_type(image->id);
|
||||
id->set_mat_index(image->subid);
|
||||
for (int i = 0; i < image->elements.size(); i++)
|
||||
{
|
||||
auto element = image->elements[i];
|
||||
auto netElement = netImage->add_elements();
|
||||
auto elementType = element->getType();
|
||||
netElement->set_type((ArtImageElementType)elementType);
|
||||
|
||||
netElement->set_count(element->count);
|
||||
|
||||
switch (elementType)
|
||||
{
|
||||
case df::enums::art_image_element_type::CREATURE:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(creature, df::art_image_element_creaturest, element);
|
||||
auto cret = netElement->mutable_creature_item();
|
||||
cret->set_mat_type(creature->race);
|
||||
cret->set_mat_index(creature->caste);
|
||||
break;
|
||||
}
|
||||
case df::enums::art_image_element_type::PLANT:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(plant, df::art_image_element_plantst, element);
|
||||
netElement->set_id(plant->plant_id);
|
||||
break;
|
||||
}
|
||||
case df::enums::art_image_element_type::TREE:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(tree, df::art_image_element_treest, element);
|
||||
netElement->set_id(tree->plant_id);
|
||||
break;
|
||||
}
|
||||
case df::enums::art_image_element_type::SHAPE:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(shape, df::art_image_element_shapest, element);
|
||||
netElement->set_id(shape->shape_id);
|
||||
break;
|
||||
}
|
||||
case df::enums::art_image_element_type::ITEM:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(item, df::art_image_element_itemst, element);
|
||||
auto it = netElement->mutable_creature_item();
|
||||
it->set_mat_type(item->item_type);
|
||||
it->set_mat_index(item->item_subtype);
|
||||
netElement->set_id(item->item_id);
|
||||
auto mat = netElement->mutable_material();
|
||||
mat->set_mat_type(item->mat_type);
|
||||
mat->set_mat_index(item->mat_index);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < image->properties.size(); i++)
|
||||
{
|
||||
auto dfProperty = image->properties[i];
|
||||
auto netProperty = netImage->add_properties();
|
||||
auto propertyType = dfProperty->getType();
|
||||
|
||||
netProperty->set_type((ArtImagePropertyType)propertyType);
|
||||
|
||||
switch (propertyType)
|
||||
{
|
||||
case df::enums::art_image_property_type::transitive_verb:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(transitive, df::art_image_property_transitive_verbst, dfProperty);
|
||||
netProperty->set_subject(transitive->subject);
|
||||
netProperty->set_object(transitive->object);
|
||||
netProperty->set_verb((ArtImageVerb)transitive->verb);
|
||||
break;
|
||||
}
|
||||
case df::enums::art_image_property_type::intransitive_verb:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(intransitive, df::art_image_property_intransitive_verbst, dfProperty);
|
||||
netProperty->set_subject(intransitive->subject);
|
||||
netProperty->set_verb((ArtImageVerb)intransitive->verb);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyItem(RemoteFortressReader::Item * NetItem, df::item * DfItem)
|
||||
{
|
||||
NetItem->set_id(DfItem->id);
|
||||
NetItem->set_flags1(DfItem->flags.whole);
|
||||
NetItem->set_flags2(DfItem->flags2.whole);
|
||||
auto pos = NetItem->mutable_pos();
|
||||
pos->set_x(DfItem->pos.x);
|
||||
pos->set_y(DfItem->pos.y);
|
||||
pos->set_z(DfItem->pos.z);
|
||||
auto mat = NetItem->mutable_material();
|
||||
mat->set_mat_index(DfItem->getMaterialIndex());
|
||||
mat->set_mat_type(DfItem->getMaterial());
|
||||
auto type = NetItem->mutable_type();
|
||||
type->set_mat_type(DfItem->getType());
|
||||
type->set_mat_index(DfItem->getSubtype());
|
||||
|
||||
bool isProjectile = false;
|
||||
|
||||
item_type::item_type itemType = DfItem->getType();
|
||||
|
||||
switch (itemType)
|
||||
{
|
||||
case df::enums::item_type::NONE:
|
||||
break;
|
||||
case df::enums::item_type::BAR:
|
||||
break;
|
||||
case df::enums::item_type::SMALLGEM:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(smallgem_item, df::item_smallgemst, DfItem);
|
||||
type->set_mat_index(smallgem_item->shape);
|
||||
break;
|
||||
}
|
||||
case df::enums::item_type::BLOCKS:
|
||||
break;
|
||||
case df::enums::item_type::ROUGH:
|
||||
break;
|
||||
case df::enums::item_type::BOULDER:
|
||||
break;
|
||||
case df::enums::item_type::WOOD:
|
||||
break;
|
||||
case df::enums::item_type::DOOR:
|
||||
break;
|
||||
case df::enums::item_type::FLOODGATE:
|
||||
break;
|
||||
case df::enums::item_type::BED:
|
||||
break;
|
||||
case df::enums::item_type::CHAIR:
|
||||
break;
|
||||
case df::enums::item_type::CHAIN:
|
||||
break;
|
||||
case df::enums::item_type::FLASK:
|
||||
break;
|
||||
case df::enums::item_type::GOBLET:
|
||||
break;
|
||||
case df::enums::item_type::INSTRUMENT:
|
||||
break;
|
||||
case df::enums::item_type::TOY:
|
||||
break;
|
||||
case df::enums::item_type::WINDOW:
|
||||
break;
|
||||
case df::enums::item_type::CAGE:
|
||||
break;
|
||||
case df::enums::item_type::BARREL:
|
||||
break;
|
||||
case df::enums::item_type::BUCKET:
|
||||
break;
|
||||
case df::enums::item_type::ANIMALTRAP:
|
||||
break;
|
||||
case df::enums::item_type::TABLE:
|
||||
break;
|
||||
case df::enums::item_type::COFFIN:
|
||||
break;
|
||||
case df::enums::item_type::STATUE:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(statue, df::item_statuest, DfItem);
|
||||
|
||||
df::art_image_chunk * chunk = NULL;
|
||||
GET_ART_IMAGE_CHUNK GetArtImageChunk = reinterpret_cast<GET_ART_IMAGE_CHUNK>(Core::getInstance().vinfo->getAddress("get_art_image_chunk"));
|
||||
if (GetArtImageChunk)
|
||||
{
|
||||
chunk = GetArtImageChunk(&(world->art_image_chunks), statue->image.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < world->art_image_chunks.size(); i++)
|
||||
{
|
||||
if (world->art_image_chunks[i]->id == statue->image.id)
|
||||
chunk = world->art_image_chunks[i];
|
||||
}
|
||||
}
|
||||
if (chunk)
|
||||
{
|
||||
CopyImage(chunk->images[statue->image.subid], NetItem->mutable_image());
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case df::enums::item_type::CORPSE:
|
||||
break;
|
||||
case df::enums::item_type::WEAPON:
|
||||
break;
|
||||
case df::enums::item_type::ARMOR:
|
||||
break;
|
||||
case df::enums::item_type::SHOES:
|
||||
break;
|
||||
case df::enums::item_type::SHIELD:
|
||||
break;
|
||||
case df::enums::item_type::HELM:
|
||||
break;
|
||||
case df::enums::item_type::GLOVES:
|
||||
break;
|
||||
case df::enums::item_type::BOX:
|
||||
type->set_mat_index(DfItem->isBag());
|
||||
break;
|
||||
case df::enums::item_type::BIN:
|
||||
break;
|
||||
case df::enums::item_type::ARMORSTAND:
|
||||
break;
|
||||
case df::enums::item_type::WEAPONRACK:
|
||||
break;
|
||||
case df::enums::item_type::CABINET:
|
||||
break;
|
||||
case df::enums::item_type::FIGURINE:
|
||||
break;
|
||||
case df::enums::item_type::AMULET:
|
||||
break;
|
||||
case df::enums::item_type::SCEPTER:
|
||||
break;
|
||||
case df::enums::item_type::AMMO:
|
||||
break;
|
||||
case df::enums::item_type::CROWN:
|
||||
break;
|
||||
case df::enums::item_type::RING:
|
||||
break;
|
||||
case df::enums::item_type::EARRING:
|
||||
break;
|
||||
case df::enums::item_type::BRACELET:
|
||||
break;
|
||||
case df::enums::item_type::GEM:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(gem_item, df::item_gemst, DfItem);
|
||||
type->set_mat_index(gem_item->shape);
|
||||
break;
|
||||
}
|
||||
case df::enums::item_type::ANVIL:
|
||||
break;
|
||||
case df::enums::item_type::CORPSEPIECE:
|
||||
break;
|
||||
case df::enums::item_type::REMAINS:
|
||||
break;
|
||||
case df::enums::item_type::MEAT:
|
||||
break;
|
||||
case df::enums::item_type::FISH:
|
||||
break;
|
||||
case df::enums::item_type::FISH_RAW:
|
||||
break;
|
||||
case df::enums::item_type::VERMIN:
|
||||
break;
|
||||
case df::enums::item_type::PET:
|
||||
break;
|
||||
case df::enums::item_type::SEEDS:
|
||||
break;
|
||||
case df::enums::item_type::PLANT:
|
||||
break;
|
||||
case df::enums::item_type::SKIN_TANNED:
|
||||
break;
|
||||
case df::enums::item_type::PLANT_GROWTH:
|
||||
break;
|
||||
case df::enums::item_type::THREAD:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(thread, df::item_threadst, DfItem);
|
||||
if (thread && thread->dye_mat_type >= 0)
|
||||
{
|
||||
DFHack::MaterialInfo info;
|
||||
if (info.decode(thread->dye_mat_type, thread->dye_mat_index))
|
||||
ConvertDFColorDescriptor(info.material->powder_dye, NetItem->mutable_dye());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case df::enums::item_type::CLOTH:
|
||||
break;
|
||||
case df::enums::item_type::TOTEM:
|
||||
break;
|
||||
case df::enums::item_type::PANTS:
|
||||
break;
|
||||
case df::enums::item_type::BACKPACK:
|
||||
break;
|
||||
case df::enums::item_type::QUIVER:
|
||||
break;
|
||||
case df::enums::item_type::CATAPULTPARTS:
|
||||
break;
|
||||
case df::enums::item_type::BALLISTAPARTS:
|
||||
break;
|
||||
case df::enums::item_type::SIEGEAMMO:
|
||||
break;
|
||||
case df::enums::item_type::BALLISTAARROWHEAD:
|
||||
break;
|
||||
case df::enums::item_type::TRAPPARTS:
|
||||
break;
|
||||
case df::enums::item_type::TRAPCOMP:
|
||||
break;
|
||||
case df::enums::item_type::DRINK:
|
||||
break;
|
||||
case df::enums::item_type::POWDER_MISC:
|
||||
break;
|
||||
case df::enums::item_type::CHEESE:
|
||||
break;
|
||||
case df::enums::item_type::FOOD:
|
||||
break;
|
||||
case df::enums::item_type::LIQUID_MISC:
|
||||
break;
|
||||
case df::enums::item_type::COIN:
|
||||
break;
|
||||
case df::enums::item_type::GLOB:
|
||||
break;
|
||||
case df::enums::item_type::ROCK:
|
||||
break;
|
||||
case df::enums::item_type::PIPE_SECTION:
|
||||
break;
|
||||
case df::enums::item_type::HATCH_COVER:
|
||||
break;
|
||||
case df::enums::item_type::GRATE:
|
||||
break;
|
||||
case df::enums::item_type::QUERN:
|
||||
break;
|
||||
case df::enums::item_type::MILLSTONE:
|
||||
break;
|
||||
case df::enums::item_type::SPLINT:
|
||||
break;
|
||||
case df::enums::item_type::CRUTCH:
|
||||
break;
|
||||
case df::enums::item_type::TRACTION_BENCH:
|
||||
break;
|
||||
case df::enums::item_type::ORTHOPEDIC_CAST:
|
||||
break;
|
||||
case df::enums::item_type::TOOL:
|
||||
if (!isProjectile)
|
||||
{
|
||||
VIRTUAL_CAST_VAR(tool, df::item_toolst, DfItem);
|
||||
if (tool)
|
||||
{
|
||||
auto vehicle = binsearch_in_vector(world->vehicles.active, tool->vehicle_id);
|
||||
if (vehicle)
|
||||
{
|
||||
NetItem->set_subpos_x(vehicle->offset_x / 100000.0);
|
||||
NetItem->set_subpos_y(vehicle->offset_y / 100000.0);
|
||||
NetItem->set_subpos_z(vehicle->offset_z / 140000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case df::enums::item_type::SLAB:
|
||||
break;
|
||||
case df::enums::item_type::EGG:
|
||||
break;
|
||||
case df::enums::item_type::BOOK:
|
||||
break;
|
||||
case df::enums::item_type::SHEET:
|
||||
break;
|
||||
case df::enums::item_type::BRANCH:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
VIRTUAL_CAST_VAR(actual_item, df::item_actual, DfItem);
|
||||
if (actual_item)
|
||||
{
|
||||
NetItem->set_stack_size(actual_item->stack_size);
|
||||
}
|
||||
|
||||
VIRTUAL_CAST_VAR(constructed_item, df::item_constructed, DfItem);
|
||||
if (constructed_item)
|
||||
{
|
||||
for (int i = 0; i < constructed_item->improvements.size(); i++)
|
||||
{
|
||||
auto improvement = constructed_item->improvements[i];
|
||||
|
||||
if (!improvement)
|
||||
continue;
|
||||
|
||||
improvement_type::improvement_type impType = improvement->getType();
|
||||
|
||||
auto netImp = NetItem->add_improvements();
|
||||
|
||||
netImp->set_type((ImprovementType)impType);
|
||||
|
||||
auto mat = netImp->mutable_material();
|
||||
mat->set_mat_type(improvement->mat_type);
|
||||
mat->set_mat_index(improvement->mat_index);
|
||||
|
||||
switch (impType)
|
||||
{
|
||||
case df::enums::improvement_type::ART_IMAGE:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(artImage, df::itemimprovement_art_imagest, improvement);
|
||||
CopyImage(artImage->getImage(DfItem), netImp->mutable_image());
|
||||
break;
|
||||
}
|
||||
case df::enums::improvement_type::COVERED:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(covered, df::itemimprovement_coveredst, improvement);
|
||||
netImp->set_shape(covered->shape);
|
||||
break;
|
||||
}
|
||||
case df::enums::improvement_type::RINGS_HANGING:
|
||||
break;
|
||||
case df::enums::improvement_type::BANDS:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(bands, df::itemimprovement_bandsst, improvement);
|
||||
netImp->set_shape(bands->shape);
|
||||
break;
|
||||
}
|
||||
case df::enums::improvement_type::ITEMSPECIFIC:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(specific, df::itemimprovement_itemspecificst, improvement);
|
||||
netImp->set_specific_type(specific->type);
|
||||
break;
|
||||
}
|
||||
case df::enums::improvement_type::THREAD:
|
||||
{
|
||||
VIRTUAL_CAST_VAR(improvement_thread, df::itemimprovement_threadst, improvement);
|
||||
if (improvement_thread->dye.mat_type >= 0)
|
||||
{
|
||||
DFHack::MaterialInfo info;
|
||||
if (!info.decode(improvement_thread->dye.mat_type, improvement_thread->dye.mat_index))
|
||||
continue;
|
||||
ConvertDFColorDescriptor(info.material->powder_dye, NetItem->mutable_dye());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case df::enums::improvement_type::CLOTH:
|
||||
break;
|
||||
case df::enums::improvement_type::SEWN_IMAGE:
|
||||
break;
|
||||
case df::enums::improvement_type::PAGES:
|
||||
break;
|
||||
case df::enums::improvement_type::ILLUSTRATION:
|
||||
break;
|
||||
case df::enums::improvement_type::INSTRUMENT_PIECE:
|
||||
break;
|
||||
case df::enums::improvement_type::WRITING:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NetItem->set_volume(DfItem->getVolume());
|
||||
}
|
||||
|
||||
DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::EmptyMessage *in, RemoteFortressReader::MaterialList *out)
|
||||
{
|
||||
if (!Core::getInstance().isWorldLoaded()) {
|
||||
//out->set_available(false);
|
||||
return CR_OK;
|
||||
}
|
||||
FOR_ENUM_ITEMS(item_type, it)
|
||||
{
|
||||
MaterialDefinition *mat_def = out->add_material_list();
|
||||
mat_def->mutable_mat_pair()->set_mat_type((int)it);
|
||||
mat_def->mutable_mat_pair()->set_mat_index(-1);
|
||||
mat_def->set_id(ENUM_KEY_STR(item_type, it));
|
||||
switch (it)
|
||||
{
|
||||
case df::enums::item_type::GEM:
|
||||
case df::enums::item_type::SMALLGEM:
|
||||
{
|
||||
for (int i = 0; i < world->raws.language.shapes.size(); i++)
|
||||
{
|
||||
auto shape = world->raws.language.shapes[i];
|
||||
if (shape->gems_use.whole == 0)
|
||||
continue;
|
||||
mat_def = out->add_material_list();
|
||||
mat_def->mutable_mat_pair()->set_mat_type((int)it);
|
||||
mat_def->mutable_mat_pair()->set_mat_index(i);
|
||||
mat_def->set_id(ENUM_KEY_STR(item_type, it) + "/" + shape->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case df::enums::item_type::BOX:
|
||||
{
|
||||
mat_def = out->add_material_list();
|
||||
mat_def->mutable_mat_pair()->set_mat_type((int)it);
|
||||
mat_def->mutable_mat_pair()->set_mat_index(0);
|
||||
mat_def->set_id("BOX_CHEST");
|
||||
mat_def = out->add_material_list();
|
||||
mat_def->mutable_mat_pair()->set_mat_type((int)it);
|
||||
mat_def->mutable_mat_pair()->set_mat_index(1);
|
||||
mat_def->set_id("BOX_BAG");
|
||||
break;
|
||||
}
|
||||
}
|
||||
int subtypes = Items::getSubtypeCount(it);
|
||||
if (subtypes >= 0)
|
||||
{
|
||||
for (int i = 0; i < subtypes; i++)
|
||||
{
|
||||
mat_def = out->add_material_list();
|
||||
mat_def->mutable_mat_pair()->set_mat_type((int)it);
|
||||
mat_def->mutable_mat_pair()->set_mat_index(i);
|
||||
df::itemdef * item = Items::getSubtypeDef(it, i);
|
||||
mat_def->set_id(ENUM_KEY_STR(item_type, it) + "/" + item->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#ifndef ITEM_READER_H
|
||||
#define ITEM_READER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "RemoteClient.h"
|
||||
#include "RemoteFortressReader.pb.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
namespace df
|
||||
{
|
||||
struct item;
|
||||
struct map_block;
|
||||
struct art_image;
|
||||
struct art_image_chunk;
|
||||
struct world;
|
||||
}
|
||||
|
||||
namespace MapExtras
|
||||
{
|
||||
class MapCache;
|
||||
}
|
||||
|
||||
DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::EmptyMessage *in, RemoteFortressReader::MaterialList *out);
|
||||
void CopyItem(RemoteFortressReader::Item * NetItem, df::item * DfItem);
|
||||
void ConvertDFColorDescriptor(int16_t index, RemoteFortressReader::ColorDefinition * out);
|
||||
|
||||
typedef df::art_image_chunk * (*GET_ART_IMAGE_CHUNK)(std::vector<df::art_image_chunk* > *, int);
|
||||
|
||||
void CopyImage(const df::art_image * image, RemoteFortressReader::ArtImage * netImage);
|
||||
|
||||
#endif // !ITEM_READER_H
|
@ -1,68 +0,0 @@
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::ui_sidebar_menus;
|
||||
using df::global::ui_workshop_in_add;
|
||||
|
||||
static df::interface_key kitchen_bindings[] = {
|
||||
df::interface_key::HOTKEY_KITCHEN_COOK_2,
|
||||
df::interface_key::HOTKEY_KITCHEN_COOK_3,
|
||||
df::interface_key::HOTKEY_KITCHEN_COOK_4,
|
||||
// DF uses CUSTOM_R for this reaction in the raws, so this key is recognized
|
||||
// by this tweak but not displayed
|
||||
df::interface_key::HOTKEY_KITCHEN_RENDER_FAT
|
||||
};
|
||||
|
||||
struct kitchen_keys_hook : df::viewscreen_dwarfmodest {
|
||||
typedef df::viewscreen_dwarfmodest interpose_base;
|
||||
|
||||
void draw_binding (int row, df::interface_key key)
|
||||
{
|
||||
std::string label = Screen::getKeyDisplay(key);
|
||||
int x = Gui::getDwarfmodeViewDims().menu_x2 - 2 - label.size();
|
||||
int y = row + 4;
|
||||
OutputString(COLOR_GREY, x, y, "(");
|
||||
OutputString(COLOR_LIGHTRED, x, y, label);
|
||||
OutputString(COLOR_GREY, x, y, ")");
|
||||
}
|
||||
|
||||
bool kitchen_in_add()
|
||||
{
|
||||
if (!*ui_workshop_in_add)
|
||||
return false;
|
||||
df::building_workshopst *ws = virtual_cast<df::building_workshopst>(world->selected_building);
|
||||
if (!ws)
|
||||
return false;
|
||||
if (ws->type != workshop_type::Kitchen)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
|
||||
{
|
||||
if (kitchen_in_add())
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (input->count(kitchen_bindings[i]))
|
||||
{
|
||||
ui_sidebar_menus->workshop_job.cursor = i;
|
||||
input->clear();
|
||||
input->insert(df::interface_key::SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
if (kitchen_in_add())
|
||||
for (int i = 0; i < 3; i++)
|
||||
draw_binding(i, kitchen_bindings[i]);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(kitchen_keys_hook, feed);
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(kitchen_keys_hook, render);
|
@ -0,0 +1,137 @@
|
||||
#include "df/global_objects.h"
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Screen.h"
|
||||
#include "df/enabler.h"
|
||||
#include "df/viewscreen_dwarfmodest.h"
|
||||
#include "df/viewscreen_titlest.h"
|
||||
|
||||
struct dwarfmode_pausing_fps_counter_hook : df::viewscreen_dwarfmodest {
|
||||
typedef df::viewscreen_dwarfmodest interpose_base;
|
||||
|
||||
static const uint32_t history_length = 3;
|
||||
|
||||
// whether init.txt have [FPS:YES]
|
||||
static bool init_have_fps_yes()
|
||||
{
|
||||
static bool first = true;
|
||||
static bool init_have_fps_yes;
|
||||
|
||||
if (first && df::global::gps)
|
||||
{
|
||||
// if first time called, then display_frames is set iff init.txt have [FPS:YES]
|
||||
first = false;
|
||||
init_have_fps_yes = (df::global::gps->display_frames == 1);
|
||||
}
|
||||
return init_have_fps_yes;
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
|
||||
if (!df::global::pause_state || !df::global::enabler || !df::global::world
|
||||
|| !df::global::gps || !df::global::pause_state)
|
||||
return;
|
||||
|
||||
// if init.txt does not have [FPS:YES] then dont show this FPS counter
|
||||
if (!dwarfmode_pausing_fps_counter_hook::init_have_fps_yes())
|
||||
return;
|
||||
|
||||
static bool prev_paused = true;
|
||||
static uint32_t prev_clock = 0;
|
||||
static int32_t prev_frames = 0;
|
||||
static uint32_t elapsed_clock = 0;
|
||||
static uint32_t elapsed_frames = 0;
|
||||
static double history[history_length];
|
||||
|
||||
if (prev_clock == 0)
|
||||
{
|
||||
// init
|
||||
for (int i = 0; i < history_length; i++)
|
||||
history[i] = 0.0;
|
||||
}
|
||||
|
||||
// disable default FPS counter because it is rendered on top of this FPS counter.
|
||||
if (df::global::gps->display_frames == 1)
|
||||
df::global::gps->display_frames = 0;
|
||||
|
||||
if (*df::global::pause_state)
|
||||
prev_paused = true;
|
||||
else
|
||||
{
|
||||
uint32_t clock = df::global::enabler->clock;
|
||||
int32_t frames = df::global::world->frame_counter;
|
||||
|
||||
if (!prev_paused && prev_clock != 0
|
||||
&& clock >= prev_clock && frames >= prev_frames)
|
||||
{
|
||||
// if we were previously paused, then dont add clock/frames,
|
||||
// but wait for the next time render is called.
|
||||
elapsed_clock += clock - prev_clock;
|
||||
elapsed_frames += frames - prev_frames;
|
||||
}
|
||||
|
||||
prev_paused = false;
|
||||
prev_clock = clock;
|
||||
prev_frames = frames;
|
||||
|
||||
// add FPS to history every second or after at least one frame.
|
||||
if (elapsed_clock >= 1000 && elapsed_frames >= 1)
|
||||
{
|
||||
double fps = elapsed_frames / (elapsed_clock / 1000.0);
|
||||
for (int i = history_length - 1; i >= 1; i--)
|
||||
history[i] = history[i - 1];
|
||||
history[0] = fps;
|
||||
|
||||
elapsed_clock = 0;
|
||||
elapsed_frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// average fps over a few seconds to stabilize the counter.
|
||||
double fps_sum = 0.0;
|
||||
int fps_count = 0;
|
||||
for (int i = 0; i < history_length; i++)
|
||||
{
|
||||
if (history[i] > 0.0)
|
||||
{
|
||||
fps_sum += history[i];
|
||||
fps_count++;
|
||||
}
|
||||
}
|
||||
|
||||
double fps = fps_count == 0 ? 1.0 : fps_sum / fps_count;
|
||||
double gfps = df::global::enabler->calculated_gfps;
|
||||
|
||||
std::stringstream fps_counter;
|
||||
fps_counter << "FPS:"
|
||||
<< setw(4) << fixed << setprecision(fps >= 1.0 ? 0 : 2) << fps
|
||||
<< " (" << gfps << ")";
|
||||
|
||||
// show this FPS counter same as the default counter.
|
||||
int x = 10;
|
||||
int y = 0;
|
||||
OutputString(COLOR_WHITE, x, y, fps_counter.str(),
|
||||
false, 0, COLOR_CYAN, false);
|
||||
}
|
||||
};
|
||||
|
||||
struct title_pausing_fps_counter_hook : df::viewscreen_titlest {
|
||||
typedef df::viewscreen_titlest interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
|
||||
// if init.txt have FPS:YES then enable default FPS counter when exiting dwarf mode.
|
||||
// So it is enabled if starting adventure mode without exiting dwarf fortress.
|
||||
if (dwarfmode_pausing_fps_counter_hook::init_have_fps_yes()
|
||||
&& df::global::gps && df::global::gps->display_frames == 0)
|
||||
df::global::gps->display_frames = 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_pausing_fps_counter_hook, render);
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(title_pausing_fps_counter_hook, render);
|
@ -1 +1 @@
|
||||
Subproject commit 3baa24fec93461218b5b658de94884ebff0a0b23
|
||||
Subproject commit 8d079a59122d9ba72ce9c0f7687402a343d09bc7
|
@ -0,0 +1,10 @@
|
||||
function set_test_stage(stage)
|
||||
local f = io.open('test_stage.txt', 'w')
|
||||
f:write(stage)
|
||||
f:close()
|
||||
end
|
||||
|
||||
print('running tests')
|
||||
|
||||
set_test_stage('done')
|
||||
dfhack.run_command('die')
|
@ -0,0 +1,2 @@
|
||||
:lua dfhack.internal.addScriptPath(os.getenv('TRAVIS_BUILD_DIR'))
|
||||
test/main
|
@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
|
||||
tardest="df.tar.bz2"
|
||||
|
||||
which md5sum && alias md5=md5sum
|
||||
selfmd5=$(openssl md5 < "$0")
|
||||
echo $selfmd5
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
echo "DF_VERSION: $DF_VERSION"
|
||||
echo "DF_FOLDER: $DF_FOLDER"
|
||||
mkdir -p "$DF_FOLDER"
|
||||
cd "$DF_FOLDER"
|
||||
|
||||
if [ -f receipt ]; then
|
||||
if [ "$selfmd5" != "$(cat receipt)" ]; then
|
||||
echo "download-df.sh changed; removing DF"
|
||||
else
|
||||
echo "Already downloaded $DF_VERSION"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rif "$tardest" df_linux
|
||||
|
||||
minor=$(echo "$DF_VERSION" | cut -d. -f2)
|
||||
patch=$(echo "$DF_VERSION" | cut -d. -f3)
|
||||
url="http://www.bay12games.com/dwarves/df_${minor}_${patch}_linux.tar.bz2"
|
||||
|
||||
echo Downloading
|
||||
wget "$url" -O "$tardest"
|
||||
echo Extracting
|
||||
tar xf "$tardest" --strip-components=1
|
||||
echo Changing settings
|
||||
echo '' >> "$DF_FOLDER/data/init/init.txt"
|
||||
echo '[PRINT_MODE:TEXT]' >> "$DF_FOLDER/data/init/init.txt"
|
||||
echo '[SOUND:NO]' >> "$DF_FOLDER/data/init/init.txt"
|
||||
echo Done
|
||||
|
||||
echo "$selfmd5" > receipt
|
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
grep DF_VERSION CMakeLists.txt | perl -ne 'print "$&\n" if /[\d\.]+/'
|
@ -0,0 +1,30 @@
|
||||
import os, subprocess, sys
|
||||
|
||||
MAX_TRIES = 5
|
||||
|
||||
dfhack = 'Dwarf Fortress.exe' if sys.platform == 'win32' else './dfhack'
|
||||
test_stage = 'test_stage.txt'
|
||||
|
||||
def get_test_stage():
|
||||
if os.path.isfile(test_stage):
|
||||
return open(test_stage).read().strip()
|
||||
return '0'
|
||||
|
||||
os.chdir(sys.argv[1])
|
||||
if os.path.exists(test_stage):
|
||||
os.remove(test_stage)
|
||||
|
||||
tries = 0
|
||||
while True:
|
||||
tries += 1
|
||||
stage = get_test_stage()
|
||||
print('Run #%i: stage=%s' % (tries, get_test_stage()))
|
||||
if stage == 'done':
|
||||
print('Done!')
|
||||
os.remove(test_stage)
|
||||
sys.exit(0)
|
||||
if tries > MAX_TRIES:
|
||||
print('Too many tries - aborting')
|
||||
sys.exit(1)
|
||||
|
||||
os.system(dfhack)
|
Loading…
Reference in New Issue