Merge branch 'develop' of https://github.com/DFHack/dfhack into develop

develop
Japa 2015-06-19 23:30:47 +05:30
commit 4182a30cc5
20 changed files with 817 additions and 386 deletions

@ -174,7 +174,7 @@ IF(BUILD_LIBRARY)
install(DIRECTORY images DESTINATION ${DFHACK_USERDOC_DESTINATION})
endif()
install(DIRECTORY dfhack-config DESTINATION .)
install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default)
#build the plugins
IF(BUILD_PLUGINS)

@ -27,11 +27,15 @@ DFHack Future
workflow: Fixed some issues with stuck jobs
- Note: Existing stuck jobs must be cancelled and re-added
Misc Improvements
dwarfmonitor date format can be modified (see Readme)
dwarfmonitor weather display can be configured separately
dwarfmonitor widgets' positions, formats, etc. are now customizable (see Readme)
dwarfmonitor weather display now separated from the date display
dwarfmonitor: New mouse cursor widget
gui/gm-editor: Pointers can now be displaced
gui/hack-wish: renamed to gui/create-item
"keybinding list" accepts a context
nyan: Can now be stopped with dfhack-run
quicksave: Restricted to fortress mode
search: Now supports the noble suggestion screen (e.g. suggesting a baron)
Removed
DFHack 0.40.24-r3

