Merge remote-tracking branch 'dfhack/develop' into create-unit
commit
9107d2be52
@ -0,0 +1,2 @@
|
||||
PROJECT(jsoncpp)
|
||||
ADD_LIBRARY(jsoncpp STATIC jsoncpp.cpp)
|
@ -0,0 +1,31 @@
|
||||
#include "jsoncpp.h"
|
||||
#pragma once
|
||||
|
||||
namespace JsonEx {
|
||||
|
||||
template <typename T> bool is (const Json::Value &val) { return false; }
|
||||
template <typename T> T as (const Json::Value &val);
|
||||
template <typename T> T get (const Json::Value &val, const std::string &key, const T &default_) { return default_; }
|
||||
|
||||
#define define_helpers(type, is_func, as_func) \
|
||||
template<> inline bool is<type> (const Json::Value &val) { return val.is_func(); } \
|
||||
template<> inline type as<type> (const Json::Value &val) { return val.as_func(); } \
|
||||
template<> inline type get<type> (const Json::Value &val, const std::string &key, const type &default_) \
|
||||
{ Json::Value x = val[key]; return is<type>(x) ? as<type>(x) : default_; }
|
||||
define_helpers(bool, isBool, asBool);
|
||||
define_helpers(Json::Int, isInt, asInt);
|
||||
define_helpers(Json::UInt, isUInt, asUInt);
|
||||
define_helpers(Json::Int64, isInt64, asInt64);
|
||||
define_helpers(Json::UInt64, isUInt64, asUInt64);
|
||||
define_helpers(float, isDouble, asFloat);
|
||||
define_helpers(double, isDouble, asDouble);
|
||||
define_helpers(std::string, isString, asString);
|
||||
#undef define_helpers
|
||||
|
||||
inline std::string toSimpleString (const Json::Value &val)
|
||||
{
|
||||
Json::FastWriter w;
|
||||
return w.write(val);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
Subproject commit 9a0b18acb0967bfc523dca7af2cb34030d97c02b
|
@ -0,0 +1,28 @@
|
||||
-- dfstatus config
|
||||
-- the dfstatus script can be found in hack/scripts/gui/
|
||||
--[[
|
||||
The following variables can be set to true/false to enable/disable categories (all true by default)
|
||||
* drink
|
||||
* wood
|
||||
* fuel
|
||||
* prepared_meals
|
||||
* tanned_hides
|
||||
* cloth
|
||||
* metals
|
||||
|
||||
Example:
|
||||
drink = false
|
||||
fuel = true
|
||||
|
||||
To add metals:
|
||||
* metal 'IRON'
|
||||
* metals "GOLD" 'SILVER'
|
||||
* metal('COPPER')
|
||||
* metals("BRONZE", 'HORN_SILVER')
|
||||
Use '-' for a blank line:
|
||||
* metal '-'
|
||||
]]
|
||||
|
||||
metals 'IRON' 'PIG_IRON' 'STEEL'
|
||||
metals '-'
|
||||
metals 'GOLD' 'SILVER' 'COPPER'
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
* MacPool.h
|
||||
* Handles creation and destruction of autorelease pool for DFHack on the Mac
|
||||
*/
|
||||
|
||||
#ifndef MACPOOL_H
|
||||
#define MACPOOL_H
|
||||
|
||||
int create_pool();
|
||||
int destroy_pool();
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* MacPool.m
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MacPool.h"
|
||||
|
||||
NSAutoreleasePool *thePool;
|
||||
|
||||
int create_pool() {
|
||||
fprintf(stderr,"Creating autorelease pool\n");
|
||||
thePool = [[NSAutoreleasePool alloc] init];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int destroy_pool() {
|
||||
fprintf(stderr,"Draining and releasing autorelease pool\n");
|
||||
[thePool drain];
|
||||
[thePool release];
|
||||
return 0;
|
||||
}
|
@ -1,9 +1,15 @@
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long
|
||||
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE DFHACK_GIT_COMMIT)
|
||||
string(STRIP ${DFHACK_GIT_DESCRIPTION} DFHACK_GIT_DESCRIPTION)
|
||||
file(WRITE ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
|
||||
"#define DFHACK_GIT_DESCRIPTION \"${DFHACK_GIT_DESCRIPTION}\"")
|
||||
"#define DFHACK_GIT_DESCRIPTION \"${DFHACK_GIT_DESCRIPTION}\"\n")
|
||||
string(STRIP ${DFHACK_GIT_COMMIT} DFHACK_GIT_COMMIT)
|
||||
file(APPEND ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
|
||||
"#define DFHACK_GIT_COMMIT \"${DFHACK_GIT_COMMIT}\"")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
|
||||
${dfhack_SOURCE_DIR}/library/include/git-describe.h)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 5c4d627d7306b8425493f8ebbfe66c0f347f6871
|
||||
Subproject commit 2a49a0761e4413603d9a72542077755cea2ad56d
|
@ -0,0 +1,504 @@
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "VTableInterpose.h"
|
||||
#include "uicommon.h"
|
||||
#include "modules/Gui.h"
|
||||
|
||||
#include "df/building_tradedepotst.h"
|
||||
#include "df/general_ref.h"
|
||||
#include "df/general_ref_contained_in_itemst.h"
|
||||
#include "df/viewscreen_dwarfmodest.h"
|
||||
#include "df/viewscreen_layer_militaryst.h"
|
||||
#include "df/viewscreen_tradegoodsst.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
DFHACK_PLUGIN("confirm");
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
REQUIRE_GLOBAL(gps);
|
||||
REQUIRE_GLOBAL(ui);
|
||||
|
||||
typedef std::set<df::interface_key> ikey_set;
|
||||
command_result df_confirm (color_ostream &out, vector <string> & parameters);
|
||||
|
||||
struct conf_wrapper;
|
||||
static std::map<std::string, conf_wrapper*> confirmations;
|
||||
|
||||
template <typename VT, typename FT>
|
||||
bool in_vector (std::vector<VT> &vec, FT item)
|
||||
{
|
||||
return std::find(vec.begin(), vec.end(), item) != vec.end();
|
||||
}
|
||||
|
||||
#define goods_selected_func(list) \
|
||||
static bool list##_goods_selected(df::viewscreen_tradegoodsst *screen) \
|
||||
{ \
|
||||
for (auto it = screen->list##_selected.begin(); it != screen->list##_selected.end(); ++it) \
|
||||
if (*it) return true; \
|
||||
return false; \
|
||||
}
|
||||
goods_selected_func(trader);
|
||||
goods_selected_func(broker);
|
||||
#undef goods_selected_func
|
||||
|
||||
#define goods_all_selected_func(list) \
|
||||
static bool list##_goods_all_selected(df::viewscreen_tradegoodsst *screen) \
|
||||
{ \
|
||||
for (size_t i = 0; i < screen->list##_selected.size(); ++i) \
|
||||
{ \
|
||||
if (!screen->list##_selected[i]) \
|
||||
{ \
|
||||
std::vector<df::general_ref*> *refs = &screen->list##_items[i]->general_refs; \
|
||||
bool in_container = false; \
|
||||
for (auto it = refs->begin(); it != refs->end(); ++it) \
|
||||
{ \
|
||||
if (virtual_cast<df::general_ref_contained_in_itemst>(*it)) \
|
||||
{ \
|
||||
in_container = true; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if (!in_container) \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
return true; \
|
||||
}
|
||||
goods_all_selected_func(trader);
|
||||
goods_all_selected_func(broker);
|
||||
#undef goods_all_selected_func
|
||||
|
||||
template <class T>
|
||||
class confirmation {
|
||||
public:
|
||||
enum cstate { INACTIVE, ACTIVE, SELECTED };
|
||||
typedef T screen_type;
|
||||
screen_type *screen;
|
||||
bool feed (ikey_set *input) {
|
||||
if (state == INACTIVE)
|
||||
{
|
||||
for (auto it = input->begin(); it != input->end(); ++it)
|
||||
{
|
||||
if (intercept_key(*it))
|
||||
{
|
||||
last_key = *it;
|
||||
state = ACTIVE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (state == ACTIVE)
|
||||
{
|
||||
if (input->count(df::interface_key::LEAVESCREEN))
|
||||
state = INACTIVE;
|
||||
else if (input->count(df::interface_key::SELECT))
|
||||
state = SELECTED;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool key_conflict (df::interface_key key)
|
||||
{
|
||||
if (key == df::interface_key::SELECT || key == df::interface_key::LEAVESCREEN)
|
||||
return false;
|
||||
return state == ACTIVE;
|
||||
}
|
||||
void render() {
|
||||
static vector<string> lines;
|
||||
Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK);
|
||||
Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK);
|
||||
Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK);
|
||||
Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK);
|
||||
Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK);
|
||||
Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK);
|
||||
if (state == ACTIVE)
|
||||
{
|
||||
split_string(&lines, get_message(), "\n");
|
||||
size_t max_length = 30;
|
||||
for (auto it = lines.begin(); it != lines.end(); ++it)
|
||||
max_length = std::max(max_length, it->size());
|
||||
int width = max_length + 4;
|
||||
int height = lines.size() + 4;
|
||||
int x1 = (gps->dimx / 2) - (width / 2);
|
||||
int x2 = x1 + width - 1;
|
||||
int y1 = (gps->dimy / 2) - (height / 2);
|
||||
int y2 = y1 + height - 1;
|
||||
for (int x = x1; x <= x2; x++)
|
||||
{
|
||||
Screen::paintTile(border_ud, x, y1);
|
||||
Screen::paintTile(border_ud, x, y2);
|
||||
}
|
||||
for (int y = y1; y <= y2; y++)
|
||||
{
|
||||
Screen::paintTile(border_lr, x1, y);
|
||||
Screen::paintTile(border_lr, x2, y);
|
||||
}
|
||||
Screen::paintTile(corner_ul, x1, y1);
|
||||
Screen::paintTile(corner_ur, x2, y1);
|
||||
Screen::paintTile(corner_dl, x1, y2);
|
||||
Screen::paintTile(corner_dr, x2, y2);
|
||||
string title = " " + get_title() + " ";
|
||||
Screen::paintString(Screen::Pen(' ', COLOR_DARKGREY, COLOR_BLACK),
|
||||
x2 - 6, y1, "DFHack");
|
||||
Screen::paintString(Screen::Pen(' ', COLOR_BLACK, COLOR_GREY),
|
||||
(gps->dimx / 2) - (title.size() / 2), y1, title);
|
||||
int x = x1 + 2;
|
||||
OutputString(COLOR_LIGHTGREEN, x, y2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
|
||||
OutputString(COLOR_WHITE, x, y2, ": Cancel");
|
||||
x = x2 - 2 - 3 - Screen::getKeyDisplay(df::interface_key::SELECT).size();
|
||||
OutputString(COLOR_LIGHTGREEN, x, y2, Screen::getKeyDisplay(df::interface_key::SELECT));
|
||||
OutputString(COLOR_WHITE, x, y2, ": Ok");
|
||||
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), x1 + 1, y1 + 1, x2 - 1, y2 - 1);
|
||||
for (size_t i = 0; i < lines.size(); i++)
|
||||
{
|
||||
Screen::paintString(Screen::Pen(' ', get_color(), COLOR_BLACK), x1 + 2, y1 + 2 + i, lines[i]);
|
||||
}
|
||||
}
|
||||
else if (state == SELECTED)
|
||||
{
|
||||
ikey_set tmp;
|
||||
tmp.insert(last_key);
|
||||
screen->feed(&tmp);
|
||||
state = INACTIVE;
|
||||
}
|
||||
}
|
||||
virtual bool intercept_key (df::interface_key key) = 0;
|
||||
virtual string get_title() { return "Confirm"; }
|
||||
virtual string get_message() = 0;
|
||||
virtual UIColor get_color() { return COLOR_YELLOW; }
|
||||
protected:
|
||||
cstate state;
|
||||
df::interface_key last_key;
|
||||
};
|
||||
|
||||
struct conf_wrapper {
|
||||
bool enabled;
|
||||
std::set<VMethodInterposeLinkBase*> hooks;
|
||||
|
||||
conf_wrapper()
|
||||
:enabled(false)
|
||||
{}
|
||||
void add_hook(VMethodInterposeLinkBase *hook)
|
||||
{
|
||||
if (!hooks.count(hook))
|
||||
hooks.insert(hook);
|
||||
}
|
||||
bool apply (bool state) {
|
||||
if (state == enabled)
|
||||
return true;
|
||||
for (auto h = hooks.begin(); h != hooks.end(); ++h)
|
||||
{
|
||||
if (!(**h).apply(state))
|
||||
return false;
|
||||
}
|
||||
enabled = state;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#define IMPLEMENT_CONFIRMATION_HOOKS(cls) IMPLEMENT_CONFIRMATION_HOOKS_PRIO(cls, 0)
|
||||
#define IMPLEMENT_CONFIRMATION_HOOKS_PRIO(cls, prio) \
|
||||
static cls cls##_instance; \
|
||||
struct cls##_hooks : cls::screen_type { \
|
||||
typedef cls::screen_type interpose_base; \
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (ikey_set *input)) \
|
||||
{ \
|
||||
cls##_instance.screen = this; \
|
||||
if (!cls##_instance.feed(input)) \
|
||||
INTERPOSE_NEXT(feed)(input); \
|
||||
} \
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ()) \
|
||||
{ \
|
||||
cls##_instance.screen = this; \
|
||||
INTERPOSE_NEXT(render)(); \
|
||||
cls##_instance.render(); \
|
||||
} \
|
||||
DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) \
|
||||
{ \
|
||||
return cls##_instance.key_conflict(key) || INTERPOSE_NEXT(key_conflict)(key); \
|
||||
} \
|
||||
}; \
|
||||
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, feed, prio); \
|
||||
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, render, prio); \
|
||||
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, key_conflict, prio);
|
||||
|
||||
class trade_confirmation : public confirmation<df::viewscreen_tradegoodsst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return key == df::interface_key::TRADE_TRADE;
|
||||
}
|
||||
virtual string get_id() { return "trade"; }
|
||||
virtual string get_title() { return "Confirm trade"; }
|
||||
virtual string get_message()
|
||||
{
|
||||
if (trader_goods_selected(screen) && broker_goods_selected(screen))
|
||||
return "Are you sure you want to trade the selected goods?";
|
||||
else if (trader_goods_selected(screen))
|
||||
return "You are not giving any items. This is likely\n"
|
||||
"to irritate the merchants.\n"
|
||||
"Attempt to trade anyway?";
|
||||
else if (broker_goods_selected(screen))
|
||||
return "You are not receiving any items. You may want to\n"
|
||||
"offer these items instead or choose items to receive.\n"
|
||||
"Attempt to trade anyway?";
|
||||
else
|
||||
return "No items are selected. This is likely\n"
|
||||
"to irritate the merchants.\n"
|
||||
"Attempt to trade anyway?";
|
||||
}
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(trade_confirmation);
|
||||
|
||||
class trade_cancel_confirmation : public confirmation<df::viewscreen_tradegoodsst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return key == df::interface_key::LEAVESCREEN &&
|
||||
(trader_goods_selected(screen) || broker_goods_selected(screen));
|
||||
}
|
||||
virtual string get_id() { return "trade-cancel"; }
|
||||
virtual string get_title() { return "Cancel trade"; }
|
||||
virtual string get_message() { return "Are you sure you want leave this screen?\nSelected items will not be saved."; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS_PRIO(trade_cancel_confirmation, -1);
|
||||
|
||||
class trade_seize_confirmation : public confirmation<df::viewscreen_tradegoodsst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return trader_goods_selected(screen) && key == df::interface_key::TRADE_SEIZE;
|
||||
}
|
||||
virtual string get_id() { return "trade-seize"; }
|
||||
virtual string get_title() { return "Confirm seize"; }
|
||||
virtual string get_message() { return "Are you sure you want to sieze these goods?"; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(trade_seize_confirmation);
|
||||
|
||||
class trade_offer_confirmation : public confirmation<df::viewscreen_tradegoodsst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return broker_goods_selected(screen) && key == df::interface_key::TRADE_OFFER;
|
||||
}
|
||||
virtual string get_id() { return "trade-offer"; }
|
||||
virtual string get_title() { return "Confirm offer"; }
|
||||
virtual string get_message() { return "Are you sure you want to offer these goods?\nYou will receive no payment."; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(trade_offer_confirmation);
|
||||
|
||||
class trade_select_all_confirmation : public confirmation<df::viewscreen_tradegoodsst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
if (key == df::interface_key::SEC_SELECT)
|
||||
{
|
||||
if (screen->in_right_pane && broker_goods_selected(screen) && !broker_goods_all_selected(screen))
|
||||
return true;
|
||||
else if (!screen->in_right_pane && trader_goods_selected(screen) && !trader_goods_all_selected(screen))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual string get_id() { return "trade-select-all"; }
|
||||
virtual string get_title() { return "Confirm selection"; }
|
||||
virtual string get_message()
|
||||
{
|
||||
return "Selecting all goods will overwrite your current selection\n"
|
||||
"and cannot be undone. Continue?";
|
||||
}
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(trade_select_all_confirmation);
|
||||
|
||||
class hauling_route_delete_confirmation : public confirmation<df::viewscreen_dwarfmodest> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
if (ui->main.mode == ui_sidebar_mode::Hauling && ui->hauling.view_routes.size())
|
||||
return key == df::interface_key::D_HAULING_REMOVE;
|
||||
return false;
|
||||
}
|
||||
virtual string get_id() { return "haul-delete"; }
|
||||
virtual string get_title() { return "Confirm deletion"; }
|
||||
virtual string get_message()
|
||||
{
|
||||
std::string type = (ui->hauling.view_stops[ui->hauling.cursor_top]) ? "stop" : "route";
|
||||
return std::string("Are you sure you want to delete this ") + type + "?";
|
||||
}
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(hauling_route_delete_confirmation);
|
||||
|
||||
class depot_remove_confirmation : public confirmation<df::viewscreen_dwarfmodest> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
df::building_tradedepotst *depot = virtual_cast<df::building_tradedepotst>(Gui::getAnyBuilding(screen));
|
||||
if (depot && key == df::interface_key::DESTROYBUILDING)
|
||||
{
|
||||
for (auto it = ui->caravans.begin(); it != ui->caravans.end(); ++it)
|
||||
{
|
||||
if ((**it).time_remaining)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual string get_id() { return "depot-remove"; }
|
||||
virtual string get_title() { return "Confirm depot removal"; }
|
||||
virtual string get_message()
|
||||
{
|
||||
return "Are you sure you want to remove this depot?\n"
|
||||
"Merchants are present and will lose profits.";
|
||||
}
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(depot_remove_confirmation);
|
||||
|
||||
class squad_disband_confirmation : public confirmation<df::viewscreen_layer_militaryst> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return screen->num_squads && key == df::interface_key::D_MILITARY_DISBAND_SQUAD;
|
||||
}
|
||||
virtual string get_id() { return "squad-disband"; }
|
||||
virtual string get_title() { return "Disband squad"; }
|
||||
virtual string get_message() { return "Are you sure you want to disband this squad?"; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(squad_disband_confirmation);
|
||||
|
||||
class note_delete_confirmation : public confirmation<df::viewscreen_dwarfmodest> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return ui->main.mode == ui_sidebar_mode::NotesPoints && key == df::interface_key::D_NOTE_DELETE;
|
||||
}
|
||||
virtual string get_id() { return "note-delete"; }
|
||||
virtual string get_title() { return "Delete note"; }
|
||||
virtual string get_message() { return "Are you sure you want to delete this note?"; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(note_delete_confirmation);
|
||||
|
||||
class route_delete_confirmation : public confirmation<df::viewscreen_dwarfmodest> {
|
||||
public:
|
||||
virtual bool intercept_key (df::interface_key key)
|
||||
{
|
||||
return ui->main.mode == ui_sidebar_mode::NotesRoutes && key == df::interface_key::D_NOTE_ROUTE_DELETE;
|
||||
}
|
||||
virtual string get_id() { return "route-delete"; }
|
||||
virtual string get_title() { return "Delete route"; }
|
||||
virtual string get_message() { return "Are you sure you want to delete this route?"; }
|
||||
};
|
||||
IMPLEMENT_CONFIRMATION_HOOKS(route_delete_confirmation);
|
||||
|
||||
#define CHOOK(cls) \
|
||||
HOOK_ACTION(cls, render) \
|
||||
HOOK_ACTION(cls, feed) \
|
||||
HOOK_ACTION(cls, key_conflict)
|
||||
|
||||
#define CHOOKS \
|
||||
CHOOK(trade_confirmation) \
|
||||
CHOOK(trade_cancel_confirmation) \
|
||||
CHOOK(trade_seize_confirmation) \
|
||||
CHOOK(trade_offer_confirmation) \
|
||||
CHOOK(trade_select_all_confirmation) \
|
||||
CHOOK(hauling_route_delete_confirmation) \
|
||||
CHOOK(depot_remove_confirmation) \
|
||||
CHOOK(squad_disband_confirmation) \
|
||||
CHOOK(note_delete_confirmation) \
|
||||
CHOOK(route_delete_confirmation)
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, vector <PluginCommand> &commands)
|
||||
{
|
||||
#define HOOK_ACTION(cls, method) \
|
||||
if (confirmations.find(cls##_instance.get_id()) == confirmations.end()) \
|
||||
confirmations[cls##_instance.get_id()] = new conf_wrapper; \
|
||||
confirmations[cls##_instance.get_id()]->add_hook(&INTERPOSE_HOOK(cls##_hooks, method));
|
||||
CHOOKS
|
||||
#undef HOOK_ACTION
|
||||
commands.push_back(PluginCommand(
|
||||
"confirm",
|
||||
"Confirmation dialogs",
|
||||
df_confirm,
|
||||
false, //allow non-interactive use
|
||||
|
||||
" confirmation enable|disable option|all ...\n"
|
||||
" confirmation help|status\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
|
||||
{
|
||||
if (is_enabled != enable)
|
||||
{
|
||||
for (auto c = confirmations.begin(); c != confirmations.end(); ++c)
|
||||
{
|
||||
if (!c->second->apply(enable))
|
||||
return CR_FAILURE;
|
||||
}
|
||||
is_enabled = enable;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
|
||||
{
|
||||
if (plugin_enable(out, false) != CR_OK)
|
||||
return CR_FAILURE;
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
void enable_conf (color_ostream &out, string name, bool state)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto it = confirmations.begin(); it != confirmations.end(); ++it)
|
||||
{
|
||||
if (it->first == name)
|
||||
{
|
||||
found = true;
|
||||
it->second->apply(state);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
out.printerr("Unrecognized option: %s\n", name.c_str());
|
||||
}
|
||||
|
||||
command_result df_confirm (color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
bool state = true;
|
||||
if (in_vector(parameters, "help") || in_vector(parameters, "status"))
|
||||
{
|
||||
out << "Available options: \n";
|
||||
for (auto it = confirmations.begin(); it != confirmations.end(); ++it)
|
||||
{
|
||||
out << " " << it->first << ": " << (it->second->enabled ? "enabled" : "disabled") << std::endl;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
for (auto it = parameters.begin(); it != parameters.end(); ++it)
|
||||
{
|
||||
if (*it == "enable")
|
||||
state = true;
|
||||
else if (*it == "disable")
|
||||
state = false;
|
||||
else if (*it == "all")
|
||||
{
|
||||
for (auto it = confirmations.begin(); it != confirmations.end(); ++it)
|
||||
{
|
||||
it->second->apply(state);
|
||||
}
|
||||
}
|
||||
else
|
||||
enable_conf(out, *it, state);
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
#include <modules/Gui.h>
|
||||
#include <modules/Screen.h>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include <VTableInterpose.h>
|
||||
#include "df/graphic.h"
|
||||
#include "df/viewscreen_titlest.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::stack;
|
||||
using namespace DFHack;
|
||||
|
||||
using df::global::gps;
|
||||
|
||||
DFHACK_PLUGIN("vshook");
|
||||
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
|
||||
struct title_hook : df::viewscreen_titlest {
|
||||
typedef df::viewscreen_titlest interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
|
||||
Screen::Pen pen(' ',COLOR_WHITE,COLOR_BLACK);
|
||||
Screen::paintString(pen, 0, 0, "DFHack ");
|
||||
Screen::paintString(pen, 7, 0, Version::dfhack_version());
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(title_hook, render);
|
||||
|
||||
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
|
||||
{
|
||||
if (!gps)
|
||||
return CR_FAILURE;
|
||||
|
||||
if (enable != is_enabled)
|
||||
{
|
||||
if (!INTERPOSE_HOOK(title_hook, render).apply(enable))
|
||||
return CR_FAILURE;
|
||||
|
||||
is_enabled = enable;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
// DON'T DO THIS IN NON-EXAMPLE PLUGINS
|
||||
plugin_enable(out, true);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
INTERPOSE_HOOK(title_hook, render).remove();
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
#include "Console.h"
|
||||
#include "Core.h"
|
||||
#include "DataDefs.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/Units.h"
|
||||
#include "modules/Translation.h"
|
||||
#include "modules/World.h"
|
||||
|
||||
#include "df/map_block.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/world.h"
|
||||
|
||||
using namespace DFHack;
|
||||
|
||||
DFHACK_PLUGIN("fix-unit-occupancy");
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
REQUIRE_GLOBAL(cursor);
|
||||
REQUIRE_GLOBAL(world);
|
||||
|
||||
static int run_interval = 1200; // daily
|
||||
|
||||
inline float getClock()
|
||||
{
|
||||
return (float)clock() / (float)CLOCKS_PER_SEC;
|
||||
}
|
||||
|
||||
static std::string get_unit_description(df::unit *unit)
|
||||
{
|
||||
if (!unit)
|
||||
return "";
|
||||
std::string desc;
|
||||
auto name = Units::getVisibleName(unit);
|
||||
if (name->has_name)
|
||||
desc = Translation::TranslateName(name, false);
|
||||
desc += (desc.size() ? ", " : "") + Units::getProfessionName(unit); // Check animal type too
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
df::unit *findUnit(int x, int y, int z)
|
||||
{
|
||||
for (auto u = world->units.active.begin(); u != world->units.active.end(); ++u)
|
||||
{
|
||||
if ((**u).pos.x == x && (**u).pos.y == y && (**u).pos.z == z)
|
||||
return *u;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct uo_opts {
|
||||
bool dry_run;
|
||||
bool use_cursor;
|
||||
uo_opts (bool dry_run = false, bool use_cursor = false)
|
||||
:dry_run(dry_run), use_cursor(use_cursor)
|
||||
{}
|
||||
};
|
||||
|
||||
command_result cmd_fix_unit_occupancy (color_ostream &out, std::vector <std::string> & parameters);
|
||||
|
||||
unsigned fix_unit_occupancy (color_ostream &out, uo_opts &opts)
|
||||
{
|
||||
if (!Core::getInstance().isWorldLoaded())
|
||||
return 0;
|
||||
|
||||
if (opts.use_cursor && cursor->x < 0)
|
||||
out.printerr("No cursor\n");
|
||||
|
||||
unsigned count = 0;
|
||||
float time1 = getClock();
|
||||
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
|
||||
{
|
||||
df::map_block *block = world->map.map_blocks[i];
|
||||
int map_z = block->map_pos.z;
|
||||
if (opts.use_cursor && (map_z != cursor->z || block->map_pos.y != (cursor->y / 16) * 16 || block->map_pos.x != (cursor->x / 16) * 16))
|
||||
continue;
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
int map_x = x + block->map_pos.x;
|
||||
for (int y = 0; y < 16; y++)
|
||||
{
|
||||
if (block->designation[x][y].bits.hidden)
|
||||
continue;
|
||||
int map_y = y + block->map_pos.y;
|
||||
if (opts.use_cursor && (map_x != cursor->x || map_y != cursor->y))
|
||||
continue;
|
||||
bool cur_occupancy = block->occupancy[x][y].bits.unit;
|
||||
bool fixed_occupancy = cur_occupancy;
|
||||
df::unit *cur_unit = findUnit(map_x, map_y, map_z);
|
||||
if (cur_occupancy && !cur_unit)
|
||||
{
|
||||
out.print("%sFixing occupancy at (%i, %i, %i) - no unit found\n",
|
||||
opts.dry_run ? "(Dry run) " : "",
|
||||
map_x, map_y, map_z);
|
||||
fixed_occupancy = false;
|
||||
}
|
||||
// else if (!cur_occupancy && cur_unit)
|
||||
// {
|
||||
// out.printerr("Unit found at (%i, %i, %i): %s\n", map_x, map_y, map_z, get_unit_description(cur_unit).c_str());
|
||||
// fixed_occupancy = true;
|
||||
// }
|
||||
if (cur_occupancy != fixed_occupancy && !opts.dry_run)
|
||||
{
|
||||
++count;
|
||||
block->occupancy[x][y].bits.unit = fixed_occupancy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float time2 = getClock();
|
||||
std::cerr << "fix-unit-occupancy: elapsed time: " << time2 - time1 << " secs" << endl;
|
||||
if (count)
|
||||
out << "Fixed occupancy of " << count << " tiles [fix-unit-occupancy]" << endl;
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned fix_unit_occupancy (color_ostream &out)
|
||||
{
|
||||
uo_opts tmp;
|
||||
return fix_unit_occupancy(out, tmp);
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"fix-unit-occupancy",
|
||||
"Fix unit occupancy issues such as phantom 'creature blocking site' messages (bug 3499)",
|
||||
cmd_fix_unit_occupancy,
|
||||
false, //allow non-interactive use
|
||||
" enable fix-unit-occupancy: Enable the plugin\n"
|
||||
" disable fix-unit-occupancy fix-unit-occupancy: Disable the plugin\n"
|
||||
" fix-unit-occupancy: Run the plugin immediately. Available options:\n"
|
||||
" -h|here|cursor: Only operate on the tile at the cursor\n"
|
||||
" -n|dry|dry-run: Do not write changes to map\n"
|
||||
" fix-unit-occupancy interval X: Run the plugin every X ticks (when enabled).\n"
|
||||
" Default is 1200, or 1 day. Ticks are only counted when the game is unpaused.\n"
|
||||
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
|
||||
{
|
||||
is_enabled = enable;
|
||||
if (is_enabled)
|
||||
fix_unit_occupancy(out);
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onupdate (color_ostream &out)
|
||||
{
|
||||
static unsigned tick = UINT_MAX;
|
||||
static decltype(world->frame_counter) old_world_frame = 0;
|
||||
if (is_enabled && World::isFortressMode())
|
||||
{
|
||||
// only increment tick when the world has changed
|
||||
if (old_world_frame != world->frame_counter)
|
||||
{
|
||||
old_world_frame = world->frame_counter;
|
||||
tick++;
|
||||
}
|
||||
if (tick > run_interval)
|
||||
{
|
||||
tick = 0;
|
||||
fix_unit_occupancy(out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tick = INT_MAX;
|
||||
old_world_frame = 0;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange (color_ostream &out, state_change_event evt)
|
||||
{
|
||||
if (evt == SC_MAP_LOADED && is_enabled && World::isFortressMode())
|
||||
fix_unit_occupancy(out);
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result cmd_fix_unit_occupancy (color_ostream &out, std::vector <std::string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
uo_opts opts;
|
||||
bool ok = true;
|
||||
|
||||
if (parameters.size() >= 1 && (parameters[0] == "-i" || parameters[0].find("interval") != std::string::npos))
|
||||
{
|
||||
if (parameters.size() >= 2)
|
||||
{
|
||||
int new_interval = atoi(parameters[1].c_str());
|
||||
if (new_interval < 100)
|
||||
{
|
||||
out.printerr("Invalid interval - minimum is 100 ticks\n");
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
run_interval = new_interval;
|
||||
if (!is_enabled)
|
||||
out << "note: Plugin not enabled (use `enable fix-unit-occupancy` to enable)" << endl;
|
||||
return CR_OK;
|
||||
}
|
||||
else
|
||||
return CR_WRONG_USAGE;
|
||||
}
|
||||
|
||||
for (auto opt = parameters.begin(); opt != parameters.end(); ++opt)
|
||||
{
|
||||
if (*opt == "-n" || opt->find("dry") != std::string::npos)
|
||||
opts.dry_run = true;
|
||||
else if (*opt == "-h" || opt->find("cursor") != std::string::npos || opt->find("here") != std::string::npos)
|
||||
opts.use_cursor = true;
|
||||
else if (opt->find("enable") != std::string::npos)
|
||||
plugin_enable(out, true);
|
||||
else if (opt->find("disable") != std::string::npos)
|
||||
plugin_enable(out, false);
|
||||
else
|
||||
{
|
||||
out.printerr("Unknown parameter: %s\n", opt->c_str());
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
unsigned count = fix_unit_occupancy(out, opts);
|
||||
if (!count)
|
||||
out << "No occupancy issues found." << endl;
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
#include "uicommon.h"
|
||||
|
||||
using df::global::enabler;
|
||||
using df::global::gps;
|
||||
|
||||
/*
|
||||
* List classes
|
||||
*/
|
||||
template <typename T>
|
||||
class ListEntry
|
||||
{
|
||||
public:
|
||||
T elem;
|
||||
string text, keywords;
|
||||
bool selected;
|
||||
UIColor color;
|
||||
|
||||
ListEntry(const string text, const T elem, const string keywords = "", const UIColor color = COLOR_UNSELECTED) :
|
||||
elem(elem), text(text), selected(false), keywords(keywords), color(color)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ListColumn
|
||||
{
|
||||
public:
|
||||
int highlighted_index;
|
||||
int display_start_offset;
|
||||
unsigned short text_clip_at;
|
||||
int32_t bottom_margin, search_margin, left_margin;
|
||||
bool multiselect;
|
||||
bool allow_null;
|
||||
bool auto_select;
|
||||
bool allow_search;
|
||||
bool feed_mouse_set_highlight;
|
||||
bool feed_changed_highlight;
|
||||
|
||||
ListColumn()
|
||||
{
|
||||
bottom_margin = 3;
|
||||
clear();
|
||||
left_margin = 2;
|
||||
search_margin = 63;
|
||||
highlighted_index = 0;
|
||||
text_clip_at = 0;
|
||||
multiselect = false;
|
||||
allow_null = true;
|
||||
auto_select = false;
|
||||
allow_search = true;
|
||||
feed_mouse_set_highlight = false;
|
||||
feed_changed_highlight = false;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
list.clear();
|
||||
display_list.clear();
|
||||
display_start_offset = 0;
|
||||
if (highlighted_index != -1)
|
||||
highlighted_index = 0;
|
||||
max_item_width = title.length();
|
||||
resize();
|
||||
}
|
||||
|
||||
void resize()
|
||||
{
|
||||
display_max_rows = gps->dimy - 4 - bottom_margin;
|
||||
}
|
||||
|
||||
void add(ListEntry<T> &entry)
|
||||
{
|
||||
list.push_back(entry);
|
||||
if (entry.text.length() > max_item_width)
|
||||
max_item_width = entry.text.length();
|
||||
}
|
||||
|
||||
void add(const string &text, const T &elem)
|
||||
{
|
||||
list.push_back(ListEntry<T>(text, elem));
|
||||
if (text.length() > max_item_width)
|
||||
max_item_width = text.length();
|
||||
}
|
||||
|
||||
int fixWidth()
|
||||
{
|
||||
if (text_clip_at > 0 && max_item_width > text_clip_at)
|
||||
max_item_width = text_clip_at;
|
||||
|
||||
for (auto it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
it->text = pad_string(it->text, max_item_width, false);
|
||||
}
|
||||
|
||||
return getMaxItemWidth();
|
||||
}
|
||||
|
||||
int getMaxItemWidth()
|
||||
{
|
||||
return left_margin + max_item_width;
|
||||
}
|
||||
|
||||
virtual void display_extras(const T &elem, int32_t &x, int32_t &y) const {}
|
||||
|
||||
void display(const bool is_selected_column) const
|
||||
{
|
||||
int32_t y = 2;
|
||||
paint_text(COLOR_TITLE, left_margin, y, title);
|
||||
|
||||
int last_index_able_to_display = display_start_offset + display_max_rows;
|
||||
for (int i = display_start_offset; i < display_list.size() && i < last_index_able_to_display; i++)
|
||||
{
|
||||
++y;
|
||||
UIColor fg_color = (display_list[i]->selected) ? COLOR_SELECTED : display_list[i]->color;
|
||||
UIColor bg_color = (is_selected_column && i == highlighted_index) ? COLOR_HIGHLIGHTED : COLOR_BLACK;
|
||||
|
||||
string item_label = display_list[i]->text;
|
||||
if (text_clip_at > 0 && item_label.length() > text_clip_at)
|
||||
item_label.resize(text_clip_at);
|
||||
|
||||
paint_text(fg_color, left_margin, y, item_label, bg_color);
|
||||
int x = left_margin + display_list[i]->text.length() + 1;
|
||||
display_extras(display_list[i]->elem, x, y);
|
||||
}
|
||||
|
||||
if (is_selected_column && allow_search)
|
||||
{
|
||||
y = gps->dimy - 3;
|
||||
int32_t x = search_margin;
|
||||
OutputHotkeyString(x, y, "Search" ,"S");
|
||||
OutputString(COLOR_WHITE, x, y, ": ");
|
||||
OutputString(COLOR_WHITE, x, y, search_string);
|
||||
OutputString(COLOR_LIGHTGREEN, x, y, "_");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void tokenizeSearch (vector<string> *dest, const string search)
|
||||
{
|
||||
if (!search.empty())
|
||||
split_string(dest, search, " ");
|
||||
}
|
||||
|
||||
virtual bool showEntry(const ListEntry<T> *entry, const vector<string> &search_tokens)
|
||||
{
|
||||
if (!search_tokens.empty())
|
||||
{
|
||||
string item_string = toLower(entry->text);
|
||||
for (auto si = search_tokens.begin(); si != search_tokens.end(); si++)
|
||||
{
|
||||
if (!si->empty() && item_string.find(*si) == string::npos &&
|
||||
entry->keywords.find(*si) == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void filterDisplay()
|
||||
{
|
||||
ListEntry<T> *prev_selected = (getDisplayListSize() > 0) ? display_list[highlighted_index] : NULL;
|
||||
display_list.clear();
|
||||
|
||||
search_string = toLower(search_string);
|
||||
vector<string> search_tokens;
|
||||
tokenizeSearch(&search_tokens, search_string);
|
||||
|
||||
for (size_t i = 0; i < list.size(); i++)
|
||||
{
|
||||
ListEntry<T> *entry = &list[i];
|
||||
|
||||
if (showEntry(entry, search_tokens))
|
||||
{
|
||||
display_list.push_back(entry);
|
||||
if (entry == prev_selected)
|
||||
highlighted_index = display_list.size() - 1;
|
||||
}
|
||||
else if (auto_select)
|
||||
{
|
||||
entry->selected = false;
|
||||
}
|
||||
}
|
||||
changeHighlight(0);
|
||||
feed_changed_highlight = true;
|
||||
}
|
||||
|
||||
void selectDefaultEntry()
|
||||
{
|
||||
for (size_t i = 0; i < display_list.size(); i++)
|
||||
{
|
||||
if (display_list[i]->selected)
|
||||
{
|
||||
highlighted_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void centerSelection()
|
||||
{
|
||||
if (display_list.size() == 0)
|
||||
return;
|
||||
display_start_offset = highlighted_index - (display_max_rows / 2);
|
||||
validateDisplayOffset();
|
||||
validateHighlight();
|
||||
}
|
||||
|
||||
void validateHighlight()
|
||||
{
|
||||
set_to_limit(highlighted_index, display_list.size() - 1);
|
||||
|
||||
if (highlighted_index < display_start_offset)
|
||||
display_start_offset = highlighted_index;
|
||||
else if (highlighted_index >= display_start_offset + display_max_rows)
|
||||
display_start_offset = highlighted_index - display_max_rows + 1;
|
||||
|
||||
if (auto_select || (!allow_null && list.size() == 1))
|
||||
display_list[highlighted_index]->selected = true;
|
||||
|
||||
feed_changed_highlight = true;
|
||||
}
|
||||
|
||||
void changeHighlight(const int highlight_change, const int offset_shift = 0)
|
||||
{
|
||||
if (!initHighlightChange())
|
||||
return;
|
||||
|
||||
highlighted_index += highlight_change + offset_shift * display_max_rows;
|
||||
|
||||
display_start_offset += offset_shift * display_max_rows;
|
||||
validateDisplayOffset();
|
||||
validateHighlight();
|
||||
}
|
||||
|
||||
void validateDisplayOffset()
|
||||
{
|
||||
set_to_limit(display_start_offset, max(0, (int)(display_list.size())-display_max_rows));
|
||||
}
|
||||
|
||||
void setHighlight(const int index)
|
||||
{
|
||||
if (!initHighlightChange())
|
||||
return;
|
||||
|
||||
highlighted_index = index;
|
||||
validateHighlight();
|
||||
}
|
||||
|
||||
bool initHighlightChange()
|
||||
{
|
||||
if (display_list.size() == 0)
|
||||
return false;
|
||||
|
||||
if (auto_select && !multiselect)
|
||||
{
|
||||
for (auto it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
it->selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void toggleHighlighted()
|
||||
{
|
||||
if (display_list.size() == 0)
|
||||
return;
|
||||
|
||||
if (auto_select)
|
||||
return;
|
||||
|
||||
ListEntry<T> *entry = display_list[highlighted_index];
|
||||
if (!multiselect || !allow_null)
|
||||
{
|
||||
int selected_count = 0;
|
||||
for (size_t i = 0; i < list.size(); i++)
|
||||
{
|
||||
if (!multiselect && !entry->selected)
|
||||
list[i].selected = false;
|
||||
if (!allow_null && list[i].selected)
|
||||
selected_count++;
|
||||
}
|
||||
|
||||
if (!allow_null && entry->selected && selected_count == 1)
|
||||
return;
|
||||
}
|
||||
|
||||
entry->selected = !entry->selected;
|
||||
}
|
||||
|
||||
vector<T> getSelectedElems(bool only_one = false)
|
||||
{
|
||||
vector<T> results;
|
||||
for (auto it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
if ((*it).selected)
|
||||
{
|
||||
results.push_back(it->elem);
|
||||
if (only_one)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
T getFirstSelectedElem()
|
||||
{
|
||||
vector<T> results = getSelectedElems(true);
|
||||
if (results.size() == 0)
|
||||
return (T)nullptr;
|
||||
else
|
||||
return results[0];
|
||||
}
|
||||
|
||||
void clearSelection()
|
||||
{
|
||||
for_each_(list, clear_fn);
|
||||
}
|
||||
|
||||
void selectItem(const T elem)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < display_list.size(); i++)
|
||||
{
|
||||
if (display_list[i]->elem == elem)
|
||||
{
|
||||
setHighlight(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearSearch()
|
||||
{
|
||||
search_string.clear();
|
||||
filterDisplay();
|
||||
}
|
||||
|
||||
size_t getDisplayListSize()
|
||||
{
|
||||
return display_list.size();
|
||||
}
|
||||
|
||||
vector<ListEntry<T>*> &getDisplayList()
|
||||
{
|
||||
return display_list;
|
||||
}
|
||||
|
||||
size_t getBaseListSize()
|
||||
{
|
||||
return list.size();
|
||||
}
|
||||
|
||||
virtual bool validSearchInput (unsigned char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || c == ' ';
|
||||
}
|
||||
|
||||
bool feed(set<df::interface_key> *input)
|
||||
{
|
||||
feed_mouse_set_highlight = feed_changed_highlight = false;
|
||||
if (input->count(interface_key::STANDARDSCROLL_UP))
|
||||
{
|
||||
changeHighlight(-1);
|
||||
}
|
||||
else if (input->count(interface_key::STANDARDSCROLL_DOWN))
|
||||
{
|
||||
changeHighlight(1);
|
||||
}
|
||||
else if (input->count(interface_key::STANDARDSCROLL_PAGEUP))
|
||||
{
|
||||
changeHighlight(0, -1);
|
||||
}
|
||||
else if (input->count(interface_key::STANDARDSCROLL_PAGEDOWN))
|
||||
{
|
||||
changeHighlight(0, 1);
|
||||
}
|
||||
else if (input->count(interface_key::SELECT) && !auto_select)
|
||||
{
|
||||
toggleHighlighted();
|
||||
}
|
||||
else if (input->count(interface_key::CUSTOM_SHIFT_S))
|
||||
{
|
||||
clearSearch();
|
||||
}
|
||||
else if (enabler->tracking_on && gps->mouse_x != -1 && gps->mouse_y != -1 && enabler->mouse_lbut)
|
||||
{
|
||||
return setHighlightByMouse();
|
||||
}
|
||||
else if (allow_search)
|
||||
{
|
||||
// Search query typing mode always on
|
||||
df::interface_key last_token = get_string_key(input);
|
||||
int charcode = Screen::keyToChar(last_token);
|
||||
if (charcode >= 0 && validSearchInput((unsigned char)charcode))
|
||||
{
|
||||
// Standard character
|
||||
search_string += char(charcode);
|
||||
filterDisplay();
|
||||
centerSelection();
|
||||
}
|
||||
else if (last_token == interface_key::STRING_A000)
|
||||
{
|
||||
// Backspace
|
||||
if (search_string.length() > 0)
|
||||
{
|
||||
search_string.erase(search_string.length()-1);
|
||||
filterDisplay();
|
||||
centerSelection();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setHighlightByMouse()
|
||||
{
|
||||
if (gps->mouse_y >= 3 && gps->mouse_y < display_max_rows + 3 &&
|
||||
gps->mouse_x >= left_margin && gps->mouse_x < left_margin + max_item_width)
|
||||
{
|
||||
int new_index = display_start_offset + gps->mouse_y - 3;
|
||||
if (new_index < display_list.size())
|
||||
{
|
||||
setHighlight(new_index);
|
||||
feed_mouse_set_highlight = true;
|
||||
}
|
||||
|
||||
enabler->mouse_lbut = enabler->mouse_rbut = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sort(bool force_sort = false)
|
||||
{
|
||||
if (force_sort || list.size() < 100)
|
||||
std::sort(list.begin(), list.end(), sort_fn);
|
||||
|
||||
filterDisplay();
|
||||
}
|
||||
|
||||
void setTitle(const string t)
|
||||
{
|
||||
title = t;
|
||||
if (title.length() > max_item_width)
|
||||
max_item_width = title.length();
|
||||
}
|
||||
|
||||
size_t getDisplayedListSize()
|
||||
{
|
||||
return display_list.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
static void clear_fn(ListEntry<T> &e) { e.selected = false; }
|
||||
static bool sort_fn(ListEntry<T> const& a, ListEntry<T> const& b) { return a.text.compare(b.text) < 0; }
|
||||
|
||||
vector<ListEntry<T>> list;
|
||||
vector<ListEntry<T>*> display_list;
|
||||
string search_string;
|
||||
string title;
|
||||
int display_max_rows;
|
||||
int max_item_width;
|
||||
};
|
||||
|
@ -0,0 +1,67 @@
|
||||
local _ENV = mkmodule('plugins.luasocket')
|
||||
local _funcs={}
|
||||
for k,v in pairs(_ENV) do
|
||||
if type(v)=="function" then
|
||||
_funcs[k]=v
|
||||
_ENV[k]=nil
|
||||
end
|
||||
end
|
||||
|
||||
local socket=defclass(socket)
|
||||
socket.ATTRS={
|
||||
server_id=-1,
|
||||
client_id=-1,
|
||||
}
|
||||
|
||||
function socket:close( )
|
||||
if self.client_id==-1 then
|
||||
_funcs.lua_server_close(self.server_id)
|
||||
else
|
||||
_funcs.lua_client_close(self.server_id,self.client_id)
|
||||
end
|
||||
end
|
||||
function socket:setTimeout( sec,msec )
|
||||
msec=msec or 0
|
||||
_funcs.lua_socket_set_timeout(self.server_id,self.client_id,sec,msec)
|
||||
end
|
||||
|
||||
local client=defclass(client,socket)
|
||||
function client:receive( pattern )
|
||||
local pattern=pattern or "*l"
|
||||
local bytes=-1
|
||||
if type(pattern)== number then
|
||||
bytes=pattern
|
||||
end
|
||||
local ret=_funcs.lua_client_receive(self.server_id,self.client_id,bytes,pattern,false)
|
||||
if ret=="" then
|
||||
return
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
function client:send( data )
|
||||
_funcs.lua_client_send(self.server_id,self.client_id,data)
|
||||
end
|
||||
|
||||
|
||||
local server=defclass(server,socket)
|
||||
function server:accept()
|
||||
local id=_funcs.lua_server_accept(self.server_id,false)
|
||||
if id~=nil then
|
||||
return client{server_id=self.server_id,client_id=id}
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
tcp={}
|
||||
function tcp:bind( address,port )
|
||||
local id=_funcs.lua_socket_bind(address,port)
|
||||
return server{server_id=id}
|
||||
end
|
||||
function tcp:connect( address,port )
|
||||
local id=_funcs.lua_socket_connect(address,port)
|
||||
return client{client_id=id}
|
||||
end
|
||||
--TODO garbage collect stuff
|
||||
return _ENV
|
@ -0,0 +1,352 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <PassiveSocket.h>
|
||||
#include <ActiveSocket.h>
|
||||
#include "MiscUtils.h"
|
||||
#include "LuaTools.h"
|
||||
#include "DataFuncs.h"
|
||||
#include <stdexcept> //todo convert errors to lua-errors and co. Then remove this
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
struct server
|
||||
{
|
||||
CPassiveSocket *socket;
|
||||
std::map<int,CActiveSocket*> clients;
|
||||
int last_client_id;
|
||||
void close();
|
||||
};
|
||||
std::map<int,server> servers;
|
||||
typedef std::map<int,CActiveSocket*> clients_map;
|
||||
clients_map clients; //free clients, i.e. non-server spawned clients
|
||||
DFHACK_PLUGIN("luasocket");
|
||||
|
||||
// The error messages are taken from the clsocket source code
|
||||
const char * translate_socket_error(CSimpleSocket::CSocketError err) {
|
||||
switch (err) {
|
||||
case CSimpleSocket::SocketError:
|
||||
return "Generic socket error translates to error below.";
|
||||
case CSimpleSocket::SocketSuccess:
|
||||
return "No socket error.";
|
||||
case CSimpleSocket::SocketInvalidSocket:
|
||||
return "Invalid socket handle.";
|
||||
case CSimpleSocket::SocketInvalidAddress:
|
||||
return "Invalid destination address specified.";
|
||||
case CSimpleSocket::SocketInvalidPort:
|
||||
return "Invalid destination port specified.";
|
||||
case CSimpleSocket::SocketConnectionRefused:
|
||||
return "No server is listening at remote address.";
|
||||
case CSimpleSocket::SocketTimedout:
|
||||
return "Timed out while attempting operation.";
|
||||
case CSimpleSocket::SocketEwouldblock:
|
||||
return "Operation would block if socket were blocking.";
|
||||
case CSimpleSocket::SocketNotconnected:
|
||||
return "Currently not connected.";
|
||||
case CSimpleSocket::SocketEinprogress:
|
||||
return "Socket is non-blocking and the connection cannot be completed immediately";
|
||||
case CSimpleSocket::SocketInterrupted:
|
||||
return "Call was interrupted by a signal that was caught before a valid connection arrived.";
|
||||
case CSimpleSocket::SocketConnectionAborted:
|
||||
return "The connection has been aborted.";
|
||||
case CSimpleSocket::SocketProtocolError:
|
||||
return "Invalid protocol for operation.";
|
||||
case CSimpleSocket::SocketFirewallError:
|
||||
return "Firewall rules forbid connection.";
|
||||
case CSimpleSocket::SocketInvalidSocketBuffer:
|
||||
return "The receive buffer point outside the process's address space.";
|
||||
case CSimpleSocket::SocketConnectionReset:
|
||||
return "Connection was forcibly closed by the remote host.";
|
||||
case CSimpleSocket::SocketAddressInUse:
|
||||
return "Address already in use.";
|
||||
case CSimpleSocket::SocketInvalidPointer:
|
||||
return "Pointer type supplied as argument is invalid.";
|
||||
case CSimpleSocket::SocketEunknown:
|
||||
return "Unknown error please report to mark@carrierlabs.com";
|
||||
default:
|
||||
return "No such CSimpleSocket error";
|
||||
}
|
||||
}
|
||||
void server::close()
|
||||
{
|
||||
for(auto it=clients.begin();it!=clients.end();it++)
|
||||
{
|
||||
CActiveSocket* sock=it->second;
|
||||
sock->Close();
|
||||
delete sock;
|
||||
}
|
||||
clients.clear();
|
||||
socket->Close();
|
||||
delete socket;
|
||||
}
|
||||
std::pair<CActiveSocket*,clients_map*> get_client(int server_id,int client_id)
|
||||
{
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
return std::make_pair(sock,target);
|
||||
}
|
||||
void handle_error(CSimpleSocket::CSocketError err,bool skip_timeout=true)
|
||||
{
|
||||
if(err==CSimpleSocket::SocketSuccess)
|
||||
return;
|
||||
if(err==CSimpleSocket::SocketTimedout && skip_timeout)
|
||||
return;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
static int lua_socket_bind(std::string ip,int port)
|
||||
{
|
||||
static int server_id=0;
|
||||
CPassiveSocket* sock=new CPassiveSocket;
|
||||
if(!sock->Initialize())
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
handle_error(err,false);
|
||||
}
|
||||
sock->SetBlocking();
|
||||
if(!sock->Listen((uint8_t*)ip.c_str(),port))
|
||||
{
|
||||
handle_error(sock->GetSocketError(),false);
|
||||
}
|
||||
server_id++;
|
||||
server& cur_server=servers[server_id];
|
||||
cur_server.socket=sock;
|
||||
cur_server.last_client_id=0;
|
||||
return server_id;
|
||||
}
|
||||
static int lua_server_accept(int id,bool fail_on_timeout)
|
||||
{
|
||||
if(servers.count(id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server not bound");
|
||||
}
|
||||
server &cur_server=servers[id];
|
||||
CActiveSocket* sock=cur_server.socket->Accept();
|
||||
if(!sock)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_server.last_client_id++;
|
||||
cur_server.clients[cur_server.last_client_id]=sock;
|
||||
return cur_server.last_client_id;
|
||||
}
|
||||
}
|
||||
static void lua_client_close(int server_id,int client_id)
|
||||
{
|
||||
auto info=get_client(server_id,client_id);
|
||||
|
||||
CActiveSocket *sock=info.first;
|
||||
std::map<int,CActiveSocket*>* target=info.second;
|
||||
|
||||
target->erase(client_id);
|
||||
CSimpleSocket::CSocketError err=CSimpleSocket::SocketSuccess;
|
||||
if(!sock->Close())
|
||||
err=sock->GetSocketError();
|
||||
delete sock;
|
||||
if(err!=CSimpleSocket::SocketSuccess)
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
}
|
||||
static void lua_server_close(int server_id)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
try{
|
||||
cur_server.close();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
servers.erase(server_id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
static std::string lua_client_receive(int server_id,int client_id,int bytes,std::string pattern,bool fail_on_timeout)
|
||||
{
|
||||
auto info=get_client(server_id,client_id);
|
||||
CActiveSocket *sock=info.first;
|
||||
if(bytes>0)
|
||||
{
|
||||
if(sock->Receive(bytes)<=0)
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(sock->GetSocketError()));
|
||||
}
|
||||
return std::string((char*)sock->GetData(),bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ret;
|
||||
if(pattern=="*a") //??
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
int received=sock->Receive(1);
|
||||
if(received<0)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return "";//maybe return partial string?
|
||||
}
|
||||
else if(received==0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ret+=(char)*sock->GetData();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else if (pattern=="" || pattern=="*l")
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
|
||||
if(sock->Receive(1)<=0)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return "";//maybe return partial string?
|
||||
}
|
||||
char rec=(char)*sock->GetData();
|
||||
if(rec=='\n')
|
||||
break;
|
||||
ret+=rec;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unsupported receive pattern");
|
||||
}
|
||||
}
|
||||
}
|
||||
static void lua_client_send(int server_id,int client_id,std::string data)
|
||||
{
|
||||
if(data.size()==0)
|
||||
return;
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
if(sock->Send((const uint8_t*)data.c_str(),data.size())!=data.size())
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(sock->GetSocketError()));
|
||||
}
|
||||
}
|
||||
static int lua_socket_connect(std::string ip,int port)
|
||||
{
|
||||
static int last_client_id=0;
|
||||
CActiveSocket* sock=new CActiveSocket;
|
||||
if(!sock->Initialize())
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
if(!sock->Open((const uint8_t*)ip.c_str(),port))
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
last_client_id++;
|
||||
clients[last_client_id]=sock;
|
||||
return last_client_id;
|
||||
}
|
||||
static void lua_socket_set_timeout(int server_id,int client_id,int32_t sec,int32_t msec)
|
||||
{
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
if(client_id==-1)
|
||||
{
|
||||
cur_server.socket->SetConnectTimeout(sec,msec);
|
||||
cur_server.socket->SetReceiveTimeout(sec,msec);
|
||||
cur_server.socket->SetSendTimeout(sec,msec);
|
||||
return;
|
||||
}
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
sock->SetConnectTimeout(sec,msec);
|
||||
sock->SetReceiveTimeout(sec,msec);
|
||||
sock->SetSendTimeout(sec,msec);
|
||||
}
|
||||
DFHACK_PLUGIN_LUA_FUNCTIONS {
|
||||
DFHACK_LUA_FUNCTION(lua_socket_bind), //spawn a server
|
||||
DFHACK_LUA_FUNCTION(lua_socket_connect),//spawn a client (i.e. connection)
|
||||
DFHACK_LUA_FUNCTION(lua_socket_set_timeout),
|
||||
DFHACK_LUA_FUNCTION(lua_server_accept),
|
||||
DFHACK_LUA_FUNCTION(lua_server_close),
|
||||
DFHACK_LUA_FUNCTION(lua_client_close),
|
||||
DFHACK_LUA_FUNCTION(lua_client_send),
|
||||
DFHACK_LUA_FUNCTION(lua_client_receive),
|
||||
DFHACK_LUA_END
|
||||
};
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
for(auto it=clients.begin();it!=clients.end();it++)
|
||||
{
|
||||
CActiveSocket* sock=it->second;
|
||||
sock->Close();
|
||||
delete sock;
|
||||
}
|
||||
clients.clear();
|
||||
for(auto it=servers.begin();it!=servers.end();it++)
|
||||
{
|
||||
it->second.close();
|
||||
}
|
||||
servers.clear();
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Screen.h"
|
||||
#include "VTableInterpose.h"
|
||||
#include "DFHackVersion.h"
|
||||
|
||||
#include "df/graphic.h"
|
||||
#include "df/viewscreen_titlest.h"
|
||||
#include "uicommon.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using namespace DFHack;
|
||||
|
||||
DFHACK_PLUGIN("title-version");
|
||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||
REQUIRE_GLOBAL(gps);
|
||||
|
||||
struct title_version_hook : df::viewscreen_titlest {
|
||||
typedef df::viewscreen_titlest interpose_base;
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, render, ())
|
||||
{
|
||||
INTERPOSE_NEXT(render)();
|
||||
int x = 0, y = 0;
|
||||
OutputString(COLOR_WHITE, x, y, string("DFHack ") + DFHACK_VERSION);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(title_version_hook, render);
|
||||
|
||||
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
|
||||
{
|
||||
if (!gps)
|
||||
return CR_FAILURE;
|
||||
|
||||
if (enable != is_enabled)
|
||||
{
|
||||
if (!INTERPOSE_HOOK(title_version_hook, render).apply(enable))
|
||||
return CR_FAILURE;
|
||||
|
||||
is_enabled = enable;
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, vector<PluginCommand> &commands)
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
|
||||
{
|
||||
INTERPOSE_HOOK(title_version_hook, render).remove();
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#include "df/viewscreen_setupdwarfgamest.h"
|
||||
using namespace DFHack;
|
||||
struct embark_profile_name_hook : df::viewscreen_setupdwarfgamest {
|
||||
typedef df::viewscreen_setupdwarfgamest interpose_base;
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
|
||||
{
|
||||
int ch = -1;
|
||||
for (auto it = input->begin(); ch == -1 && it != input->end(); ++it)
|
||||
ch = Screen::keyToChar(*it);
|
||||
if (in_save_profile && ch >= 32 && ch <= 126)
|
||||
{
|
||||
profile_name.push_back((char)ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input->count(df::interface_key::LEAVESCREEN))
|
||||
input->insert(df::interface_key::SETUPGAME_SAVE_PROFILE_ABORT);
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
}
|
||||
};
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(embark_profile_name_hook, feed);
|
@ -0,0 +1 @@
|
||||
Subproject commit ceed207e38220e21067a91b8d6f7b9680a476f69
|
@ -0,0 +1,2 @@
|
||||
include(Scripts.cmake)
|
||||
DFHACK_3RDPARTY_SCRIPT_REPO(lethosor)
|
@ -0,0 +1,19 @@
|
||||
include(../plugins/Plugins.cmake)
|
||||
|
||||
MACRO(DFHACK_SCRIPTS)
|
||||
PARSE_ARGUMENTS(SCRIPT
|
||||
"SUBDIRECTORY"
|
||||
"SOME_OPT"
|
||||
${ARGN}
|
||||
)
|
||||
CAR(SCRIPT_SUBDIRECTORY ${SCRIPT_SUBDIRECTORY})
|
||||
install(FILES ${SCRIPT_DEFAULT_ARGS}
|
||||
DESTINATION ${DFHACK_DATA_DESTINATION}/scripts/${SCRIPT_SUBDIRECTORY})
|
||||
ENDMACRO()
|
||||
|
||||
MACRO(DFHACK_3RDPARTY_SCRIPT_REPO repo_path)
|
||||
if(NOT EXISTS ${dfhack_SOURCE_DIR}/scripts/3rdparty/${repo_path}/CMakeLists.txt)
|
||||
MESSAGE(FATAL_ERROR "Script submodule scripts/3rdparty/${repo_path} does not exist - run `git submodule update`.")
|
||||
endif()
|
||||
add_subdirectory(3rdparty/${repo_path})
|
||||
ENDMACRO()
|
@ -0,0 +1,3 @@
|
||||
-- by Meph
|
||||
-- http://www.bay12forums.com/smf/index.php?topic=135506.msg4925005#msg4925005
|
||||
df.global.world.worldgen.worldgen_parms.embark_points=tonumber(...)
|
Loading…
Reference in New Issue