Merge branch 'develop' of https://github.com/DFHack/dfhack into develop
Conflicts: library/xmldevelop
commit
3f717af0b7
Binary file not shown.
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
@ -0,0 +1,194 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
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.
|
||||
*/
|
||||
/* Based on luafilesystem
|
||||
Copyright © 2003-2014 Kepler Project.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "Export.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef _AIX
|
||||
#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */
|
||||
#else
|
||||
#define _LARGE_FILES 1 /* AIX */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <sys/locking.h>
|
||||
#ifdef __BORLANDC__
|
||||
#include <utime.h>
|
||||
#else
|
||||
#include <sys/utime.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#define LFS_VERSION "1.6.2"
|
||||
#define LFS_LIBNAME "lfs"
|
||||
|
||||
#if LUA_VERSION_NUM < 502
|
||||
# define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l))
|
||||
#endif
|
||||
|
||||
/* Define 'strerror' for systems that do not implement it */
|
||||
#ifdef NO_STRERROR
|
||||
#define strerror(_) "System unable to describe the error"
|
||||
#endif
|
||||
|
||||
/* Define 'getcwd' for systems that do not implement it */
|
||||
#ifdef NO_GETCWD
|
||||
#define getcwd(p,s) NULL
|
||||
#define getcwd_error "Function 'getcwd' not provided by system"
|
||||
#else
|
||||
#define getcwd_error strerror(errno)
|
||||
#ifdef _WIN32
|
||||
/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
|
||||
#define LFS_MAXPATHLEN MAX_PATH
|
||||
#else
|
||||
/* For MAXPATHLEN: */
|
||||
#include <sys/param.h>
|
||||
#define LFS_MAXPATHLEN MAXPATHLEN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct dir_data {
|
||||
int closed;
|
||||
#ifdef _WIN32
|
||||
intptr_t hFile;
|
||||
char pattern[MAX_PATH+1];
|
||||
#else
|
||||
DIR *dir;
|
||||
#endif
|
||||
} dir_data;
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __BORLANDC__
|
||||
#define lfs_setmode(L,file,m) ((void)L, setmode(_fileno(file), m))
|
||||
#define STAT_STRUCT struct stati64
|
||||
#else
|
||||
#define lfs_setmode(L,file,m) ((void)L, _setmode(_fileno(file), m))
|
||||
#define STAT_STRUCT struct _stati64
|
||||
#endif
|
||||
#define STAT_FUNC _stati64
|
||||
#define LSTAT_FUNC STAT_FUNC
|
||||
#else
|
||||
#define _O_TEXT 0
|
||||
#define _O_BINARY 0
|
||||
#define lfs_setmode(L,file,m) ((void)L, (void)file, (void)m, 0)
|
||||
#define STAT_STRUCT struct stat
|
||||
#define STAT_FUNC stat
|
||||
#define LSTAT_FUNC lstat
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(mode) (mode&_S_IFDIR)
|
||||
#endif
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(mode) (mode&_S_IFREG)
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(mode) (0)
|
||||
#endif
|
||||
#ifndef S_ISSOCK
|
||||
#define S_ISSOCK(mode) (0)
|
||||
#endif
|
||||
#ifndef S_ISFIFO
|
||||
#define S_ISFIFO(mode) (0)
|
||||
#endif
|
||||
#ifndef S_ISCHR
|
||||
#define S_ISCHR(mode) (mode&_S_IFCHR)
|
||||
#endif
|
||||
#ifndef S_ISBLK
|
||||
#define S_ISBLK(mode) (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
enum _filetype {
|
||||
FILETYPE_NONE = -2,
|
||||
FILETYPE_UNKNOWN = -1,
|
||||
FILETYPE_FILE = 1,
|
||||
FILETYPE_DIRECTORY,
|
||||
FILETYPE_LINK,
|
||||
FILETYPE_SOCKET,
|
||||
FILETYPE_NAMEDPIPE,
|
||||
FILETYPE_CHAR_DEVICE,
|
||||
FILETYPE_BLOCK_DEVICE
|
||||
};
|
||||
|
||||
namespace DFHack {
|
||||
namespace Filesystem {
|
||||
DFHACK_EXPORT bool chdir (std::string path);
|
||||
DFHACK_EXPORT std::string getcwd ();
|
||||
DFHACK_EXPORT bool mkdir (std::string path);
|
||||
DFHACK_EXPORT bool rmdir (std::string path);
|
||||
DFHACK_EXPORT bool stat (std::string path, STAT_STRUCT &info);
|
||||
DFHACK_EXPORT bool exists (std::string path);
|
||||
DFHACK_EXPORT _filetype filetype (std::string path);
|
||||
DFHACK_EXPORT bool isfile (std::string path);
|
||||
DFHACK_EXPORT bool isdir (std::string path);
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
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.
|
||||
*/
|
||||
/* Based on luafilesystem
|
||||
Copyright © 2003-2014 Kepler Project.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "modules/Filesystem.h"
|
||||
|
||||
bool DFHack::Filesystem::chdir (std::string path)
|
||||
{
|
||||
return !(bool)::chdir(path.c_str());
|
||||
}
|
||||
|
||||
std::string DFHack::Filesystem::getcwd ()
|
||||
{
|
||||
char *path;
|
||||
char buf[LFS_MAXPATHLEN];
|
||||
std::string result = "";
|
||||
#ifdef _WIN32
|
||||
if ((path = ::_getcwd(buf, LFS_MAXPATHLEN)) != NULL)
|
||||
#else
|
||||
if ((path = ::getcwd(buf, LFS_MAXPATHLEN)) != NULL)
|
||||
#endif
|
||||
result = buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::mkdir (std::string path)
|
||||
{
|
||||
int fail;
|
||||
#ifdef _WIN32
|
||||
fail = ::_mkdir(path.c_str());
|
||||
#else
|
||||
fail = ::mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
|
||||
S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
return !(bool)fail;
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::rmdir (std::string path)
|
||||
{
|
||||
int fail;
|
||||
#ifdef _WIN32
|
||||
fail = ::_rmdir(path.c_str());
|
||||
#else
|
||||
fail = ::rmdir(path.c_str());
|
||||
#endif
|
||||
return !(bool)fail;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
_filetype mode2type (unsigned short mode) {
|
||||
#else
|
||||
_filetype mode2type (mode_t mode) {
|
||||
#endif
|
||||
if (S_ISREG(mode))
|
||||
return FILETYPE_FILE;
|
||||
else if (S_ISDIR(mode))
|
||||
return FILETYPE_DIRECTORY;
|
||||
else if (S_ISLNK(mode))
|
||||
return FILETYPE_LINK;
|
||||
else if (S_ISSOCK(mode))
|
||||
return FILETYPE_SOCKET;
|
||||
else if (S_ISFIFO(mode))
|
||||
return FILETYPE_NAMEDPIPE;
|
||||
else if (S_ISCHR(mode))
|
||||
return FILETYPE_CHAR_DEVICE;
|
||||
else if (S_ISBLK(mode))
|
||||
return FILETYPE_BLOCK_DEVICE;
|
||||
else
|
||||
return FILETYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::stat (std::string path, STAT_STRUCT &info)
|
||||
{
|
||||
return !(bool)(STAT_FUNC(path.c_str(), &info));
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::exists (std::string path)
|
||||
{
|
||||
STAT_STRUCT info;
|
||||
return (bool)DFHack::Filesystem::stat(path.c_str(), info);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
_filetype DFHack::Filesystem::filetype (std::string path)
|
||||
{
|
||||
STAT_STRUCT info;
|
||||
DFHack::Filesystem::stat(path, info);
|
||||
std::cout << info.st_mode << std::endl;
|
||||
return mode2type(info.st_mode);
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::isfile (std::string path)
|
||||
{
|
||||
return DFHack::Filesystem::filetype(path) == FILETYPE_FILE;
|
||||
}
|
||||
|
||||
bool DFHack::Filesystem::isdir (std::string path)
|
||||
{
|
||||
return DFHack::Filesystem::filetype(path) == FILETYPE_DIRECTORY;
|
||||
}
|
@ -0,0 +1,513 @@
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/Screen.h"
|
||||
#include "modules/Gui.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
#include "ColorText.h"
|
||||
#include "df/viewscreen_choose_start_sitest.h"
|
||||
#include "df/interface_key.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
struct EmbarkTool
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string desc;
|
||||
bool enabled;
|
||||
df::interface_key toggle_key;
|
||||
};
|
||||
|
||||
EmbarkTool embark_tools[] = {
|
||||
{"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map",
|
||||
false, df::interface_key::CUSTOM_A},
|
||||
{"nano", "Nano embark", "Allows the embark size to be decreased below 2x2",
|
||||
false, df::interface_key::CUSTOM_N},
|
||||
{"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site",
|
||||
false, df::interface_key::CUSTOM_S},
|
||||
{"sticky", "Stable position", "Maintains the selected local area while navigating the world map",
|
||||
false, df::interface_key::CUSTOM_P},
|
||||
};
|
||||
#define NUM_TOOLS int(sizeof(embark_tools) / sizeof(EmbarkTool))
|
||||
|
||||
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
void OutputString (int8_t color, int &x, int y, const std::string &text);
|
||||
|
||||
bool tool_exists (std::string tool_name);
|
||||
bool tool_enabled (std::string tool_name);
|
||||
bool tool_enable (std::string tool_name, bool enable_state);
|
||||
void tool_update (std::string tool_name);
|
||||
|
||||
class embark_tools_settings : public dfhack_viewscreen
|
||||
{
|
||||
public:
|
||||
embark_tools_settings () { };
|
||||
~embark_tools_settings () { };
|
||||
void help () { };
|
||||
std::string getFocusString () { return "embark-tools/options"; };
|
||||
void render ()
|
||||
{
|
||||
int x;
|
||||
auto dim = Screen::getWindowSize();
|
||||
int width = 50,
|
||||
height = 4 + 1 + NUM_TOOLS, // Padding + lower row
|
||||
min_x = (dim.x - width) / 2,
|
||||
max_x = (dim.x + width) / 2,
|
||||
min_y = (dim.y - height) / 2,
|
||||
max_y = min_y + height;
|
||||
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
|
||||
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
|
||||
x = min_x + 2;
|
||||
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::SELECT));
|
||||
OutputString(COLOR_WHITE, x, max_y - 2, "/");
|
||||
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
|
||||
OutputString(COLOR_WHITE, x, max_y - 2, ": Done");
|
||||
for (int i = 0, y = min_y + 2; i < NUM_TOOLS; i++, y++)
|
||||
{
|
||||
EmbarkTool t = embark_tools[i];
|
||||
x = min_x + 2;
|
||||
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t.toggle_key));
|
||||
OutputString(COLOR_WHITE, x, y, ": " + t.name + (t.enabled ? ": Enabled" : ": Disabled"));
|
||||
}
|
||||
};
|
||||
void feed (std::set<df::interface_key> * input)
|
||||
{
|
||||
if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
|
||||
{
|
||||
Screen::dismiss(this);
|
||||
return;
|
||||
}
|
||||
for (auto iter = input->begin(); iter != input->end(); iter++)
|
||||
{
|
||||
df::interface_key key = *iter;
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
if (embark_tools[i].toggle_key == key)
|
||||
{
|
||||
embark_tools[i].enabled = !embark_tools[i].enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic
|
||||
*/
|
||||
|
||||
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
|
||||
{
|
||||
bool is_top = false;
|
||||
if (screen->embark_pos_min.y == 0)
|
||||
is_top = true;
|
||||
std::set<df::interface_key> keys;
|
||||
keys.insert(df::interface_key::SETUP_LOCAL_Y_MUP);
|
||||
screen->feed(&keys);
|
||||
if (!is_top)
|
||||
{
|
||||
keys.insert(df::interface_key::SETUP_LOCAL_Y_MDOWN);
|
||||
screen->feed(&keys);
|
||||
}
|
||||
}
|
||||
|
||||
void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
|
||||
{
|
||||
/* Reproduces DF's embark resizing functionality
|
||||
* Local area resizes up and to the right, unless it's already touching the edge
|
||||
*/
|
||||
int x1 = screen->embark_pos_min.x,
|
||||
x2 = screen->embark_pos_max.x,
|
||||
y1 = screen->embark_pos_min.y,
|
||||
y2 = screen->embark_pos_max.y,
|
||||
width = x2 - x1 + dx,
|
||||
height = y2 - y1 + dy;
|
||||
if (x1 == x2 && dx == -1)
|
||||
dx = 0;
|
||||
if (y1 == y2 && dy == -1)
|
||||
dy = 0;
|
||||
|
||||
x2 += dx; // Resize right
|
||||
while (x2 > 15)
|
||||
{
|
||||
x2--;
|
||||
x1--;
|
||||
}
|
||||
x1 = std::max(0, x1);
|
||||
|
||||
y1 -= dy; // Resize up
|
||||
while (y1 < 0)
|
||||
{
|
||||
y1++;
|
||||
y2++;
|
||||
}
|
||||
y2 = std::min(15, y2);
|
||||
|
||||
screen->embark_pos_min.x = x1;
|
||||
screen->embark_pos_max.x = x2;
|
||||
screen->embark_pos_min.y = y1;
|
||||
screen->embark_pos_max.y = y2;
|
||||
|
||||
update_embark_sidebar(screen);
|
||||
}
|
||||
|
||||
std::string sand_indicator = "";
|
||||
bool sand_dirty = true; // Flag set when update is needed
|
||||
void sand_update (df::viewscreen_choose_start_sitest * screen)
|
||||
{
|
||||
CoreSuspendClaimer suspend;
|
||||
buffered_color_ostream out;
|
||||
Core::getInstance().runCommand(out, "prospect");
|
||||
auto fragments = out.fragments();
|
||||
sand_indicator = "";
|
||||
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
|
||||
{
|
||||
std::string fragment = iter->second;
|
||||
if (fragment.find("SAND_") != std::string::npos)
|
||||
{
|
||||
sand_indicator = "Sand";
|
||||
break;
|
||||
}
|
||||
}
|
||||
sand_dirty = false;
|
||||
}
|
||||
|
||||
int sticky_pos[] = {0, 0, 3, 3};
|
||||
bool sticky_moved = false;
|
||||
void sticky_save (df::viewscreen_choose_start_sitest * screen)
|
||||
{
|
||||
sticky_pos[0] = screen->embark_pos_min.x;
|
||||
sticky_pos[1] = screen->embark_pos_max.x;
|
||||
sticky_pos[2] = screen->embark_pos_min.y;
|
||||
sticky_pos[3] = screen->embark_pos_max.y;
|
||||
}
|
||||
|
||||
void sticky_apply (df::viewscreen_choose_start_sitest * screen)
|
||||
{
|
||||
screen->embark_pos_min.x = sticky_pos[0];
|
||||
screen->embark_pos_max.x = sticky_pos[1];
|
||||
screen->embark_pos_min.y = sticky_pos[2];
|
||||
screen->embark_pos_max.y = sticky_pos[3];
|
||||
update_embark_sidebar(screen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Viewscreen hooks
|
||||
*/
|
||||
|
||||
void OutputString (int8_t color, int &x, int y, const std::string &text)
|
||||
{
|
||||
Screen::paintString(Screen::Pen(' ', color, 0), x, y, text);
|
||||
x += text.length();
|
||||
}
|
||||
|
||||
struct choose_start_site_hook : df::viewscreen_choose_start_sitest
|
||||
{
|
||||
typedef df::viewscreen_choose_start_sitest interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
|
||||
{
|
||||
bool prevent_default = false;
|
||||
if (tool_enabled("anywhere"))
|
||||
{
|
||||
for (auto iter = input->begin(); iter != input->end(); iter++)
|
||||
{
|
||||
df::interface_key key = *iter;
|
||||
if (key == df::interface_key::SETUP_EMBARK)
|
||||
{
|
||||
prevent_default = true;
|
||||
this->in_embark_normal = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input->count(df::interface_key::CUSTOM_S))
|
||||
{
|
||||
Screen::show(new embark_tools_settings);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tool_enabled("nano"))
|
||||
{
|
||||
for (auto iter = input->begin(); iter != input->end(); iter++)
|
||||
{
|
||||
df::interface_key key = *iter;
|
||||
bool is_resize = true;
|
||||
int dx = 0, dy = 0;
|
||||
switch (key)
|
||||
{
|
||||
case df::interface_key::SETUP_LOCAL_Y_UP:
|
||||
dy = 1;
|
||||
break;
|
||||
case df::interface_key::SETUP_LOCAL_Y_DOWN:
|
||||
dy = -1;
|
||||
break;
|
||||
case df::interface_key::SETUP_LOCAL_X_UP:
|
||||
dx = 1;
|
||||
break;
|
||||
case df::interface_key::SETUP_LOCAL_X_DOWN:
|
||||
dx = -1;
|
||||
break;
|
||||
default:
|
||||
is_resize = false;
|
||||
}
|
||||
if (is_resize)
|
||||
{
|
||||
prevent_default = true;
|
||||
resize_embark(this, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tool_enabled("sticky"))
|
||||
{
|
||||
for (auto iter = input->begin(); iter != input->end(); iter++)
|
||||
{
|
||||
df::interface_key key = *iter;
|
||||
bool is_motion = false;
|
||||
int dx = 0, dy = 0;
|
||||
switch (key)
|
||||
{
|
||||
case df::interface_key::CURSOR_UP:
|
||||
case df::interface_key::CURSOR_DOWN:
|
||||
case df::interface_key::CURSOR_LEFT:
|
||||
case df::interface_key::CURSOR_RIGHT:
|
||||
case df::interface_key::CURSOR_UPLEFT:
|
||||
case df::interface_key::CURSOR_UPRIGHT:
|
||||
case df::interface_key::CURSOR_DOWNLEFT:
|
||||
case df::interface_key::CURSOR_DOWNRIGHT:
|
||||
case df::interface_key::CURSOR_UP_FAST:
|
||||
case df::interface_key::CURSOR_DOWN_FAST:
|
||||
case df::interface_key::CURSOR_LEFT_FAST:
|
||||
case df::interface_key::CURSOR_RIGHT_FAST:
|
||||
case df::interface_key::CURSOR_UPLEFT_FAST:
|
||||
case df::interface_key::CURSOR_UPRIGHT_FAST:
|
||||
case df::interface_key::CURSOR_DOWNLEFT_FAST:
|
||||
case df::interface_key::CURSOR_DOWNRIGHT_FAST:
|
||||
is_motion = true;
|
||||
break;
|
||||
default:
|
||||
is_motion = false;
|
||||
}
|
||||
if (is_motion && !sticky_moved)
|
||||
{
|
||||
sticky_save(this);
|
||||
sticky_moved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tool_enabled("sand"))
|
||||
{
|
||||
sand_dirty = true;
|
||||
}
|
||||
if (!prevent_default)
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
if (tool_enabled("sticky") && sticky_moved)
|
||||
{
|
||||
sticky_apply(this);
|
||||
sticky_moved = false;
|
||||
}
|
||||
|
||||
INTERPOSE_NEXT(render)();
|
||||
|
||||
auto dim = Screen::getWindowSize();
|
||||
int x = 1,
|
||||
y = dim.y - 5;
|
||||
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
|
||||
OutputString(COLOR_WHITE, x, y, ": Enabled: ");
|
||||
std::list<std::string> parts;
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
if (embark_tools[i].enabled)
|
||||
{
|
||||
parts.push_back(embark_tools[i].name);
|
||||
parts.push_back(", ");
|
||||
}
|
||||
}
|
||||
if (parts.size())
|
||||
{
|
||||
parts.pop_back(); // Remove trailing comma
|
||||
for (auto iter = parts.begin(); iter != parts.end(); iter++)
|
||||
{
|
||||
OutputString(COLOR_LIGHTMAGENTA, x, y, *iter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
|
||||
}
|
||||
|
||||
if (tool_enabled("anywhere"))
|
||||
{
|
||||
x = 20; y = dim.y - 2;
|
||||
if (this->page >= 0 && this->page <= 4)
|
||||
{
|
||||
// Only display on five map pages, not on site finder or notes
|
||||
OutputString(COLOR_WHITE, x, y, ": Embark!");
|
||||
}
|
||||
}
|
||||
if (tool_enabled("sand"))
|
||||
{
|
||||
if (sand_dirty)
|
||||
{
|
||||
sand_update(this);
|
||||
}
|
||||
x = dim.x - 28; y = 13;
|
||||
if (this->page == 0)
|
||||
{
|
||||
OutputString(COLOR_YELLOW, x, y, sand_indicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
|
||||
|
||||
/*
|
||||
* Tool management
|
||||
*/
|
||||
|
||||
bool tool_exists (std::string tool_name)
|
||||
{
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
if (embark_tools[i].id == tool_name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tool_enabled (std::string tool_name)
|
||||
{
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
if (embark_tools[i].id == tool_name)
|
||||
return embark_tools[i].enabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tool_enable (std::string tool_name, bool enable_state)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
if (embark_tools[i].id == tool_name || tool_name == "all")
|
||||
{
|
||||
embark_tools[i].enabled = enable_state;
|
||||
tool_update(tool_name);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return (bool)n;
|
||||
}
|
||||
|
||||
void tool_update (std::string tool_name)
|
||||
{
|
||||
// Called whenever a tool is enabled/disabled
|
||||
if (tool_name == "sand")
|
||||
{
|
||||
sand_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Plugin management
|
||||
*/
|
||||
|
||||
DFHACK_PLUGIN("embark-tools");
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
std::string help = "";
|
||||
help += "embark-tools (enable/disable) tool [tool...]\n"
|
||||
"Tools:\n";
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n");
|
||||
}
|
||||
commands.push_back(PluginCommand(
|
||||
"embark-tools",
|
||||
"A collection of embark tools",
|
||||
embark_tools_cmd,
|
||||
false,
|
||||
help.c_str()
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
|
||||
{
|
||||
INTERPOSE_HOOK(choose_start_site_hook, feed).remove();
|
||||
INTERPOSE_HOOK(choose_start_site_hook, render).remove();
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
|
||||
{
|
||||
if (is_enabled != enable)
|
||||
{
|
||||
if (!INTERPOSE_HOOK(choose_start_site_hook, feed).apply(enable) ||
|
||||
!INTERPOSE_HOOK(choose_start_site_hook, render).apply(enable))
|
||||
return CR_FAILURE;
|
||||
is_enabled = enable;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
if (parameters.size())
|
||||
{
|
||||
// Set by "enable"/"disable" - allows for multiple commands, e.g. "enable nano disable anywhere"
|
||||
bool enable_state = true;
|
||||
for (size_t i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
if (parameters[i] == "enable")
|
||||
{
|
||||
enable_state = true;
|
||||
plugin_enable(out, true); // Enable plugin
|
||||
}
|
||||
else if (parameters[i] == "disable")
|
||||
enable_state = false;
|
||||
else if (tool_exists(parameters[i]) || parameters[i] == "all")
|
||||
{
|
||||
tool_enable(parameters[i], enable_state);
|
||||
}
|
||||
else
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_enabled)
|
||||
{
|
||||
out << "Tool status:" << std::endl;
|
||||
for (int i = 0; i < NUM_TOOLS; i++)
|
||||
{
|
||||
EmbarkTool t = embark_tools[i];
|
||||
out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "Plugin not enabled" << std::endl;
|
||||
}
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "modules/EventManager.h"
|
||||
#include "modules/Maps.h"
|
||||
|
||||
#include "df/caste_raw.h"
|
||||
#include "df/caste_raw_flags.h"
|
||||
#include "df/creature_raw.h"
|
||||
#include "df/profession.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace std;
|
||||
|
||||
static int32_t howOften = 10000;
|
||||
static int32_t popcap = 100;
|
||||
static int32_t pregtime = 200000;
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
|
||||
command_result petcapRemover (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("petcapRemover");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"petcapRemover",
|
||||
"Removes the pet population cap by causing pregnancies.",
|
||||
petcapRemover,
|
||||
false, //allow non-interactive use
|
||||
"petcapRemover\n"
|
||||
" does pregnancies now and schedules the next check\n"
|
||||
"petcapRemover every n\n"
|
||||
" set how often in ticks the plugin checks for possible pregnancies\n"
|
||||
"petcapRemover cap n\n"
|
||||
" sets the new cap to n. if n = 0, no cap. Caps between 1 and 50 effectively don't do anything because normal DF pregnancies will continue to happen below that cap.\n"
|
||||
"petcapRemover pregtime n\n"
|
||||
" sets the pregnancy duration to n ticks. Natural pregnancies are 300000 ticks for the current race and 200000 ticks for everyone else.\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
bool impregnate(df::unit* female, df::unit* male);
|
||||
void impregnateMany() {
|
||||
map<int32_t, vector<int32_t> > males;
|
||||
map<int32_t, vector<int32_t> > females;
|
||||
map<int32_t, int32_t> popcount;
|
||||
auto units = df::global::world->units.all;
|
||||
for ( size_t a = 0; a < units.size(); a++ ) {
|
||||
df::unit* unit = units[a];
|
||||
if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor )
|
||||
continue;
|
||||
popcount[unit->race]++;
|
||||
if ( unit->relations.pregnancy_genes ) {
|
||||
//already pregnant
|
||||
//for player convenience and population stability, count the fetus toward the population cap
|
||||
popcount[unit->race]++;
|
||||
continue;
|
||||
}
|
||||
if ( unit->flags1.bits.caged )
|
||||
continue;
|
||||
int32_t race = unit->race;
|
||||
int16_t caste = unit->caste;
|
||||
df::creature_raw* creatureRaw = df::global::world->raws.creatures.all[race];
|
||||
df::caste_raw* casteRaw = creatureRaw->caste[caste];
|
||||
//must have PET or PET_EXOTIC
|
||||
if ( !(casteRaw->flags.is_set(df::enums::caste_raw_flags::PET) || casteRaw->flags.is_set(df::enums::caste_raw_flags::PET_EXOTIC) ) )
|
||||
continue;
|
||||
//check for adulthood
|
||||
if ( unit->profession == df::enums::profession::CHILD || unit->profession == df::enums::profession::BABY )
|
||||
continue;
|
||||
if ( unit->sex == 1 )
|
||||
males[unit->race].push_back(a);
|
||||
else
|
||||
females[unit->race].push_back(a);
|
||||
}
|
||||
|
||||
for ( auto i = females.begin(); i != females.end(); i++ ) {
|
||||
int32_t race = i->first;
|
||||
vector<int32_t>& femalesList = i->second;
|
||||
for ( size_t a = 0; a < femalesList.size(); a++ ) {
|
||||
if ( popcap > 0 && popcount[race] >= popcap )
|
||||
break;
|
||||
vector<int32_t> compatibles;
|
||||
df::coord pos1 = units[femalesList[a]]->pos;
|
||||
|
||||
if ( males.find(i->first) == males.end() )
|
||||
continue;
|
||||
|
||||
vector<int32_t>& malesList = males[i->first];
|
||||
for ( size_t b = 0; b < malesList.size(); b++ ) {
|
||||
df::coord pos2 = units[malesList[b]]->pos;
|
||||
if ( Maps::canWalkBetween(pos1,pos2) )
|
||||
compatibles.push_back(malesList[b]);
|
||||
}
|
||||
if ( compatibles.empty() )
|
||||
continue;
|
||||
|
||||
size_t maleIndex = (size_t)(compatibles.size()*((float)rand() / (1+(float)RAND_MAX)));
|
||||
if ( impregnate(units[femalesList[a]], units[compatibles[maleIndex]]) )
|
||||
popcount[race]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool impregnate(df::unit* female, df::unit* male) {
|
||||
if ( !female || !male )
|
||||
return false;
|
||||
if ( female->relations.pregnancy_genes )
|
||||
return false;
|
||||
|
||||
df::unit_genes* preg = new df::unit_genes;
|
||||
*preg = male->appearance.genes;
|
||||
female->relations.pregnancy_genes = preg;
|
||||
female->relations.pregnancy_timer = pregtime; //300000 for dwarves
|
||||
female->relations.pregnancy_caste = male->caste;
|
||||
return true;
|
||||
}
|
||||
|
||||
void tickHandler(color_ostream& out, void* data) {
|
||||
if ( !is_enabled )
|
||||
return;
|
||||
CoreSuspender suspend;
|
||||
impregnateMany();
|
||||
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
EventManager::EventHandler handle(tickHandler, howOften);
|
||||
EventManager::registerTick(handle, howOften, plugin_self);
|
||||
}
|
||||
|
||||
command_result petcapRemover (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
|
||||
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
||||
if ( parameters[a] == "every" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
int32_t old = howOften;
|
||||
howOften = atoi(parameters[a+1].c_str());
|
||||
if (howOften < -1) {
|
||||
howOften = old;
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
a++;
|
||||
continue;
|
||||
} else if ( parameters[a] == "cap" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
int32_t old = popcap;
|
||||
popcap = atoi(parameters[a+1].c_str());
|
||||
if ( popcap < 0 ) {
|
||||
popcap = old;
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
a++;
|
||||
continue;
|
||||
} else if ( parameters[a] == "pregtime" ) {
|
||||
if ( a+1 >= parameters.size() )
|
||||
return CR_WRONG_USAGE;
|
||||
int32_t old = pregtime;
|
||||
pregtime = atoi(parameters[a+1].c_str());
|
||||
if ( pregtime <= 0 ) {
|
||||
pregtime = old;
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
a++;
|
||||
continue;
|
||||
}
|
||||
out.print("%s, line %d: invalid argument: %s\n", __FILE__, __LINE__, parameters[a].c_str());
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
if ( howOften < 0 ) {
|
||||
is_enabled = false;
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
is_enabled = true;
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
EventManager::EventHandler handle(tickHandler, howOften);
|
||||
EventManager::registerTick(handle, howOften, plugin_self);
|
||||
out.print("petcapRemover: howOften = every %d ticks, popcap per species = %d, preg time = %d ticks.\n", howOften, popcap, pregtime);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
|
||||
{
|
||||
if (enable != is_enabled)
|
||||
{
|
||||
is_enabled = enable;
|
||||
if ( !is_enabled ) {
|
||||
EventManager::unregisterAll(plugin_self);
|
||||
}
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue