diff --git a/LUA_API.rst b/LUA_API.rst index b0f1a9f31..9fb37375d 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -726,7 +726,7 @@ Units module * ``dfhack.units.isDead(unit)`` - The unit is completely dead and passive. + The unit is completely dead and passive, or a ghost. * ``dfhack.units.isAlive(unit)`` @@ -734,7 +734,16 @@ Units module * ``dfhack.units.isSane(unit)`` - The unit is capable of rational action, i.e. not dead, insane or zombie. + The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf. + +* ``dfhack.units.isDwarf(unit)`` + + The unit is of the correct race of the fortress. + +* ``dfhack.units.isCitizen(unit)`` + + The unit is an alive sane citizen of the fortress; wraps the + same checks the game uses to decide game-over by extinction. * ``dfhack.units.getAge(unit[,true_age])`` diff --git a/Lua API.html b/Lua API.html index 4b763af25..1576652d6 100644 --- a/Lua API.html +++ b/Lua API.html @@ -960,13 +960,20 @@ a lua list containing them.
Returns the nemesis record of the unit if it has one, or nil.
dfhack.units.isDead(unit)
-The unit is completely dead and passive.
+The unit is completely dead and passive, or a ghost.
dfhack.units.isAlive(unit)
The unit isn't dead or undead.
dfhack.units.isSane(unit)
-The unit is capable of rational action, i.e. not dead, insane or zombie.
+The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.
+dfhack.units.isDwarf(unit)
+The unit is of the correct race of the fortress.
+dfhack.units.isCitizen(unit)
+The unit is an alive sane citizen of the fortress; wraps the +same checks the game uses to decide game-over by extinction.
dfhack.units.getAge(unit[,true_age])
Returns the age of the unit in years as a floating-point value.
diff --git a/depends/protobuf/CMakeLists.txt b/depends/protobuf/CMakeLists.txt
index dddefb06b..570c77b18 100644
--- a/depends/protobuf/CMakeLists.txt
+++ b/depends/protobuf/CMakeLists.txt
@@ -200,7 +200,9 @@ google/protobuf/compiler/zip_writer.cc
LIST(APPEND LIBPROTOBUF_FULL_SRCS ${LIBPROTOBUF_LITE_SRCS})
-SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Wno-sign-compare")
+IF(CMAKE_COMPILER_IS_GNUCC)
+ SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Wno-sign-compare")
+ENDIF()
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
SET(PROTOBUF_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/dfhack.init-example b/dfhack.init-example
index 7bceda2d0..4af4246fc 100644
--- a/dfhack.init-example
+++ b/dfhack.init-example
@@ -13,6 +13,9 @@ keybinding add Ctrl-Shift-K autodump-destroy-here
# any item:
keybinding add Ctrl-K autodump-destroy-item
+# quicksave, only in main dwarfmode screen and menu page
+keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
+
##############################
# Generic adv mode bindings #
##############################
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 32b7d9dad..c49ac6884 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -208,6 +208,8 @@ IF(UNIX)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32)
+ SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
+ PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
ENDIF()
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 6a550db87..8e8a3c4e3 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -715,6 +715,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
+ WRAPM(Units, isDwarf),
+ WRAPM(Units, isCitizen),
WRAPM(Units, getAge),
WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName),
diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp
index 36dbd9aae..972ab31b4 100644
--- a/library/VersionInfoFactory.cpp
+++ b/library/VersionInfoFactory.cpp
@@ -107,7 +107,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
}
else
{
- throw Error::SymbolsXmlBadAttribute("os-type");
+ return; // ignore it if it's invalid
}
// process additional entries
diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h
index aff04128c..637a532f8 100644
--- a/library/include/DataFuncs.h
+++ b/library/include/DataFuncs.h
@@ -160,6 +160,17 @@ INSTANTIATE_WRAPPERS(6, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6), (out,vA1,vA2,vA3,vA4,vA
#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7))
+INSTANTIATE_WRAPPERS(7, (A1,A2,A3,A4,A5,A6,A7), (vA1,vA2,vA3,vA4,vA5,vA6,vA7),
+ LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);
+ LOAD_ARG(A7);)
+INSTANTIATE_WRAPPERS(7, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6,A7), (out,vA1,vA2,vA3,vA4,vA5,vA6,vA7),
+ LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6); LOAD_ARG(A7);)
+#undef FW_TARGS
+
+#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8
+INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7,A8))
#undef FW_TARGS
#undef FW_TARGSC
diff --git a/library/include/modules/World.h b/library/include/modules/World.h
index e7b17ce41..81ebfbd60 100644
--- a/library/include/modules/World.h
+++ b/library/include/modules/World.h
@@ -37,16 +37,6 @@ distribution.
namespace DFHack
{
- /**
- * \ingroup grp_world
- */
- enum WeatherType
- {
- CLEAR,
- RAINING,
- SNOWING
- };
- typedef unsigned char weather_map [5][5];
/**
* \ingroup grp_world
*/
@@ -113,7 +103,6 @@ namespace DFHack
class DFHACK_EXPORT World : public Module
{
public:
- weather_map * wmap;
World();
~World();
bool Start();
diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp
index 2290110a8..63ba811c1 100644
--- a/library/modules/Gui.cpp
+++ b/library/modules/Gui.cpp
@@ -58,12 +58,14 @@ using namespace DFHack;
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_layer_assigntradest.h"
#include "df/viewscreen_layer_militaryst.h"
+#include "df/viewscreen_layer_stockpilest.h"
#include "df/viewscreen_petst.h"
#include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_storesst.h"
#include "df/ui_unit_view_mode.h"
#include "df/ui_sidebar_menus.h"
#include "df/ui_look_list.h"
+#include "df/ui_advmode.h"
#include "df/job.h"
#include "df/ui_build_selector.h"
#include "df/building_workshopst.h"
@@ -273,7 +275,9 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
break;
case Burrows:
- if (ui->burrows.in_add_units_mode)
+ if (ui->burrows.in_confirm_delete)
+ focus += "/ConfirmDelete";
+ else if (ui->burrows.in_add_units_mode)
focus += "/AddUnits";
else if (ui->burrows.in_edit_name_mode)
focus += "/EditName";
@@ -288,6 +292,16 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
}
}
+DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode)
+{
+ using df::global::ui_advmode;
+
+ if (!ui_advmode)
+ return;
+
+ focus += "/" + enum_item_key(ui_advmode->menu);
+}
+
DEFINE_GET_FOCUS_STRING_HANDLER(unitlist)
{
focus += "/" + enum_item_key(screen->page);
@@ -407,6 +421,35 @@ DEFINE_GET_FOCUS_STRING_HANDLER(stores)
focus += "/Items";
}
+DEFINE_GET_FOCUS_STRING_HANDLER(layer_stockpile)
+{
+ auto list1 = getLayerList(screen, 0);
+ auto list2 = getLayerList(screen, 1);
+ auto list3 = getLayerList(screen, 2);
+ if (!list1 || !list2 || !list3 || !screen->settings) return;
+
+ auto group = screen->cur_group;
+ if (group != vector_get(screen->group_ids, list1->cursor))
+ return;
+
+ focus += "/" + enum_item_key(group);
+
+ auto bits = vector_get(screen->group_bits, list1->cursor);
+ if (bits.whole && !(bits.whole & screen->settings->flags.whole))
+ {
+ focus += "/Off";
+ return;
+ }
+
+ focus += "/On";
+
+ if (list2->bright || list3->bright || screen->list_ids.empty()) {
+ focus += "/" + enum_item_key(screen->cur_list);
+
+ if (list3->bright)
+ focus += (screen->item_names.empty() ? "/None" : "/Item");
+ }
+}
std::string Gui::getFocusString(df::viewscreen *top)
{
@@ -998,16 +1041,29 @@ bool Gui::setDesignationCoords (const int32_t x, const int32_t y, const int32_t
bool Gui::getMousePos (int32_t & x, int32_t & y)
{
- x = gps->mouse_x;
- y = gps->mouse_y;
+ if (gps) {
+ x = gps->mouse_x;
+ y = gps->mouse_y;
+ }
+ else {
+ x = -1;
+ y = -1;
+ }
return (x == -1) ? false : true;
}
bool Gui::getWindowSize (int32_t &width, int32_t &height)
{
- width = gps->dimx;
- height = gps->dimy;
- return true;
+ if (gps) {
+ width = gps->dimx;
+ height = gps->dimy;
+ return true;
+ }
+ else {
+ width = 80;
+ height = 25;
+ return false;
+ }
}
bool Gui::getMenuWidth(uint8_t &menu_width, uint8_t &area_map_width)
diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp
index f49dd82e2..f8f99f81e 100644
--- a/library/modules/Materials.cpp
+++ b/library/modules/Materials.cpp
@@ -761,8 +761,8 @@ bool Materials::ReadCreatureTypesEx (void)
{
df::body_part_raw *bp = ca->body_info.body_parts[k];
t_bodypart part;
- part.id = bp->part_code;
- part.category = bp->part_name;
+ part.id = bp->token;
+ part.category = bp->category;
caste.bodypart.push_back(part);
}
using namespace df::enums::mental_attribute_type;
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index dd26109e7..ee383cc07 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -613,12 +613,45 @@ df::nemesis_record *Units::getNemesis(df::unit *unit)
return NULL;
}
+static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag)
+{
+ auto creature = df::creature_raw::find(race);
+ if (!creature)
+ return false;
+
+ auto craw = vector_get(creature->caste, caste);
+ if (!craw)
+ return false;
+
+ return craw->flags.is_set(flag);
+}
+
+static bool isCrazed(df::unit *unit)
+{
+ if (unit->flags3.bits.scuttle)
+ return false;
+ if (unit->curse.rem_tags1.bits.CRAZED)
+ return false;
+ if (unit->curse.add_tags1.bits.CRAZED)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CRAZED);
+}
+
+static bool isOpposedToLife(df::unit *unit)
+{
+ if (unit->curse.rem_tags1.bits.OPPOSED_TO_LIFE)
+ return false;
+ if (unit->curse.add_tags1.bits.OPPOSED_TO_LIFE)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CANNOT_UNDEAD);
+}
bool DFHack::Units::isDead(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->flags1.bits.dead;
+ return unit->flags1.bits.dead ||
+ unit->flags3.bits.ghostly;
}
bool DFHack::Units::isAlive(df::unit *unit)
@@ -636,8 +669,11 @@ bool DFHack::Units::isSane(df::unit *unit)
if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly ||
- unit->curse.add_tags1.bits.OPPOSED_TO_LIFE ||
- unit->curse.add_tags1.bits.CRAZED)
+ isOpposedToLife(unit) ||
+ unit->unknown8.unk2)
+ return false;
+
+ if (unit->unknown8.normal_race == unit->unknown8.were_race && isCrazed(unit))
return false;
switch (unit->mood)
@@ -657,19 +693,38 @@ bool DFHack::Units::isCitizen(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
+ // Copied from the conditions used to decide game over,
+ // except that the game appears to let melancholy/raving
+ // dwarves count as citizens.
+
+ if (!isDwarf(unit) || !isSane(unit))
+ return false;
+
+ if (unit->flags1.bits.marauder ||
+ unit->flags1.bits.invader_origin ||
+ unit->flags1.bits.active_invader ||
+ unit->flags1.bits.forest ||
+ unit->flags1.bits.merchant ||
+ unit->flags1.bits.diplomat)
+ return false;
+
+ if (unit->flags1.bits.tame)
+ return true;
+
return unit->civ_id == ui->civ_id &&
- !unit->flags1.bits.merchant &&
- !unit->flags1.bits.diplomat &&
+ unit->civ_id != -1 &&
+ !unit->flags2.bits.underworld &&
!unit->flags2.bits.resident &&
- !unit->flags1.bits.dead &&
- !unit->flags3.bits.ghostly;
+ !unit->flags2.bits.visitor_uninvited &&
+ !unit->flags2.bits.visitor;
}
bool DFHack::Units::isDwarf(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->race == ui->race_id;
+ return unit->race == ui->race_id ||
+ unit->unknown8.normal_race == ui->race_id;
}
double DFHack::Units::getAge(df::unit *unit, bool true_age)
diff --git a/library/modules/Windows.cpp b/library/modules/Windows.cpp
index e069d964e..218b6f7e0 100644
--- a/library/modules/Windows.cpp
+++ b/library/modules/Windows.cpp
@@ -34,10 +34,12 @@ distribution.
#include "modules/Windows.h"
using namespace DFHack;
+using df::global::gps;
Windows::df_screentile *Windows::getScreenBuffer()
{
- return (df_screentile *) df::global::gps->screen;
+ if (!gps) return NULL;
+ return (df_screentile *) gps->screen;
}
Windows::df_window::df_window(int x, int y, unsigned int width, unsigned int height)
@@ -79,7 +81,7 @@ bool Windows::df_window::unlock (painter * painter)
return false;
}
-Windows::top_level_window::top_level_window(): df_window(0,0,df::global::gps->dimx,df::global::gps->dimy)
+Windows::top_level_window::top_level_window() : df_window(0,0,gps ? gps->dimx : 80,gps ? gps->dimy : 25)
{
buffer = 0;
}
diff --git a/library/modules/World.cpp b/library/modules/World.cpp
index 6f6a69a59..4b50b44ca 100644
--- a/library/modules/World.cpp
+++ b/library/modules/World.cpp
@@ -63,15 +63,8 @@ struct World::Private
bool Inited;
bool PauseInited;
- void * pause_state_offset;
-
bool StartedWeather;
- char * weather_offset;
-
bool StartedMode;
- void * gamemode_offset;
- void * controlmode_offset;
- void * controlmodecopy_offset;
int next_persistent_id;
std::multimap