@ -1693,8 +1693,10 @@ Options:
``dwarfmonitor enable <mode>``:
Start monitoring ``mode``. ``mode`` can be "work", "misery", "weather", or "all".
This will enable all corresponding widgets, if applicable.
``dwarfmonitor disable <mode>``:
Stop monitoring ``mode`` (see above)
This will disable all corresponding widgets, if applicable.
``dwarfmonitor stats``:
Show statistics summary
``dwarfmonitor prefs``:
@ -1702,17 +1704,65 @@ Options:
``dwarfmonitor reload``:
Reload configuration file (``dfhack-config/dwarfmonitor.json``)
Configuration options:
Widget configuration:
``date_format``:
Date format
The following types of widgets (defined in ``hack/lua/plugins/dwarfmonitor.lua``)
can be displayed on the main fortress mode screen:
Example configuration::
* ``date``: Shows the in-game date
* ``misery``: Shows overall happiness levels of all dwarves
* ``weather``: Shows current weather (rain/snow)
* ``cursor``: Shows the current mouse cursor position
The file ``dfhack-config/dwarfmonitor.json`` can be edited to control the
positions and settings of all widgets displayed. This file should contain a
JSON object with the key ``widgets`` containing an array of objects - see the
included file in the ``dfhack-config`` folder for an example::
{
"date_format": "y-m-d"
"widgets": [
{
"type": "widget type (weather, misery, etc.)",
"x": X coordinate,
"y": Y coordinate
<...additional options...>
}
]
}
X and Y coordinates begin at zero (in the upper left corner of the screen).
Negative coordinates will be treated as distances from the lower right corner,
beginning at 1 - e.g. an x coordinate of 0 is the leftmost column, while an x
coordinate of 1 is the rightmost column.
By default, the x and y coordinates given correspond to the leftmost tile of
the widget. Including an ``anchor`` option set to ``right`` will cause the
rightmost tile of the widget to be located at this position instead.
Some widgets support additional options:
* ``date`` widget:
* ``format``: specifies the format of the date. The following characters
are replaced (all others, such as punctuation, are not modified)
* ``Y`` or ``y``: The current year
* ``M``: The current month, zero-padded if necessary
* ``m``: The current month, *not* zero-padded
* ``D``: The current day, zero-padded if necessary
* ``d``: The current day, *not* zero-padded
The default date format is ``Y-M-D``.
* ``cursor`` widget:
* ``format``: Specifies the format. ``X``, ``x``, ``Y``, and ``y`` are
replaced with the corresponding cursor cordinates, while all other
characters are unmodified.
* ``show_invalid``: If set to ``true``, the mouse coordinates will both be
displayed as ``-1`` when the cursor is outside of the DF window; otherwise,
nothing will be displayed.
seedwatch
---------
Watches the numbers of seeds available and enables/disables seed and plant cooking.
@ -2226,7 +2276,7 @@ gui/*
Scripts that implement dialogs inserted into the main game window are put in this
directory.
* gui/hack-wish
* gui/create-item
A graphical interface for creating items.

@ -1,3 +1,21 @@
{
"date_format": "y-m-d"
"widgets": [
{
"type": "weather",
"x": 1,
"y": -1
},
{
"type": "date",
"x": -30,
"y": 0,
"format": "Y-M-D"
},
{
"type": "misery",
"x": -2,
"y": -1,
"anchor": "right"
}
]
}

@ -2,11 +2,14 @@
# regenerate documentation after editing the .rst files. Requires python and docutils.
force=
reset=
while [ $# -gt 0 ]
do
case "$1" in
--force) force=1
;;
--reset) reset=1
;;
esac
shift
done
@ -32,7 +35,9 @@ cd `dirname $0`
status=0
function process() {
if [ "$1" -nt "$2" ] || [ -n "$force" ]; then
if [ -n "$reset" ]; then
git checkout -- "$2"
elif [ "$1" -nt "$2" ] || [ -n "$force" ]; then
echo -n "Updating $2... "
if "$rst2html" --no-generator --no-datestamp "$1" "$2"; then
echo "Done"

@ -1297,6 +1297,36 @@ bool Core::Init()
virtual_identity::Init(this);
init_screen_module(this);
// copy over default config files if necessary
std::vector<std::string> config_files;
std::vector<std::string> default_config_files;
if (Filesystem::listdir("dfhack-config", config_files) != 0)
con.printerr("Failed to list directory: dfhack-config");
else if (Filesystem::listdir("dfhack-config/default", default_config_files) != 0)
con.printerr("Failed to list directory: dfhack-config/default");
else
{
for (auto it = default_config_files.begin(); it != default_config_files.end(); ++it)
{
std::string filename = *it;
if (std::find(config_files.begin(), config_files.end(), filename) == config_files.end())
{
std::string src_file = std::string("dfhack-config/default/") + filename;
std::string dest_file = std::string("dfhack-config/") + filename;
std::ifstream src(src_file, std::ios::binary);
std::ofstream dest(dest_file, std::ios::binary);
if (!src.good() || !dest.good())
{
con.printerr("Copy failed: %s\n", filename.c_str());
continue;
}
dest << src.rdbuf();
src.close();
dest.close();
}
}
}
// initialize common lua context
Lua::Core::Init(con);

@ -451,6 +451,8 @@ local valid_script_flags = {
error = 'Cannot be used as a module'
},
module_strict = {required = false},
alias = {required = false},
alias_count = {required = false},
}
function dfhack.run_script(name,...)
@ -497,6 +499,13 @@ function dfhack.run_script_with_env(envVars, name, flags, ...)
scripts[file] = Script(file)
end
local script_flags = scripts[file]:get_flags()
if script_flags.alias then
flags.alias_count = (flags.alias_count or 0) + 1
if flags.alias_count > 10 then
error('Too many script aliases: ' .. flags.alias_count)
end
return dfhack.run_script_with_env(envVars, script_flags.alias, flags, ...)
end
for flag, value in pairs(flags) do
if value then
local v = valid_script_flags[flag]

@ -36,7 +36,7 @@ function decode_file(path, ...)
if not f then
error('Could not open ' .. path)
end
local raw = f:read('*all')
local contents = f:read('*all')
f:close()
return decode(contents, ...)
end

@ -1 +1 @@
Subproject commit 8cd1994027d023f32e7eb6e424232fc30d37fb90
Subproject commit fa77e361c48ec75accbb0210fdb4f243ade1eb13

@ -112,7 +112,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(digFlood digFlood.cpp)
add_subdirectory(diggingInvaders)
DFHACK_PLUGIN(drybuckets drybuckets.cpp)
DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp LINK_LIBRARIES jsonxx)
DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp LINK_LIBRARIES jsonxx lua)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)

@ -157,26 +157,30 @@ static const char *state_names[] = {
"BUSY",
"MILITARY",
"CHILD",
"OTHER",
"OTHER"
};
// List of possible activites of a dwarf that will be further narrowed to states
// IDLE - Specifically waiting to be assigned a task (No Job)
// BUSY - Performing a toggleable labor, or a support action for that labor.
// OTHER - Doing something else
static const dwarf_state dwarf_states[] = {
BUSY /* CarveFortification */,
BUSY /* DetailWall */,
BUSY /* DetailFloor */,
BUSY /* Dig */,
BUSY /* CarveUpwardStaircase */,
BUSY /* CarveDownwardStaircase */,
BUSY /* CarveUpDownStaircase */,
BUSY /* CarveRamp */,
BUSY /* DigChannel */,
BUSY /* FellTree */,
BUSY /* GatherPlants */,
BUSY /* RemoveConstruction */,
BUSY /* CollectWebs */,
BUSY /* BringItemToDepot */,
BUSY /* BringItemToShop */,
BUSY /* CarveFortification */,
BUSY /* DetailWall */,
BUSY /* DetailFloor */,
BUSY /* Dig */,
BUSY /* CarveUpwardStaircase */,
BUSY /* CarveDownwardStaircase */,
BUSY /* CarveUpDownStaircase */,
BUSY /* CarveRamp */,
BUSY /* DigChannel */,
BUSY /* FellTree */,
BUSY /* GatherPlants */,
BUSY /* RemoveConstruction */,
BUSY /* CollectWebs */,
BUSY /* BringItemToDepot */,
BUSY /* BringItemToShop */,
OTHER /* Eat */,
OTHER /* GetProvisions */,
OTHER /* Drink */,
@ -184,39 +188,39 @@ static const dwarf_state dwarf_states[] = {
OTHER /* FillWaterskin */,
OTHER /* FillWaterskin2 */,
OTHER /* Sleep */,
BUSY /* CollectSand */,
BUSY /* Fish */,
BUSY /* Hunt */,
OTHER /* HuntVermin */,
BUSY /* Kidnap */,
BUSY /* BeatCriminal */,
BUSY /* StartingFistFight */,
BUSY /* CollectTaxes */,
BUSY /* GuardTaxCollector */,
BUSY /* CatchLiveLandAnimal */,
BUSY /* CatchLiveFish */,
BUSY /* ReturnKill */,
BUSY /* CheckChest */,
BUSY /* StoreOwnedItem */,
BUSY /* PlaceItemInTomb */,
BUSY /* StoreItemInStockpile */,
BUSY /* StoreItemInBag */,
BUSY /* StoreItemInHospital */,
BUSY /* StoreItemInChest */,
BUSY /* StoreItemInCabinet */,
BUSY /* StoreWeapon */,
BUSY /* StoreArmor */,
BUSY /* StoreItemInBarrel */,
BUSY /* StoreItemInBin */,
BUSY /* SeekArtifact */,
BUSY /* SeekInfant */,
BUSY /* CollectSand */,
BUSY /* Fish */,
BUSY /* Hunt */,
BUSY /* HuntVermin */,
OTHER /* Kidnap */,
OTHER /* BeatCriminal */,
OTHER /* StartingFistFight */,
OTHER /* CollectTaxes */,
OTHER /* GuardTaxCollector */,
BUSY /* CatchLiveLandAnimal */,
BUSY /* CatchLiveFish */,
OTHER /* ReturnKill */,
OTHER /* CheckChest */,
OTHER /* StoreOwnedItem */,
BUSY /* PlaceItemInTomb */,
BUSY /* StoreItemInStockpile */,
BUSY /* StoreItemInBag */,
BUSY /* StoreItemInHospital */,
BUSY /* StoreItemInChest */,
BUSY /* StoreItemInCabinet */,
BUSY /* StoreWeapon */,
BUSY /* StoreArmor */,
BUSY /* StoreItemInBarrel */,
BUSY /* StoreItemInBin */,
OTHER /* SeekArtifact */,
OTHER /* SeekInfant */,
OTHER /* AttendParty */,
OTHER /* GoShopping */,
OTHER /* GoShopping2 */,
BUSY /* Clean */,
OTHER /* Clean */,
OTHER /* Rest */,
BUSY /* PickupEquipment */,
BUSY /* DumpItem */,
BUSY /* PickupEquipment */,
BUSY /* DumpItem */,
OTHER /* StrangeMoodCrafter */,
OTHER /* StrangeMoodJeweller */,
OTHER /* StrangeMoodForge */,
@ -230,170 +234,170 @@ static const dwarf_state dwarf_states[] = {
OTHER /* StrangeMoodWeaver */,
OTHER /* StrangeMoodGlassmaker */,
OTHER /* StrangeMoodMechanics */,
BUSY /* ConstructBuilding */,
BUSY /* ConstructDoor */,
BUSY /* ConstructFloodgate */,
BUSY /* ConstructBed */,
BUSY /* ConstructThrone */,
BUSY /* ConstructCoffin */,
BUSY /* ConstructTable */,
BUSY /* ConstructChest */,
BUSY /* ConstructBin */,
BUSY /* ConstructArmorStand */,
BUSY /* ConstructWeaponRack */,
BUSY /* ConstructCabinet */,
BUSY /* ConstructStatue */,
BUSY /* ConstructBlocks */,
BUSY /* MakeRawGlass */,
BUSY /* MakeCrafts */,
BUSY /* MintCoins */,
BUSY /* CutGems */,
BUSY /* CutGlass */,
BUSY /* EncrustWithGems */,
BUSY /* EncrustWithGlass */,
BUSY /* DestroyBuilding */,
BUSY /* SmeltOre */,
BUSY /* MeltMetalObject */,
BUSY /* ExtractMetalStrands */,
BUSY /* PlantSeeds */,
BUSY /* HarvestPlants */,
BUSY /* TrainHuntingAnimal */,
BUSY /* TrainWarAnimal */,
BUSY /* MakeWeapon */,
BUSY /* ForgeAnvil */,
BUSY /* ConstructCatapultParts */,
BUSY /* ConstructBallistaParts */,
BUSY /* MakeArmor */,
BUSY /* MakeHelm */,
BUSY /* MakePants */,
BUSY /* StudWith */,
BUSY /* ButcherAnimal */,
BUSY /* PrepareRawFish */,
BUSY /* MillPlants */,
BUSY /* BaitTrap */,
BUSY /* MilkCreature */,
BUSY /* MakeCheese */,
BUSY /* ProcessPlants */,
BUSY /* ProcessPlantsBag */,
BUSY /* ProcessPlantsVial */,
BUSY /* ProcessPlantsBarrel */,
BUSY /* PrepareMeal */,
BUSY /* WeaveCloth */,
BUSY /* MakeGloves */,
BUSY /* MakeShoes */,
BUSY /* MakeShield */,
BUSY /* MakeCage */,
BUSY /* MakeChain */,
BUSY /* MakeFlask */,
BUSY /* MakeGoblet */,
BUSY /* MakeInstrument */,
BUSY /* MakeToy */,
BUSY /* MakeAnimalTrap */,
BUSY /* MakeBarrel */,
BUSY /* MakeBucket */,
BUSY /* MakeWindow */,
BUSY /* MakeTotem */,
BUSY /* MakeAmmo */,
BUSY /* DecorateWith */,
BUSY /* MakeBackpack */,
BUSY /* MakeQuiver */,
BUSY /* MakeBallistaArrowHead */,
BUSY /* AssembleSiegeAmmo */,
BUSY /* LoadCatapult */,
BUSY /* LoadBallista */,
BUSY /* FireCatapult */,
BUSY /* FireBallista */,
BUSY /* ConstructMechanisms */,
BUSY /* MakeTrapComponent */,
BUSY /* LoadCageTrap */,
BUSY /* LoadStoneTrap */,
BUSY /* LoadWeaponTrap */,
BUSY /* CleanTrap */,
BUSY /* CastSpell */,
BUSY /* LinkBuildingToTrigger */,
BUSY /* PullLever */,
BUSY /* BrewDrink */,
BUSY /* ExtractFromPlants */,
BUSY /* ExtractFromRawFish */,
BUSY /* ExtractFromLandAnimal */,
BUSY /* TameVermin */,
BUSY /* TameAnimal */,
BUSY /* ChainAnimal */,
BUSY /* UnchainAnimal */,
BUSY /* UnchainPet */,
BUSY /* ReleaseLargeCreature */,
BUSY /* ReleasePet */,
BUSY /* ReleaseSmallCreature */,
BUSY /* HandleSmallCreature */,
BUSY /* HandleLargeCreature */,
BUSY /* CageLargeCreature */,
BUSY /* CageSmallCreature */,
BUSY /* RecoverWounded */,
BUSY /* DiagnosePatient */,
BUSY /* ImmobilizeBreak */,
BUSY /* DressWound */,
BUSY /* CleanPatient */,
BUSY /* Surgery */,
BUSY /* Suture */,
BUSY /* SetBone */,
BUSY /* PlaceInTraction */,
BUSY /* DrainAquarium */,
BUSY /* FillAquarium */,
BUSY /* FillPond */,
BUSY /* GiveWater */,
BUSY /* GiveFood */,
BUSY /* GiveWater2 */,
BUSY /* GiveFood2 */,
BUSY /* RecoverPet */,
BUSY /* PitLargeAnimal */,
BUSY /* PitSmallAnimal */,
BUSY /* SlaughterAnimal */,
BUSY /* MakeCharcoal */,
BUSY /* MakeAsh */,
BUSY /* MakeLye */,
BUSY /* MakePotashFromLye */,
BUSY /* FertilizeField */,
BUSY /* MakePotashFromAsh */,
BUSY /* DyeThread */,
BUSY /* DyeCloth */,
BUSY /* SewImage */,
BUSY /* MakePipeSection */,
BUSY /* OperatePump */,
BUSY /* ConstructBuilding */,
BUSY /* ConstructDoor */,
BUSY /* ConstructFloodgate */,
BUSY /* ConstructBed */,
BUSY /* ConstructThrone */,
BUSY /* ConstructCoffin */,
BUSY /* ConstructTable */,
BUSY /* ConstructChest */,
BUSY /* ConstructBin */,
BUSY /* ConstructArmorStand */,
BUSY /* ConstructWeaponRack */,
BUSY /* ConstructCabinet */,
BUSY /* ConstructStatue */,
BUSY /* ConstructBlocks */,
BUSY /* MakeRawGlass */,
BUSY /* MakeCrafts */,
BUSY /* MintCoins */,
BUSY /* CutGems */,
BUSY /* CutGlass */,
BUSY /* EncrustWithGems */,
BUSY /* EncrustWithGlass */,
BUSY /* DestroyBuilding */,
BUSY /* SmeltOre */,
BUSY /* MeltMetalObject */,
BUSY /* ExtractMetalStrands */,
BUSY /* PlantSeeds */,
BUSY /* HarvestPlants */,
BUSY /* TrainHuntingAnimal */,
BUSY /* TrainWarAnimal */,
BUSY /* MakeWeapon */,
BUSY /* ForgeAnvil */,
BUSY /* ConstructCatapultParts */,
BUSY /* ConstructBallistaParts */,
BUSY /* MakeArmor */,
BUSY /* MakeHelm */,
BUSY /* MakePants */,
BUSY /* StudWith */,
BUSY /* ButcherAnimal */,
BUSY /* PrepareRawFish */,
BUSY /* MillPlants */,
BUSY /* BaitTrap */,
BUSY /* MilkCreature */,
BUSY /* MakeCheese */,
BUSY /* ProcessPlants */,
BUSY /* ProcessPlantsBag */,
BUSY /* ProcessPlantsVial */,
BUSY /* ProcessPlantsBarrel */,
BUSY /* PrepareMeal */,
BUSY /* WeaveCloth */,
BUSY /* MakeGloves */,
BUSY /* MakeShoes */,
BUSY /* MakeShield */,
BUSY /* MakeCage */,
BUSY /* MakeChain */,
BUSY /* MakeFlask */,
BUSY /* MakeGoblet */,
BUSY /* MakeInstrument */,
BUSY /* MakeToy */,
BUSY /* MakeAnimalTrap */,
BUSY /* MakeBarrel */,
BUSY /* MakeBucket */,
BUSY /* MakeWindow */,
BUSY /* MakeTotem */,
BUSY /* MakeAmmo */,
BUSY /* DecorateWith */,
BUSY /* MakeBackpack */,
BUSY /* MakeQuiver */,
BUSY /* MakeBallistaArrowHead */,
BUSY /* AssembleSiegeAmmo */,
BUSY /* LoadCatapult */,
BUSY /* LoadBallista */,
BUSY /* FireCatapult */,
BUSY /* FireBallista */,
BUSY /* ConstructMechanisms */,
BUSY /* MakeTrapComponent */,
BUSY /* LoadCageTrap */,
BUSY /* LoadStoneTrap */,
BUSY /* LoadWeaponTrap */,
BUSY /* CleanTrap */,
OTHER /* CastSpell */,
BUSY /* LinkBuildingToTrigger */,
BUSY /* PullLever */,
BUSY /* BrewDrink */,
BUSY /* ExtractFromPlants */,
BUSY /* ExtractFromRawFish */,
BUSY /* ExtractFromLandAnimal */,
BUSY /* TameVermin */,
BUSY /* TameAnimal */,
BUSY /* ChainAnimal */,
BUSY /* UnchainAnimal */,
BUSY /* UnchainPet */,
BUSY /* ReleaseLargeCreature */,
BUSY /* ReleasePet */,
BUSY /* ReleaseSmallCreature */,
BUSY /* HandleSmallCreature */,
BUSY /* HandleLargeCreature */,
BUSY /* CageLargeCreature */,
BUSY /* CageSmallCreature */,
BUSY /* RecoverWounded */,
BUSY /* DiagnosePatient */,
BUSY /* ImmobilizeBreak */,
BUSY /* DressWound */,
BUSY /* CleanPatient */,
BUSY /* Surgery */,
BUSY /* Suture */,
BUSY /* SetBone */,
BUSY /* PlaceInTraction */,
BUSY /* DrainAquarium */,
BUSY /* FillAquarium */,
BUSY /* FillPond */,
BUSY /* GiveWater */,
BUSY /* GiveFood */,
BUSY /* GiveWater2 */,
BUSY /* GiveFood2 */,
BUSY /* RecoverPet */,
BUSY /* PitLargeAnimal */,
BUSY /* PitSmallAnimal */,
BUSY /* SlaughterAnimal */,
BUSY /* MakeCharcoal */,
BUSY /* MakeAsh */,
BUSY /* MakeLye */,
BUSY /* MakePotashFromLye */,
BUSY /* FertilizeField */,
BUSY /* MakePotashFromAsh */,
BUSY /* DyeThread */,
BUSY /* DyeCloth */,
BUSY /* SewImage */,
BUSY /* MakePipeSection */,
BUSY /* OperatePump */,
OTHER /* ManageWorkOrders */,
OTHER /* UpdateStockpileRecords */,
OTHER /* TradeAtDepot */,
BUSY /* ConstructHatchCover */,
BUSY /* ConstructGrate */,
BUSY /* RemoveStairs */,
BUSY /* ConstructQuern */,
BUSY /* ConstructMillstone */,
BUSY /* ConstructSplint */,
BUSY /* ConstructCrutch */,
BUSY /* ConstructTractionBench */,
BUSY /* CleanSelf */,
BUSY /* BringCrutch */,
BUSY /* ApplyCast */,
BUSY /* CustomReaction */,
BUSY /* ConstructSlab */,
BUSY /* EngraveSlab */,
BUSY /* ShearCreature */,
BUSY /* SpinThread */,
BUSY /* PenLargeAnimal */,
BUSY /* PenSmallAnimal */,
BUSY /* MakeTool */,
BUSY /* CollectClay */,
BUSY /* InstallColonyInHive */,
BUSY /* CollectHiveProducts */,
BUSY /* ConstructHatchCover */,
BUSY /* ConstructGrate */,
BUSY /* RemoveStairs */,
BUSY /* ConstructQuern */,
BUSY /* ConstructMillstone */,
BUSY /* ConstructSplint */,
BUSY /* ConstructCrutch */,
BUSY /* ConstructTractionBench */,
OTHER /* CleanSelf */,
BUSY /* BringCrutch */,
BUSY /* ApplyCast */,
BUSY /* CustomReaction */,
BUSY /* ConstructSlab */,
BUSY /* EngraveSlab */,
BUSY /* ShearCreature */,
BUSY /* SpinThread */,
BUSY /* PenLargeAnimal */,
BUSY /* PenSmallAnimal */,
BUSY /* MakeTool */,
BUSY /* CollectClay */,
BUSY /* InstallColonyInHive */,
BUSY /* CollectHiveProducts */,
OTHER /* CauseTrouble */,
OTHER /* DrinkBlood */,
OTHER /* ReportCrime */,
OTHER /* ExecuteCriminal */,
BUSY /* TrainAnimal */,
BUSY /* CarveTrack */,
BUSY /* PushTrackVehicle */,
BUSY /* PlaceTrackVehicle */,
BUSY /* StoreItemInVehicle */,
BUSY /* GeldAnimal */
BUSY /* TrainAnimal */,
BUSY /* CarveTrack */,
BUSY /* PushTrackVehicle */,
BUSY /* PlaceTrackVehicle */,
BUSY /* StoreItemInVehicle */,
BUSY /* GeldAnimal */
};
// Mode assigned to labors. Either it's a hauling job, or it's not.
@ -426,6 +430,7 @@ struct labor_info
// Return the labor_mode associated with this labor
labor_mode mode() { return (labor_mode) config.ival(0); }
// Set the labor_mode associated with this labor
void set_mode(labor_mode mode) { config.ival(0) = mode; }
};
@ -458,7 +463,7 @@ static const struct labor_default default_labor_infos[] = {
/* BONE_SETTING */ {ALLOW, 0},
/* SUTURING */ {ALLOW, 0},
/* DRESSING_WOUNDS */ {ALLOW, 0},
/* FEED_WATER_CIVILIANS */ {ALLOW, 0},
/* FEED_WATER_CIVILIANS */ {HAULERS, 0}, // This could also be ALLOW
/* RECOVER_WOUNDED */ {HAULERS, 0},
/* BUTCHER */ {ALLOW, 0},
/* TRAPPER */ {ALLOW, 0},
@ -534,6 +539,7 @@ struct dwarf_info_t
{
// Current simplified employment status of dwarf
dwarf_state state;
// Set to true if for whatever reason we are exempting this dwarf
// from hauling
bool haul_exempt;
@ -704,6 +710,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
" List current status of all labors.\n"
" autohauler status\n"
" Show basic status information.\n"
" autohauler debug\n"
" In the next cycle, will output the state of every dwarf.\n"
"Function:\n"
" When enabled, autohauler periodically checks your dwarves and assigns\n"
" hauling jobs to idle dwarves while removing them from busy dwarves.\n"
@ -818,6 +826,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
/* Before determining how to handle employment status, handle
* hauling exemptions first */
// Default deny condition of on break for later else-if series
bool is_migrant = false;
// Scan every labor. If a labor that disallows hauling is present
// for the dwarf, the dwarf is hauling exempt
FOR_ENUM_ITEMS(unit_labor, labor)
@ -836,8 +847,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// to try to find real jobs first
for (auto p = dwarfs[dwarf]->status.misc_traits.begin(); p < dwarfs[dwarf]->status.misc_traits.end(); p++)
{
if ((*p)->id == misc_trait_type::Migrant || (*p)->id == misc_trait_type::OnBreak)
dwarf_info[dwarf].haul_exempt = true;
if ((*p)->id == misc_trait_type::Migrant)
is_migrant = true;
}
/* Now determine a dwarf's employment status and decide whether
@ -852,17 +863,24 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// Account for any hauling exemptions here
else if (dwarf_info[dwarf].haul_exempt)
{
dwarf_info[dwarf].state = OTHER;
dwarf_info[dwarf].state = BUSY;
}
// Don't give hauling jobs to the military either
// Account for the military
else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession))
dwarf_info[dwarf].state = MILITARY;
// Account for dwarves on break or migrants
// DF leaves the OnBreak trait type on some dwarves while they're not actually on break
// Since they have no current job, they will default to IDLE
else if (is_migrant)
// Dwarf is unemployed with null job
{
dwarf_info[dwarf].state = OTHER;
}
else if (dwarfs[dwarf]->job.current_job == NULL)
{
dwarf_info[dwarf].state = IDLE;
}
// If it gets to this point the dwarf is employed
// If it gets to this point we look at the task and assign either BUSY or OTHER
else
{
int job = dwarfs[dwarf]->job.current_job->job_type;
@ -870,17 +888,25 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
dwarf_info[dwarf].state = dwarf_states[job];
else
{
// Warn the console that the dwarf has an unregistered labor, default to OTHER
// Warn the console that the dwarf has an unregistered labor, default to BUSY
out.print("Dwarf %i \"%s\" has unknown job %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), job);
dwarf_info[dwarf].state = OTHER;
dwarf_info[dwarf].state = BUSY;
}
}
// Debug: Output dwarf job and state data
if(print_debug)
out.print("Dwarf %i %s State: %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(),
dwarf_info[dwarf].state);
// Increment corresponding labor in default_labor_infos struct
state_count[dwarf_info[dwarf].state]++;
}
// At this point the debug if present has been completed
print_debug = false;
// This is a vector of all the labors
std::vector<df::unit_labor> labors;
@ -902,33 +928,23 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// have "real" jobs filled first, then if nothing is available the dwarf
// instead resorts to hauling.
// IDLE - Enable hauling
// BUSY - Disable hauling
// OTHER - Disable hauling
// This is a vector of potential hauler IDs
std::vector<int> hauler_ids;
// IDLE - Enable hauling
// BUSY - Disable hauling
// OTHER - Enable hauling
// MILITARY - Enable hauling
// Pretty much we are only considering non-military, non-child dwarves
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
if (dwarf_info[dwarf].state == IDLE ||
dwarf_info[dwarf].state == BUSY ||
dwarf_info[dwarf].state == OTHER)
{
hauler_ids.push_back(dwarf);
}
}
// There was no reason to put potential haulers in an array. All of them are
// covered in the following for loop.
// Equivalent of Java for(unit_labor : labor)
// For every labor...
FOR_ENUM_ITEMS(unit_labor, labor)
{
// If this is a non-labor skip this for loop
// If this is a non-labor continue to next item
if (labor == unit_labor::NONE)
continue;
// If this is not a hauling labor then skip this for loop
// If this is not a hauling labor continue to next item
if (labor_infos[labor].mode() != HAULERS)
continue;
@ -936,15 +952,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
for(int dwarf = 0; dwarf < dwarfs.size(); dwarf++)
{
// If the dwarf is idle, enable the hauling labor
if(dwarf_info[dwarf].state == IDLE)
{
// And enable the job for the dwarf
// Set hauling labors based on employment states
if(dwarf_info[dwarf].state == IDLE) {
dwarfs[dwarf]->status.labors[labor] = true;
}
// If the dwarf is busy, disable the hauling labor
if(dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == OTHER)
{
else if(dwarf_info[dwarf].state == MILITARY) {
dwarfs[dwarf]->status.labors[labor] = true;
}
else if(dwarf_info[dwarf].state == OTHER) {
dwarfs[dwarf]->status.labors[labor] = true;
}
else if(dwarf_info[dwarf].state == BUSY) {
dwarfs[dwarf]->status.labors[labor] = false;
}
// If at the end of this the dwarf has the hauling labor, increment the
@ -954,6 +972,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
labor_infos[labor].active_dwarfs++;
}
// CHILD ignored
}
// Let's play a game of "find the missing bracket!" I hope this is correct.

@ -10,6 +10,8 @@
#include "df/misc_trait_type.h"
#include "df/unit_misc_trait.h"
#include "LuaTools.h"
#include "LuaWrapper.h"
#include "modules/Gui.h"
#include "modules/Units.h"
#include "modules/Translation.h"
@ -96,6 +98,8 @@ static color_value monitor_colors[] =
static int get_happiness_cat(df::unit *unit)
{
if (!unit || !unit->status.current_soul)
return 3;
int stress = unit->status.current_soul->personality.stress_level;
if (stress >= 500000)
return 0;
@ -150,6 +154,113 @@ static void move_cursor(df::coord &pos)
static void open_stats_srceen();
namespace dm_lua {
static color_ostream_proxy *out;
typedef int(*initializer)(lua_State*);
int no_args (lua_State *L) { return 0; }
void cleanup()
{
if (out)
{
delete out;
out = NULL;
}
}
bool init_call (lua_State *L, const char *func)
{
if (!out)
out = new color_ostream_proxy(Core::getInstance().getConsole());
return Lua::PushModulePublic(*out, L, "plugins.dwarfmonitor", func);
}
bool safe_call (lua_State *L, int nargs)
{
return Lua::SafeCall(*out, L, nargs, 0);
}
bool call (const char *func, initializer init = no_args)
{
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
if (!init_call(L, func))
return false;
int nargs = init(L);
return safe_call(L, nargs);
}
template <typename KeyType, typename ValueType>
void table_set (lua_State *L, KeyType k, ValueType v)
{
Lua::Push(L, k);
Lua::Push(L, v);
lua_settable(L, -3);
}
namespace api {
int monitor_state (lua_State *L)
{
std::string type = luaL_checkstring(L, 1);
if (type == "weather")
lua_pushboolean(L, monitor_weather);
else if (type == "misery")
lua_pushboolean(L, monitor_misery);
else if (type == "date")
lua_pushboolean(L, monitor_date);
else
lua_pushnil(L);
return 1;
}
int get_weather_counts (lua_State *L)
{
#define WEATHER_TYPES WTYPE(clear, None); WTYPE(rain, Rain); WTYPE(snow, Snow);
#define WTYPE(type, name) int type = 0;
WEATHER_TYPES
#undef WTYPE
int i, j;
for (i = 0; i < 5; ++i)
{
for (j = 0; j < 5; ++j)
{
switch ((*current_weather)[i][j])
{
#define WTYPE(type, name) case weather_type::name: type++; break;
WEATHER_TYPES
#undef WTYPE
}
}
}
lua_newtable(L);
#define WTYPE(type, name) dm_lua::table_set(L, #type, type);
WEATHER_TYPES
#undef WTYPE
#undef WEATHER_TYPES
return 1;
}
int get_misery_data (lua_State *L)
{
lua_newtable(L);
for (int i = 0; i < 7; i++)
{
Lua::Push(L, i);
lua_newtable(L);
dm_lua::table_set(L, "value", misery[i]);
dm_lua::table_set(L, "color", monitor_colors[i]);
dm_lua::table_set(L, "last", (i == 6));
lua_settable(L, -3);
}
return 1;
}
}
}
#define DM_LUA_FUNC(name) { #name, df::wrap_function(dm_lua::api::name, true) }
#define DM_LUA_CMD(name) { #name, dm_lua::api::name }
DFHACK_PLUGIN_LUA_COMMANDS {
DM_LUA_CMD(monitor_state),
DM_LUA_CMD(get_weather_counts),
DM_LUA_CMD(get_misery_data),
DFHACK_LUA_END
};
#define JOB_IDLE -1
#define JOB_UNKNOWN -2
#define JOB_MILITARY -3
@ -1699,97 +1810,7 @@ struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest
if (Maps::IsValid())
{
if (monitor_misery)
{
string entries[7];
size_t length = 9;
for (int i = 0; i < 7; i++)
{
entries[i] = int_to_string(misery[i]);
length += entries[i].length();
}
int x = gps->dimx - length;
int y = gps->dimy - 1;
OutputString(COLOR_WHITE, x, y, "H:");
for (int i = 0; i < 7; i++)
{
OutputString(monitor_colors[i], x, y, entries[i]);
if (i < 6)
OutputString(COLOR_WHITE, x, y, "/");
}
}
if (monitor_date)
{
int x = gps->dimx - 30;
int y = 0;
ostringstream date_str;
auto month = World::ReadCurrentMonth() + 1;
auto day = World::ReadCurrentDay();
date_str << "Date:";
for (size_t i = 0; i < dwarfmonitor_config.date_format.size(); i++)
{
char c = dwarfmonitor_config.date_format[i];
switch (c)
{
case 'Y':
case 'y':
date_str << World::ReadCurrentYear();
break;
case 'M':
case 'm':
date_str << ((month < 10) ? "0" : "") << month;
break;
case 'D':
case 'd':
date_str << ((day < 10) ? "0" : "") << day;
break;
default:
date_str << c;
break;
}
}
OutputString(COLOR_GREY, x, y, date_str.str());
}
if (monitor_weather)
{
int x = 1;
int y = gps->dimy - 1;
bool rain = false,
snow = false;
if (current_weather)
{
int i, j;
for (i = 0; i < 5; ++i)
{
for (j = 0; j < 5; ++j)
{
switch ((*current_weather)[i][j])
{
case weather_type::Rain:
rain = true;
break;
case weather_type::Snow:
snow = true;
break;
default:
break;
}
}
}
}
if (rain)
{
OutputString(COLOR_LIGHTBLUE, x, y, "Rain");
++x;
}
if (snow)
OutputString(COLOR_WHITE, x, y, "Snow");
}
dm_lua::call("render_all");
}
}
};
@ -1811,17 +1832,17 @@ static bool set_monitoring_mode(const string &mode, const bool &state)
if (!monitor_jobs)
reset();
}
else if (mode == "misery" || mode == "all")
if (mode == "misery" || mode == "all")
{
mode_recognized = true;
monitor_misery = state;
}
else if (mode == "date" || mode == "all")
if (mode == "date" || mode == "all")
{
mode_recognized = true;
monitor_date = state;
}
else if (mode == "weather" || mode == "all")
if (mode == "weather" || mode == "all")
{
mode_recognized = true;
monitor_weather = state;
@ -1830,11 +1851,15 @@ static bool set_monitoring_mode(const string &mode, const bool &state)
return mode_recognized;
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
static bool load_config()
{
if (!gps)
return CR_FAILURE;
return dm_lua::call("load_config");
}
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enable)
load_config();
if (is_enabled != enable)
{
if (!INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply(enable) ||
@ -1848,19 +1873,6 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
return CR_OK;
}
static bool load_config (color_ostream &out)
{
jsonxx::Object o;
std::ifstream infile("dfhack-config/dwarfmonitor.json");
if (infile.good())
{
std::string contents((std::istreambuf_iterator<char>(infile)), (std::istreambuf_iterator<char>()));
if (!o.parse(contents))
out.printerr("dwarfmonitor: invalid JSON\n");
}
dwarfmonitor_config.date_format = o.get<jsonxx::String>("date_format", "y-m-d");
}
static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & parameters)
{
bool show_help = false;
@ -1913,7 +1925,7 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & par
}
else if (cmd == 'r' || cmd == 'R')
{
load_config(out);
load_config();
}
else
{
@ -1929,7 +1941,6 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & par
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands)
{
load_config(out);
activity_labels[JOB_IDLE] = "Idle";
activity_labels[JOB_MILITARY] = "Military Duty";
activity_labels[JOB_LEISURE] = "Leisure";
@ -1962,12 +1973,18 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
"dwarfmonitor prefs\n"
" Show dwarf preferences summary\n\n"
"dwarfmonitor reload\n"
" Reload configuration file (hack/config/dwarfmonitor.json)\n"
" Reload configuration file (dfhack-config/dwarfmonitor.json)\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown(color_ostream &out)
{
dm_lua::cleanup();
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {

@ -0,0 +1,175 @@
local _ENV = mkmodule('plugins.dwarfmonitor')
local gps = df.global.gps
local gui = require 'gui'
config = {}
widgets = {}
function dmerror(desc)
qerror('dwarfmonitor: ' .. tostring(desc))
end
Widget = defclass(Widget)
function Widget:init(opts)
self.opts = opts
end
function Widget:get_pos()
local x = self.opts.x >= 0 and self.opts.x or gps.dimx + self.opts.x
local y = self.opts.y >= 0 and self.opts.y or gps.dimy + self.opts.y
if self.opts.anchor == 'right' then
x = x - (self:get_width() or 0) + 1
end
return x, y
end
function Widget:render()
if monitor_state(self.opts.type) == false then
return
end
self:update()
local x, y = self:get_pos()
local p = gui.Painter.new_xy(x, y, gps.dimx - 1, y)
self:render_body(p)
end
function Widget:update() end
function Widget:get_width() end
function Widget:render_body() end
Widget_weather = defclass(Widget_weather, Widget)
function Widget_weather:update()
self.counts = get_weather_counts()
end
function Widget_weather:get_width()
if self.counts.rain > 0 then
if self.counts.snow > 0 then
return 9
end
return 4
elseif self.counts.snow > 0 then
return 4
end
return 0
end
function Widget_weather:render_body(p)
if self.counts.rain > 0 then
p:string('Rain', COLOR_LIGHTBLUE):advance(1)
end
if self.counts.snow > 0 then
p:string('Snow', COLOR_WHITE)
end
end
Widget_date = defclass(Widget_date, Widget)
Widget_date.ATTRS = {
output = ''
}
function Widget_date:update()
if not self.opts.format then
self.opts.format = 'Y-M-D'
end
local year = dfhack.world.ReadCurrentYear()
local month = dfhack.world.ReadCurrentMonth() + 1
local day = dfhack.world.ReadCurrentDay()
self.output = 'Date:'
for i = 1, #self.opts.format do
local c = self.opts.format:sub(i, i)
if c == 'y' or c == 'Y' then
self.output = self.output .. year
elseif c == 'm' or c == 'M' then
if c == 'M' and month < 10 then
self.output = self.output .. '0'
end
self.output = self.output .. month
elseif c == 'd' or c == 'D' then
if c == 'D' and day < 10 then
self.output = self.output .. '0'
end
self.output = self.output .. day
else
self.output = self.output .. c
end
end
end
function Widget_date:get_width()
return #self.output
end
function Widget_date:render_body(p)
p:string(self.output, COLOR_GREY)
end
Widget_misery = defclass(Widget_misery, Widget)
function Widget_misery:update()
self.data = get_misery_data()
end
function Widget_misery:get_width()
local w = 2 + 6
for k, v in pairs(self.data) do
w = w + #tostring(v.value)
end
return w
end
function Widget_misery:render_body(p)
p:string("H:", COLOR_WHITE)
for i = 0, 6 do
local v = self.data[i]
p:string(tostring(v.value), v.color)
if not v.last then
p:string("/", COLOR_WHITE)
end
end
end
Widget_cursor = defclass(Widget_cursor, Widget)
function Widget_cursor:update()
if gps.mouse_x == -1 and not self.opts.show_invalid then
self.output = ''
return
end
self.output = (self.opts.format or '(x,y)'):gsub('[xX]', gps.mouse_x):gsub('[yY]', gps.mouse_y)
end
function Widget_cursor:get_width()
return #self.output
end
function Widget_cursor:render_body(p)
p:string(self.output)
end
function render_all()
for _, w in pairs(widgets) do
w:render()
end
end
function load_config()
config = require('json').decode_file('dfhack-config/dwarfmonitor.json')
if not config.widgets then
dmerror('No widgets enabled')
end
if type(config.widgets) ~= 'table' then
dmerror('"widgets" is not a table')
end
widgets = {}
for _, opts in pairs(config.widgets) do
if type(opts) ~= 'table' then qerror('"widgets" is not an array') end
if not opts.type then dmerror('Widget missing type field') end
local cls = _ENV['Widget_' .. opts.type]
if not cls then
dmerror('Invalid widget type: ' .. opts.type)
end
table.insert(widgets, cls(opts))
end
end
return _ENV

@ -113,13 +113,13 @@ message MapBlock
repeated MatPair base_materials = 8;
repeated int32 magma = 9;
repeated int32 water = 10;
repeated bool hidden = 11;
repeated bool light = 12;
repeated bool subterranean = 13;
repeated bool outside = 14;
repeated bool aquifer = 15;
repeated bool water_stagnant = 16;
repeated bool water_salt = 17;
repeated bool hidden = 11;
repeated bool light = 12;
repeated bool subterranean = 13;
repeated bool outside = 14;
repeated bool aquifer = 15;
repeated bool water_stagnant = 16;
repeated bool water_salt = 17;
}
message MatPair {
@ -212,4 +212,4 @@ message MapInfo
optional string world_name = 7;
optional string world_name_english = 8;
optional string save_name = 9;
}
}

@ -12,10 +12,12 @@
#include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_topicmeeting_fill_land_holder_positionsst.h"
#include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_buildinglistst.h"
#include "df/viewscreen_joblistst.h"
#include "df/historical_figure.h"
#include "df/interface_key.h"
#include "df/interfacest.h"
#include "df/layer_object_listst.h"
@ -1746,6 +1748,48 @@ IMPLEMENT_HOOKS(df::viewscreen_dwarfmodest, room_assign_search);
// END: Room assignment search
//
//
// START: Noble suggestion search
//
typedef search_generic<df::viewscreen_topicmeeting_fill_land_holder_positionsst, int32_t> noble_suggest_search_base;
class noble_suggest_search : public noble_suggest_search_base
{
public:
string get_element_description (int32_t hf_id) const
{
df::historical_figure *histfig = df::historical_figure::find(hf_id);
if (!histfig)
return "";
df::unit *unit = df::unit::find(histfig->unit_id);
if (!unit)
return "";
return get_unit_description(unit);
}
void render() const
{
print_search_option(2, gps->dimy - 4);
}
vector<int32_t> *get_primary_list()
{
return &viewscreen->candidate_histfig_ids;
}
virtual int32_t *get_viewscreen_cursor()
{
return &viewscreen->cursor;
}
};
IMPLEMENT_HOOKS(df::viewscreen_topicmeeting_fill_land_holder_positionsst, noble_suggest_search);
//
// END: Noble suggestion search
//
#define SEARCH_HOOKS \
HOOK_ACTION(unitlist_search_hook) \
HOOK_ACTION(roomlist_search_hook) \
@ -1760,7 +1804,8 @@ IMPLEMENT_HOOKS(df::viewscreen_dwarfmodest, room_assign_search);
HOOK_ACTION(joblist_search_hook) \
HOOK_ACTION(burrow_search_hook) \
HOOK_ACTION(stockpile_search_hook) \
HOOK_ACTION(room_assign_search_hook)
HOOK_ACTION(room_assign_search_hook) \
HOOK_ACTION(noble_suggest_search_hook)
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
{

@ -1,5 +1,5 @@
-- hack-wish.lua
-- Allows for script-based wishing.
-- create-item.lua
-- A gui-based item creation script.
-- author Putnam
-- edited by expwnent
@ -235,7 +235,7 @@ if not args.startup then
if unit then
hackWish(unit)
else
qerror('A unit needs to be selected to use hackwish.')
qerror('A unit needs to be selected to use gui/create-item.')
end
else
eventful.onReactionComplete.hackWishP=function(reaction,unit,input_items,input_reagents,output_items,call_native)

@ -14,6 +14,7 @@ local keybindings={
reinterpret={key="CUSTOM_ALT_R",desc="Open selected entry as something else"},
start_filter={key="CUSTOM_S",desc="Start typing filter, Enter to finish"},
help={key="HELP",desc="Show this help"},
displace={key="STRING_A093",desc="Open reference offseted by index"},
NOT_USED={key="SEC_SELECT",desc="Choose an enum value from a list"}, --not a binding...
}
function getTargetFromScreens()
@ -85,8 +86,9 @@ function GmEditorUi:init(args)
local mainPage=widgets.Panel{
subviews={
mainList,
widgets.Label{text={{text="<no item>",id="name"},{gap=1,text="Help",key="HELP",key_sep = '()'}}, view_id = 'lbl_current_item',frame = {l=1,t=1,yalign=0}},
widgets.EditField{frame={l=1,t=2},active=false,on_change=self:callback('text_input'),on_submit=self:callback("enable_input",false),view_id="filter_input"},
widgets.Label{text={{text="<no item>",id="name"},{gap=1,text="Help",key=keybindings.help.key,key_sep = '()'}}, view_id = 'lbl_current_item',frame = {l=1,t=1,yalign=0}},
widgets.Label{text={{text="Search",key=keybindings.start_filter.key,key_sep = '()'},{text=":"}},frame={l=1,t=2}},
widgets.EditField{frame={l=12,t=2},active=false,on_change=self:callback('text_input'),on_submit=self:callback("enable_input",false),view_id="filter_input"},
--widgets.Label{text="BLAH2"}
}
,view_id='page_main'}
@ -199,6 +201,15 @@ function GmEditorUi:openReinterpret(key)
self:pushTarget(df.reinterpret_cast(ntype,trg.target[key]))
end)
end
function GmEditorUi:openOffseted(index,choice)
local trg=self:currentTarget()
local trg_key=trg.keys[index]
dialog.showInputPrompt(tostring(trg_key),"Enter offset:",COLOR_WHITE,"",
function(choice)
self:pushTarget(trg.target[trg_key]:_displace(tonumber(choice)))
end)
end
function GmEditorUi:editSelected(index,choice)
local trg=self:currentTarget()
local trg_key=trg.keys[index]
@ -272,7 +283,8 @@ function GmEditorUi:onInput(keys)
local _,stoff=df.sizeof(trg.target)
local size,off=df.sizeof(trg.target:_field(self:getSelectedKey()))
dialog.showMessage("Offset",string.format("Size hex=%x,%x dec=%d,%d\nRelative hex=%x dec=%d",size,off,size,off,off-stoff,off-stoff),COLOR_WHITE)
elseif keys[keybindings.displace.key] then
self:openOffseted(self.subviews.list_main:getSelected())
elseif keys[keybindings.find.key] then
self:find()
elseif keys[keybindings.lua_set.key] then

@ -249,7 +249,6 @@ end
function get_plant_reaction_products (mat)
local list = {}
add_react_prod (list, mat, "DRINK_MAT", "Used to brew ")
add_react_prod (list, mat, "GROWTH_JUICE_PROD", "Pressed into ")
add_react_prod (list, mat, "PRESS_LIQUID_MAT", "Pressed into ")
add_react_prod (list, mat, "LIQUID_EXTRACTABLE", "Extractable product: ")
@ -290,15 +289,26 @@ function GetFoodPropertiesStringList (item)
if item._type == df.item_plantst and GetMatPlant (item) then
local plant = GetMatPlant (item)
for k,v in pairs (plant.material_defs) do
if v ~= -1 and string.find (k,"type_") and not string.find (k,"type_basic")
or string.find (k,"type_seed") or string.find (k,"type_tree") then
if v ~= -1 and k:find("type_") and not (k:find("type_basic")
or k:find("type_seed") or k:find("type_tree")) then
local targetmat = dfhack.matinfo.decode (v,
plant.material_defs["idx_"..string.match (k,"type_(.+)")])
plant.material_defs["idx_"..k:match("type_(.+)")])
local state = "Liquid"
if string.find (k,"type_mill") then state = "Powder"
elseif string.find (k,"type_thread") then state = "Solid" end
local describe = "Made into "
if k:find("type_mill")
then state = "Powder" describe = "Ground into "
elseif k:find("type_thread")
then state = "Solid" describe = "Woven into "
elseif k:find("type_drink")
then describe = "Brewed into "
elseif k:find("type_extract_barrel")
then describe = "Cask-aged into "
elseif k:find("type_extract_vial")
then describe = "Refined into vials of "
elseif k:find("type_extract_still_vial")
then describe = "Distilled into vials of " end
local st_name = targetmat.material.state_name[state]
append(list,"Used to make "..targetmat.material.prefix..''..st_name)
append(list,describe..targetmat.material.prefix..''..st_name)
end
end
end

@ -0,0 +1,33 @@
import os, sys, time
red = '\x1b[31m\x1b[1m'
green = '\x1b[32m\x1b[1m'
reset = '\x1b(B\x1b[m'
if os.environ.get('TRAVIS', '') == 'true':
print('This script cannot be used in a travis build')
sys.exit(1)
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
commands = []
with open('.travis.yml') as f:
lines = list(f.readlines())
script_found = False
for line in lines:
if line.startswith('script:'):
script_found = True
elif script_found:
if line.startswith('- '):
commands.append(line[2:].rstrip('\r\n'))
else:
break
ret = 0
for cmd in commands:
print('$ %s' % cmd)
start = time.time()
code = os.system(cmd)
end = time.time()
if code != 0:
ret = 1
print('\n%sThe command "%s" exited with %i.%s [%.3f secs]' %
(green if code == 0 else red, cmd, code, reset, end - start))
print('\nDone. Your build exited with %i.' % ret)
sys.exit(ret)

@ -5,14 +5,18 @@ else:
from urllib.request import urlopen
from urllib.error import HTTPError
try:
repo = os.environ.get('TRAVIS_REPO_SLUG', 'dfhack/dfhack').lower()
pr_id = int(os.environ.get('TRAVIS_PULL_REQUEST', 'false'))
except ValueError:
print('Not a pull request')
sys.exit(0)
print('Pull request %i' % pr_id)
print('Pull request %s#%i' % (repo, pr_id))
if repo != 'dfhack/dfhack':
print('Not in dfhack/dfhack')
sys.exit(0)
res = {}
try:
res = json.loads(urlopen('https://api.github.com/repos/dfhack/dfhack/pulls/%i' % pr_id).read().decode('utf-8'))
res = json.loads(urlopen('https://api.github.com/repos/%s/pulls/%i' % (repo, pr_id)).read().decode('utf-8'))
except ValueError:
pass
except HTTPError: