Merge branch 'develop' into zone-unassign

develop
lethosor 2016-04-22 18:40:18 -04:00
commit 52728babd4
122 changed files with 2844 additions and 6115 deletions

3
.gitmodules vendored

@ -4,9 +4,6 @@
[submodule "plugins/isoworld"]
path = plugins/isoworld
url = git://github.com/DFHack/isoworld.git
[submodule "plugins/df2mc"]
path = plugins/df2mc
url = git://github.com/DFHack/DF2MC.git
[submodule "library/xml"]
path = library/xml
url = git://github.com/DFHack/df-structures.git

@ -1,19 +1,36 @@
sudo: false
language: cpp
addons:
apt:
packages:
- gcc-multilib
- g++-multilib
- lua5.2
- libxml-libxml-perl
- libxml-libxslt-perl
- zlib1g-dev:i386
language: cpp
packages: &default_packages
- lua5.2
- libxml-libxml-perl
- libxml-libxslt-perl
- zlib1g-dev:i386
matrix:
include:
- env: GCC_VERSION=4.5
addons:
apt:
packages:
- *default_packages
- gcc-4.5-multilib
- g++-4.5-multilib
- env: GCC_VERSION=4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- *default_packages
- gcc-4.8-multilib
- g++-4.8-multilib
before_install:
pip install --user sphinx
script:
- git tag tmp-travis-build
- sh travis/git-info.sh
- sphinx-build -qW -j3 . docs/html
- python travis/pr-check-base.py
- python travis/lint.py
- python travis/authors-rst.py
@ -22,7 +39,12 @@ script:
- python travis/script-syntax.py --ext=rb --cmd="ruby -c" --path scripts/
- mkdir build-travis
- cd build-travis
- cmake .. -DBUILD_DOCS:BOOL=ON
- cmake .. -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DBUILD_DOCS:BOOL=ON
- make -j3
notifications:
email: false
irc:
channels:
- "chat.freenode.net#dfhack"
on_success: change
on_failure: always

@ -105,8 +105,8 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif()
# set up versioning.
set(DF_VERSION "0.42.05")
SET(DFHACK_RELEASE "alpha0")
set(DF_VERSION "0.42.06")
SET(DFHACK_RELEASE "beta1")
SET(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")

@ -21,6 +21,7 @@
When adding a new release, change "DFHack future" to the appropriate title
before releasing, and then add a new "DFHack future" section after releasing.
.. _changelog:
#########
Changelog
@ -42,6 +43,12 @@ Internals
"Dwarf Fortress.exe" +devel/print-args example
- Prevented plugins with active viewscreens from being unloaded and causing a crash
- Additional script search paths can be specified in dfhack-config/script-paths.txt
Lua
---
- `building-hacks` now supports ``auto_gears`` flags. It automatically finds and animates gears in building definition
- Changed how `eventful` triggers reaction complete. Now it has ``onReactionComplete`` and ``onReactionCompleting``. Second one can be canceled
New Plugins
-----------
@ -50,6 +57,7 @@ New Plugins
New Scripts
-----------
- `devel/save-version`: Displays DF version information about the current save
- `modtools/extra-gamelog`: replaces ``log-region``, ``soundsense-season``, and ``soundsense``
New Features
------------
@ -68,15 +76,52 @@ New Features
- ``tweak hide-priority``: Adds an option to hide designation priority indicators
- ``tweak title-start-rename``: Adds a safe rename option to the title screen "Start Playing" menu
- `colonies`: new ``place`` subcommand and supports any vermin (default honey bees)
Fixes
-----
- Fixed a crash bug caused by the historical figures DFHack uses to store persistent data.
- More plugins should recognize non-dwarf citizens
- Fixed a possible crash from cloning jobs
- moveToBuilding() now sets flags for items that aren't a structural part of the building properly
- `autotrade`, `stocks`: Made trading work when multiple caravans are present but only some can trade
- `confirm` note-delete: No longer interferes with name entry
- `exportlegends`: Handles entities without specific races, and a few other fixes for things new to v0.42
- `fastdwarf`: Fixed a bug involving teleporting mothers but not the babies they're holding.
- `gaydar`: Fixed text display on OS X/Linux and failure with soul-less creatures
- `manipulator`:
- allowed editing of non-dwarf citizens
- stopped ghosts and visitors from being editable
- fixed applying last custom profession
- `modtools/create-unit`: Stopped making units without civs historical figures
- `modtools/force`:
- Removed siege option
- Prevented a crash resulting from a bad civilization option
- `showmood`: Fixed name display on OS X/Linux
- `view-item-info`: Fixed density units
Misc Improvements
-----------------
- `autochop`: Can now edit log minimum/maximum directly and remove limit entirely
- `autolabor`, `autohauler`, `manipulator`: Added support for new jobs/labors/skills
- `colonies`: now implemented by a script
- `createitem`: Can now create items anywhere without specifying a unit, as long as a unit exists on the map
- `devel/export-dt-ini`: Updated for 0.42.06
- `devel/find-offsets`: Automated several more scans
- `gui/gm-editor`: Now supports finding some items with a numeric ID (with ``i``)
- `lua`: Now supports some built-in variables like `gui/gm-editor`, e.g. ``unit``, ``screen``
- `remotefortressreader`: Can now trigger keyboard events
- `stockflow`: Now offers better control over individual craft jobs
- `weather`: now implemented by a script
Removed
-------
- DFusion: legacy script system, obsolete or replaced by better alternatives
DFHack 0.40.24-r5
=================
@ -120,28 +165,29 @@ Internals
- More DFHack build information used in plugin version checks and available to plugins and lua scripts
- Fixed a rare overflow issue that could cause crashes on Linux and OS X
- Stopped DF window from receiving input when unfocused on OS X
- Fixed issues with keybindings involving Ctrl-A and Ctrl-Z, as well as Alt-E/U/N on OS X
- Fixed issues with keybindings involving :kbd:`Ctrl`:kbd:`A` and :kbd:`Ctrl`:kbd:`Z`,
as well as :kbd:`Alt`:kbd:`E`/:kbd:`U`/:kbd:`N` on OS X
- Multiple contexts can now be specified when adding keybindings
- Keybindings can now use F10-F12 and 0-9
- Keybindings can now use :kbd:`F10`-:kbd:`F12` and :kbd:`0`-:kbd:`9`
- Plugin system is no longer restricted to plugins that exist on startup
- :file:`dfhack.init` file locations significantly generalized
Lua
---
- Scripts can be enabled with the built-in enable/disable commands
- Scripts can be enabled with the built-in `enable`/`disable <disable>` commands
- A new function, ``reqscript()``, is available as a safer alternative to ``script_environment()``
- Lua viewscreens can choose not to intercept the OPTIONS keybinding
New internal commands
---------------------
- kill-lua: Interrupt running Lua scripts
- type: Show where a command is implemented
- `kill-lua`: Interrupt running Lua scripts
- `type`: Show where a command is implemented
New plugins
-----------
- `confirm`: Adds confirmation dialogs for several potentially dangerous actions
- `fix-unit-occupancy`: Fixes issues with unit occupancy, such as faulty "unit blocking tile" messages (:bug:`3499`)
- title-version (formerly vshook): Display DFHack version on title screen
- `title-version` (formerly ``vshook``): Display DFHack version on title screen
New scripts
-----------
@ -183,7 +229,7 @@ Fixes
- `buildingplan`: Now supports hatch covers
- `gui/create-item`: fixed assigning quality to items, made :kbd:`Esc` work properly
- `gui/gm-editor`: handles lua tables properly
- help: now recognizes built-in commands, like "help"
- `help`: now recognizes built-in commands, like ``help``
- `manipulator`: fixed crash when selecting custom professions when none are found
- `remotefortressreader`: fixed crash when attempting to send map info when no map was loaded
- `search-plugin`: fixed crash in unit list after cancelling a job; fixed crash when disabling stockpile category after searching in a subcategory
@ -215,7 +261,7 @@ Misc Improvements
- `automaterial`: Fixed several issues with constructions being allowed/disallowed incorrectly when using box-select
- `dwarfmonitor`:
- widgets' positions, formats, etc. are now customizable (see Readme)
- widgets' positions, formats, etc. are now customizable
- weather display now separated from the date display
- New mouse cursor widget
@ -228,16 +274,16 @@ Misc Improvements
- Now avoids errors with unrecognized types
- `gui/hack-wish`: renamed to `gui/create-item`
- "keybinding list" accepts a context
- `keybinding list <keybinding>` accepts a context
- `lever`:
- Lists lever names
- "lever pull" can be used to pull the currently-selected lever
- ``lever pull`` can be used to pull the currently-selected lever
- memview: Fixed display issue
- ``memview``: Fixed display issue
- `modtools/create-item`: arguments are named more clearly, and you can specify the creator to be the unit with id ``df.global.unit_next_id-1`` (useful in conjunction with `modtools/create-unit`)
- nyan: Can now be stopped with dfhack-run
- plug: lists all plugins; shows state and number of commands in plugins
- ``nyan``: Can now be stopped with dfhack-run
- `plug`: lists all plugins; shows state and number of commands in plugins
- `prospect`: works from within command-prompt
- `quicksave`: Restricted to fortress mode
- `remotefortressreader`: Exposes more information
@ -268,7 +314,7 @@ Internals
---------
- Ruby library now included on OS X - Ruby scripts should work on OS X 10.10
- libstdc++ should work with older versions of OS X
- Added support for ``onLoadMap.init``/``onUnloadMap.init`` scripts
- Added support for `onMapLoad.init / onMapUnload.init <other_init_files>` scripts
- game type detection functions are now available in the World module
- The ``DFHACK_LOG_MEM_RANGES`` environment variable can be used to log information to ``stderr.log`` on OS X
- Fixed adventure mode menu names
@ -283,7 +329,7 @@ Lua
New Internal Commands
---------------------
- `hide`, `show`: hide and show the console on Windows
- `sc-script`: Allows additional scripts to be run when certain events occur (similar to onLoad*.init scripts)
- `sc-script`: Allows additional scripts to be run when certain events occur (similar to `onLoad.init` scripts)
New Plugins
-----------
@ -302,7 +348,7 @@ New Tweaks
Fixes
-----
- Fixed game type detection in `3dveins`, `gui/create-item`, `reveal`, `seedwatch`
- PRELOAD_LIB: More extensible on Linux
- ``PRELOAD_LIB``: More extensible on Linux
- `add-spatter`, `eventful`: Fixed crash on world load
- `add-thought`: Now has a proper subthought arg.
- `building-hacks`: Made buildings produce/consume correct amount of power
@ -316,7 +362,7 @@ Fixes
- Fixed error message in Arena mode
- no longer checks the DF version
- fixed ballistic arrow head orders
- convinces the bookkeeper to update records more often'
- convinces the bookkeeper to update records more often
- `zone`: Stopped crash when scrolling cage owner list
@ -344,8 +390,8 @@ DFHack 0.40.24-r2
Internals
---------
- Lua scripts can set environment variables of each other with dfhack.run_script_with_env.
- Lua scripts can now call each others internal nonlocal functions with dfhack.script_environment(scriptName).functionName(arg1,arg2).
- Lua scripts can set environment variables of each other with ``dfhack.run_script_with_env``
- Lua scripts can now call each others internal nonlocal functions with ``dfhack.script_environment(scriptName).functionName(arg1,arg2)``
- `eventful`: Lua reactions no longer require LUA_HOOK as a prefix; you can register a callback for the completion of any reaction with a name
- Filesystem module now provides file access/modification times and can list directories (normally and recursively)
- Units Module: New functions::
@ -388,7 +434,7 @@ Fixes
- `add-thought`: updated to properly affect stress.
- `hfs-pit`: should work now
- `autobutcher`: takes gelding into account
- init.lua existence checks should be more reliable (notably when using non-English locales)
- :file:`init.lua` existence checks should be more reliable (notably when using non-English locales)
Misc Improvements
-----------------
@ -425,7 +471,6 @@ New Scripts
Removed
-------
- embark.lua
- needs_porting/*
Misc Improvements
@ -433,615 +478,8 @@ Misc Improvements
Added support for searching more lists
DFHack 0.40.23-r1
=================
Internals
- plugins will not be loaded if globals they specify as required are not located (should prevent some crashes)
Fixes
-----
- Fixed numerous (mostly Lua-related) crashes on OS X by including a more up-to-date libstdc++
- :kbd:`Alt` should no longer get stuck on Windows (and perhaps other platforms as well)
- `gui/advfort` works again
- `autobutcher`: takes sexualities into account
- devel/export-dt-ini: Updated for 0.40.20+
- `digfort`: now checks file type and existence
- `exportlegends`: Fixed map export
- `full-heal`: Fixed a problem with selecting units in the GUI
- `gui/hack-wish`: Fixed restrictive material filters
- `mousequery`: Changed box-select key to Alt+M
- `dwarfmonitor`: correct date display (month index, separator)
- `putontable`: added to the readme
- `siren` should work again
- stderr.log: removed excessive debug output on OS X
- `trackstop`: No longer prevents cancelling the removal of a track stop or roller.
- Fixed a display issue with ``PRINT_MODE:TEXT``
- Fixed a symbol error (MapExtras::BiomeInfo::MAX_LAYERS) when compiling DFHack in Debug mode
New Plugins
-----------
- `fortplan`: designate construction of (limited) buildings from .csv file, quickfort-style
New Scripts
-----------
- `gui/stockpiles`: an in-game interface for saving and loading stockpile settings files.
- `position`: Reports the current date, time, month, and season, plus some location info. Port/update of position.py
- `hfs-pit`: Digs a hole to hell under the cursor. Replaces needs_porting/hellhole.cpp
Removed
-------
- embark.lua: Obsolete, use `embark-tools`
New tweaks
----------
- `eggs-fertile <tweak>`: Displays an egg fertility indicator on nestboxes
- `max-wheelbarrow <tweak>`: Allows assigning more than 3 wheelbarrows to a stockpile
Misc Improvements
-----------------
- `embark-tools`: Added basic mouse support on the local map
- Made some adventure mode keybindings in :file:`dfhack.init-example` only work in adventure mode
- `gui/companion-order`: added a default keybinding
- further work on needs_porting
DFHack 0.40.19-r1
=================
Fixes
-----
- `modtools/reaction-trigger`: fixed typo
- `modtools/item-trigger`: should now work with item types
New plugins
-----------
- `savestock, loadstock <stocksettings>`: save and load stockpile settings across worlds and saves
New scripts
-----------
- `remove-stress`: set selected or all units unit to -1,000,000 stress (this script replaces removebadthoughts)
Misc improvements
-----------------
- `command-prompt`: can now access selected items, units, and buildings
- `autolabor`: add an optional talent pool parameter
DFHack 0.40.16-r1
=================
Internals
---------
- `EventManager` should handle INTERACTION triggers a little better. It still can get confused about who did what but only rarely.
- `EventManager` should no longer trigger REPORT events for old reports after loading a save.
- lua/persist-table: a convenient way of using persistent tables of arbitrary structure and dimension in Lua
Fixes
-----
- `mousequery`: Disabled when linking levers
- `stocks`: Melting should work now
- `full-heal`: Updated with proper argument handling
- `modtools/reaction-trigger-transition`: should produce the correct syntax now
- `superdwarf`: should work better now
- `forum-dwarves`: update for new df-structures changes
New Scripts
-----------
- `adaptation`: view or set the cavern adaptation level of your citizens
- `add-thought`: allows the user to add thoughts to creatures.
- `gaydar`: detect the sexual orientation of units on the map
- `markdown`: Save a copy of a text screen in markdown (for reddit among others).
- devel/all-bob: renames everyone Bob to help test interaction-trigger
Misc Improvements
-----------------
- `autodump`: Can now mark a stockpile for auto-dumping (similar to automelt and autotrade)
- `buildingplan`: Can now auto-allocate rooms to dwarves with specific positions (e.g. expedition leader, mayor)
- `dwarfmonitor`: now displays a weather indicator and date
- lua/syndrome-util, `modtools/add-syndrome`: now you can remove syndromes by SYN_CLASS
- No longer write empty :file:`.history` files
DFHack 0.40.15-r1
=================
Fixes
-----
- mousequery: Fixed behavior when selecting a tile on the lowest z-level
Misc Improvements
-----------------
- `EventManager`: deals with frame_counter getting reset properly now.
- `modtools/item-trigger`: fixed equip/unequip bug and corrected minor documentation error
- `teleport`: Updated with proper argument handling and proper unit-at-destination handling.
- `autotrade`: Removed the newly obsolete :guilabel:`Mark all` functionality.
- `search-plugin`: Adapts to the new trade screen column width
- `tweak fast-trade <tweak>`: Switching the fast-trade keybinding to Shift-Up/Shift-Down, due to Select All conflict
DFHack 0.40.14-r1
=================
Internals
---------
- The DFHack console can now be disabled by setting the DFHACK_DISABLE_CONSOLE environment variable: ``DFHACK_DISABLE_CONSOLE=1 ./dfhack``
Fixes
-----
- Stopped duplicate load/unload events when unloading a world
- Stopped ``-e`` from being echoed when DFHack quits on Linux
- `automelt`: now uses a faster method to locate items
- `autotrade`: "Mark all" no longer double-marks bin contents
- `drain-aquifer`: new script replaces the buggy plugin
- `embark-tools`: no longer conflicts with keys on the notes screen
- `fastdwarf`: Fixed problems with combat/attacks
- `forum-dwarves`: should work now
- `manipulator`: now uses a stable sort, allowing sorting by multiple categories
- `rendermax`: updated to work with 0.40
New Plugins
-----------
- `trackstop`: Shows track stop friction and dump direction in its :kbd:`q` menu
New Tweaks
----------
- farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus
- import-priority-category: Allows changing the priority of all goods in a category when discussing an import agreement with the liaison
- manager-quantity: Removes the limit of 30 jobs per manager order
- civ-view-agreement: Fixes overlapping text on the "view agreement" screen
- nestbox-color: Fixes the color of built nestboxes
Misc Improvements
-----------------
- `exportlegends`: can now handle site maps
DFHack 0.40.13-r1
=================
Internals
---------
- unified spatter structs
- added ruby df.print_color(color, string) method for dfhack console
Fixes
-----
- no more ``-e`` after terminating
- fixed `superdwarf`
DFHack 0.40.12-r1
=================
Internals
---------
- support for global :file:`onLoadWorld.init` and :file:`onUnloadWorld.init` files, called when loading and unloading a world
- Close file after loading a binary patch.
New Plugins
-----------
- `hotkeys`: Shows ingame viewscreen with all dfhack keybindings active in current mode.
- `automelt`: allows marking stockpiles so any items placed in them will be designated for melting
Fixes
-----
- possible crash fixed for `gui/hack-wish`
- `search-plugin`: updated to not conflict with BUILDJOB_SUSPEND
- `workflow`: job_material_category -> dfhack_material_category
Misc Improvements
-----------------
- now you can use ``@`` to print things in interactive Lua with subtley different semantics
- optimizations for stockpiles for `autotrade` and `stockflow`
- updated `exportlegends` to work with new maps, dfhack 40.11 r1+
DFHack 0.40.11-r1
=================
Internals
- Plugins on OS X now use ``.plug.dylib` as an extension instead of ``.plug.so``
Fixes
-----
- `3dveins`: should no longer hang/crash on specific maps
- `autotrade`, `search-plugin`: fixed some layout issues
- `deathcause`: updated
- `gui/hack-wish`: should work now
- `reveal`: no longer allocates data for nonexistent map blocks
- Various documentation fixes and updates
DFHack v0.40.10-r1
==================
A few bugfixes.
DFHack v0.40.08-r2
==================
Internals
---------
- supported per save script folders
- Items module: added createItem function
- Sorted CMakeList for plugins and plugins/devel
- diggingInvaders no longer builds if plugin building is disabled
- `EventManager`: EQUIPMENT_CHANGE now triggers for new units. New events::
ON_REPORT
UNIT_ATTACK
UNLOAD
INTERACTION
New Scripts
-----------
- lua/repeat-util: makes it easier to make things repeat indefinitely
- lua/syndrome-util: makes it easier to deal with unit syndromes
- forum-dwarves: helps copy df viewscreens to a file
- full-heal: fully heal a unit
- remove-wear: removes wear from all items in the fort
- repeat: repeatedly calls a script or a plugin
- ShowUnitSyndromes: shows syndromes affecting units and other relevant info
- teleport: teleports units
- devel/print-args
- fix/blood-del: makes it so civs don't bring barrels full of blood ichor or goo
- fix/feeding-timers: reset the feeding timers of all units
- gui/hack-wish: creates items out of any material
- gui/unit-info-viewer: displays information about units
- modtools/add-syndrome: add a syndrome to a unit or remove one
- modtools/anonymous-script: execute an lua script defined by a string. Useful for the ``*-trigger`` scripts.
- modtools/force: forces events: caravan, migrants, diplomat, megabeast, curiousbeast, mischievousbeast, flier, siege, nightcreature
- modtools/item-trigger: triggers commands based on equipping, unequipping, and wounding units with items
- modtools/interaction-trigger: triggers commands when interactions happen
- modtools/invader-item-destroyer: destroys invaders' items when they die
- modtools/moddable-gods: standardized version of Putnam's moddable gods script
- modtools/projectile-trigger: standardized version of projectileExpansion
- modtools/reaction-trigger: trigger commands when custom reactions complete; replaces autoSyndrome
- modtools/reaction-trigger-transition: a tool for converting mods from autoSyndrome to reaction-trigger
- modtools/random-trigger: triggers random scripts that you register
- modtools/skill-change: for incrementing and setting skills
- modtools/spawn-flow: creates flows, like mist or dragonfire
- modtools/syndrome-trigger: trigger commands when syndromes happen
- modtools/transform-unit: shapeshifts a unit, possibly permanently
Misc improvements
-----------------
- new function in utils.lua for standardized argument processing
Removed
-------
- digmat.rb: digFlood does the same functionality with less FPS impact
- scripts/invasionNow: scripts/modtools/force.lua does it better
- autoSyndrome replaced with scripts/modtools/reaction-trigger.lua
- syndromeTrigger replaced with scripts/modtools/syndrome-trigger.lua
- devel/printArgs plugin converted to scripts/devel/print-args.lua
- outsideOnly plugin replaced by modtools/outside-only
DFHack v0.40.08-r1
==================
Was a mistake. Don't use it.
DFHack v0.34.11-r5
==================
Internals
---------
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
- support for basic filesystem operations (e.g. chdir, mkdir, rmdir, stat) in C++ and Lua
- Lua API for listing files in directory. Needed for mod-manager.
- Lua API for creating unit combat reports and writing to gamelog.
- Lua API for running arbitrary DFHack commands
- support for multiple ``raw/init.d/*.lua`` init scripts in one save.
- eventful now has a more friendly way of making custom sidebars
- on Linux and OS X the console now supports moving the cursor back and forward by a whole word.
New scripts
-----------
- gui/mod-manager: allows installing/uninstalling mods into df from df/mods directory.
- gui/clone-uniform: duplicates the currently selected uniform in the military screen.
- fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it)
- undump-buildings: removes dump designation from materials used in buildings.
- exportlegends: exports data from legends mode, allowing a set-and-forget export of large worlds.
- log-region: each time a fort is loaded identifying information will be written to the gamelog.
- dfstatus: show an overview of critical stock quantities, including food, drinks, wood, and bars.
- command-prompt: a dfhack command prompt in df.
New plugins
-----------
- rendermax: replace the renderer with something else, eg ``rendermax light``- a lighting engine
- automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting)
- embark-tools: implementations of Embark Anywhere, Nano Embark, and a few other embark-related utilities
- building-hacks: Allows to add custom functionality and/or animations to buildings.
- petcapRemover: triggers pregnancies in creatures so that you can effectively raise the default pet population cap
- 'plant create': spawn a new shrub under the cursor
New tweaks
----------
- craft-age-wear: make crafted items wear out with time like in old versions (bug 6003)
- adamantine-cloth-wear: stop adamantine clothing from wearing out (bug 6481)
- confirm-embark: adds a prompt before embarking (on the "prepare carefully" screen)
Misc improvements
-----------------
- plant: move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- digfort: improved csv parsing, add start() comment handling
- exterminate: allow specifying a caste (exterminate gob:male)
- createitem: in adventure mode it now defaults to the controlled unit as maker.
- autotrade: adds "(Un)mark All" options to both panes of trade screen.
- mousequery: several usability improvements.
- mousequery: show live overlay (in menu area) of what's on the tile under the mouse cursor.
- search: workshop profile search added.
- dwarfmonitor: add screen to summarise preferences of fortress dwarfs.
- getplants: add autochop function to automate woodcutting.
- stocks: added more filtering and display options.
- Siege engine plugin:
- engine quality and distance to target now affect accuracy
- firing the siege engine at a target produces a combat report
- improved movement speed computation for meandering units
- operators in Prepare To Fire mode are released from duty once hungry/thirsty if there is a free replacement
DFHack v0.34.11-r4
==================
New commands
------------
- diggingInvaders - allows invaders to dig and/or deconstruct walls and buildings in order to get at your dwarves.
- digFlood - automatically dig out specified veins as they are revealed
- enable, disable - Built-in commands that can be used to enable/disable many plugins.
- restrictice - Restrict traffic on squares above visible ice.
- restrictliquid - Restrict traffic on every visible square with liquid.
- treefarm - automatically chop trees and dig obsidian
New Scripts
-----------
- autobutcher: A GUI front-end for the autobutcher plugin.
- invasionNow: trigger an invasion, or many
- locate_ore: scan the map for unmined ore veins
- masspit: designate caged creatures in a zone for pitting
- multicmd: run a sequence of dfhack commands, separated by ';'
- startdwarf: change the number of dwarves for a new embark
- digmat: dig veins/layers tile by tile, as discovered
Misc improvements
-----------------
- autoSyndrome:
- disable by default
- reorganized special tags
- minimized error spam
- reset policies: if the target already has an instance of the syndrome you can skip,
add another instance, reset the timer, or add the full duration to the time remaining
- core: fix SC_WORLD_(UN)LOADED event for arena mode
- exterminate: renamed from slayrace, add help message, add butcher mode
- fastdwarf: fixed bug involving fastdwarf and teledwarf being on at the same time
- magmasource: rename to ``source``, allow water/magma sources/drains
- ruby: add df.dfhack_run "somecommand"
- syndromeTrigger: replaces and extends trueTransformation. Can trigger things when syndromes are added for any reason.
- tiletypes: support changing tile material to arbitrary stone.
- workNow: can optionally look for jobs when jobs are completed
New tweaks
----------
- hive-crash: Prevent crash if bees die in a hive with ungathered products (bug 6368).
New plugins
-----------
- 3dveins: Reshapes all veins on the map in a way that flows between Z levels. May be unstable. Backup before using.
- autotrade: Automatically send items in marked stockpiles to trade depot, when trading is possible.
- buildingplan: Place furniture before it's built
- dwarfmonitor: Records dwarf activity to measure fort efficiency
- mousequery: Look and poke at the map elements with the mouse.
- outsideOnly: make raw-specified buildings impossible to build inside
- resume: A plugin to help display and resume suspended constructions conveniently
- stocks: An improved stocks display screen.
Internals
---------
- Core: there is now a per-save dfhack.init file for when the save is loaded, and another for when it is unloaded
- EventManager: fixed job completion detection, fixed removal of TICK events, added EQUIPMENT_CHANGE event
- Lua API for a better random number generator and perlin noise functions.
- Once: easy way to make sure something happens once per run of DF, such as an error message
DFHack v0.34.11-r3
==================
Internals
---------
- support for displaying active keybindings properly.
- support for reusable widgets in lua screen library.
- Maps::canStepBetween: returns whether you can walk between two tiles in one step.
- EventManager: monitors various in game events centrally so that individual plugins
don't have to monitor the same things redundantly.
- Now works with OS X 10.6.8
Notable bugfixes
----------------
- autobutcher can be re-enabled again after being stopped.
- stopped Dwarf Manipulator from unmasking vampires.
- Stonesense is now fixed on OS X
Misc improvements
-----------------
- fastdwarf: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing binary patches.
- removebadthoughts: add --dry-run option
- superdwarf: work in adventure mode too
- tweak stable-cursor: carries cursor location from/to Build menu.
- deathcause: allow selection from the unitlist screen
- slayrace: allow targetting undeads
- Workflow plugin:
- properly considers minecarts assigned to routes busy.
- code for deducing job outputs rewritten in lua for flexibility.
- logic fix: collecting webs produces silk, and ungathered webs are not thread.
- items assigned to squads are considered busy, even if not in inventory.
- shearing and milking jobs are supported, but only with generic MILK or YARN outputs.
- workflow announces when the stock level gets very low once a season.
- Auto syndrome plugin: A way of automatically applying boiling rock syndromes and calling dfhack commands controlled by raws.
- Infinite sky plugin: Create new z-levels automatically or on request.
- True transformation plugin: A better way of doing permanent transformations that allows later transformations.
- Work now plugin: Makes the game assign jobs every time you pause.
New tweaks
----------
- tweak military-training: speed up melee squad training up to 10x (normally 3-5x).
New scripts
-----------
- binpatch: the same as the stand-alone binpatch.exe, but works at runtime.
- region-pops: displays animal populations of the region and allows tweaking them.
- lua: lua interpreter front-end converted to a script from a native command.
- dfusion: misc scripts with a text based menu.
- embark: lets you embark anywhere.
- lever: list and pull fort levers from the dfhack console.
- stripcaged: mark items inside cages for dumping, eg caged goblin weapons.
- soundsense-season: writes the correct season to gamelog.txt on world load.
- create-items: spawn items
- fix/cloth-stockpile: fixes bug 5739; needs to be run after savegame load every time.
New GUI scripts
---------------
- gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them.
- gui/workflow: a front-end for the workflow plugin (part inspired by falconne).
- gui/assign-rack: works together with a binary patch to fix weapon racks.
- gui/gm-editor: an universal editor for lots of dfhack things.
- gui/companion-order: a adventure mode command interface for your companions.
- gui/advfort: a way to do jobs with your adventurer (e.g. build fort).
New binary patches
------------------
(for use with binpatch)
- armorstand-capacity: doubles the capacity of armor stands.
- custom-reagent-size: lets custom reactions use small amounts of inputs.
- deconstruct-heapfall: stops some items still falling on head when deconstructing.
- deconstruct-teleport: stops items from 16x16 block teleporting when deconstructing.
- hospital-overstocking: stops hospital overstocking with supplies.
- training-ammo: lets dwarves with quiver full of combat-only ammo train.
- weaponrack-unassign: fixes bug that negates work done by gui/assign-rack.
New Plugins
-----------
- fix-armory: Together with a couple of binary patches and the gui/assign-rack script, this plugin makes weapon racks, armor stands, chests and cabinets in properly designated barracks be used again for storage of squad equipment.
- search: Adds an incremental search function to the Stocks, Trading, Stockpile and Unit List screens.
- automaterial: Makes building constructions (walls, floors, fortifications, etc) a little bit easier by saving you from having to trawl through long lists of materials each time you place one.
- Dfusion: Reworked to make use of lua modules, now all the scripts can be used from other scripts.
- Eventful: A collection of lua events, that will allow new ways to interact with df world.
DFHack v0.34.11-r2
==================
Internals
---------
- full support for Mac OS X.
- a plugin that adds scripting in ruby.
- support for interposing virtual methods in DF from C++ plugins.
- support for creating new interface screens from C++ and lua.
- added various other API functions.
Notable bugfixes
----------------
- better terminal reset after exit on linux.
- seedwatch now works on reclaim.
- the sort plugin won't crash on cages anymore.
Misc improvements
-----------------
- autodump: can move items to any walkable tile, not just floors.
- stripcaged: by default keep armor, new dumparmor option.
- zone: allow non-domesticated birds in nestboxes.
- workflow: quality range in constraints.
- cleanplants: new command to remove rain water from plants.
- liquids: can paint permaflow, i.e. what makes rivers power water wheels.
- prospect: pre-embark prospector accounts for caves & magma sea in its estimate.
- rename: supports renaming stockpiles, workshops, traps, siege engines.
- fastdwarf: now has an additional option to make dwarves teleport to their destination.
- Autolabor plugin:
- can set nonidle hauler percentage.
- broker excluded from all labors when needed at depot.
- likewise, anybody with a scheduled diplomat meeting.
New commands
------------
- misery: multiplies every negative thought gained (2x by default).
- digtype: designates every tile of the same type of vein on the map for 'digging' (any dig designation).
New tweaks
----------
- tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus.
- tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does.
- tweak readable-build-plate: fix unreadable truncation in unit pressure plate build ui.
- tweak stable-temp: fixes bug 6012; may improve FPS by 50-100% on a slow item-heavy fort.
- tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster.
- tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc.
- tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode.
- tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens.
- tweak military-stable-assign: Stop rightmost list of military->Positions from jumping to top.
- tweak military-color-assigned: In same list, color already assigned units in brown & green.
New scripts
-----------
- fixnaked: removes thoughts about nakedness.
- setfps: set FPS cap at runtime, in case you want slow motion or speed-up.
- siren: wakes up units, stops breaks and parties - but causes bad thoughts.
- fix/population-cap: run after every migrant wave to prevent exceeding the cap.
- fix/stable-temp: counts items with temperature updates; does instant one-shot stable-temp.
- fix/loyaltycascade: fix units allegiance, eg after ordering a dwarf merchant kill.
- deathcause: shows the circumstances of death for a given body.
- digfort: designate areas to dig from a csv file.
- drainaquifer: remove aquifers from the map.
- growcrops: cheat to make farm crops instantly grow.
- magmasource: continuously spawn magma from any map tile.
- removebadthoughts: delete all negative thoughts from your dwarves.
- slayrace: instakill all units of a given race, optionally with magma.
- superdwarf: per-creature fastdwarf.
- gui/mechanisms: browse mechanism links of the current building.
- gui/room-list: browse other rooms owned by the unit when assigning one.
- gui/liquids: a GUI front-end for the liquids plugin.
- gui/rename: renaming stockpiles, workshops and units via an in-game dialog.
- gui/power-meter: front-end for the Power Meter plugin.
- gui/siege-engine: front-end for the Siege Engine plugin.
- gui/choose-weapons: auto-choose matching weapons in the military equip screen.
New Plugins
-----------
- Dwarf Manipulator: Open the unit list, and press 'l' to access a Dwarf Therapist like UI in the game.
- Steam Engine:
Dwarven Water Reactors don't make any sense whatsoever and cause lag, so this may be
a replacement for those concerned by it. The plugin detects if a workshop with a
certain name is in the raws used by the current world, and provides the necessary
behavior. See ``hack/raw/*_steam_engine.txt`` for the necessary raw definitions.
Note: Stuff like animal treadmills might be more period, but absolutely can't be
done with tools dfhack has access to.
- Power Meter:
When activated, implements a pressure plate modification that detects power in gear
boxes built on the four adjacent N/S/W/E tiles. The gui/power-meter script implements
the necessary build configuration UI.
- Siege Engine:
When enabled and configured via gui/siege-engine, allows aiming siege engines
at a designated rectangular area with 360 degree fire range and across Z levels;
this works by rewriting the projectile trajectory immediately after it appears.
Also supports loading catapults with non-boulder projectiles, taking from a stockpile,
and restricting operator skill range like with ordinary workshops.
Disclaimer: not in any way to undermine the future siege update from Toady, but
the aiming logic of existing engines hasn't been updated since 2D, and is almost
useless above ground :(. Again, things like making siegers bring their own engines
is totally out of the scope of dfhack and can only be done by Toady.
- Add Spatter:
Detects reactions with certain names in the raws, and changes them from adding
improvements to adding item contaminants. This allows directly covering items
with poisons. The added spatters are immune both to water and 'clean items'.
Intended to give some use to all those giant cave spider poison barrels brought
by the caravans.
Older Changelogs
================
Are kept in a seperate file: `HISTORY`
.. that's ``docs/history.rst``, if you're reading the raw text.

@ -4,13 +4,13 @@
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=./docs/html/index.html">
<meta http-equiv="refresh" content="0;url=./docs/index.html">
<script type="text/javascript">
window.location.href = "./docs/html/index.html"
window.location.href = "./docs/index.html"
</script>
<title>Page Redirection</title>
</head>
<body>
Follow this <a href='./docs/html/index.html'>link to the documentation.</a>
Follow this <a href='./docs/index.html'>link to the documentation.</a>
</body>
</html>

@ -0,0 +1,6 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
mkdir VC2010
cd VC2010
echo generating a build folder
cmake ..\.. -G"Visual Studio 10" -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DF2MC=1 -DBUILD_DFUSION=1 -DBUILD_STONESENSE=1 -DBUILD_SERVER=1

@ -108,6 +108,7 @@ extlinks = {
'bug': ('http://www.bay12games.com/dwarves/mantisbt/view.php?id=%s',
'Bug '),
'issue': ('https://github.com/DFHack/dfhack/issues/%s', 'Issue '),
'commit': ('https://github.com/DFHack/dfhack/commit/%s', 'Commit '),
}
# Add any paths that contain templates here, relative to this directory.

@ -0,0 +1,4 @@
# Add additional script search paths here
# Blank lines and lines that start with "#" will be ignored
# Paths preceded by "+" will be searched first
# Paths preceded by "-" will be searched after the default paths

@ -234,11 +234,8 @@ embark-tools enable sand mouse
# Scripts #
###########
# write the correct season to gamelog on world load
soundsense-season
# write identifying information about the fort to gamelog on world load
log-region
# write extra information to the gamelog
modtools/extra-gamelog enable
# add information to item viewscreens
view-item-info enable

@ -7,7 +7,7 @@ If you should be here and aren't, please get in touch on IRC or the forums,
or make a pull request!
======================= ======================= ===========================
Name Github Other names
Name Github Other
======================= ======================= ===========================
8Z 8Z
acwatkins acwatkins
@ -91,11 +91,13 @@ Sebastian Wolfertz Enkrod
Seth Woodworth sethwoodworth
simon
Simon Jackson sizeak
sv-esk sv-esk
Tacomagic
Tim Walberg twalberg
Timothy Collett danaris
Tom Jobbins TheBloke
Tom Prince
Travis Hoppe thoppe orthographic-pedant
txtsd txtsd
U-glouglou\\simon
Valentin Ochs Cat-Ion

@ -29,8 +29,7 @@ decent skill in `memory research <contributing-memory-research>`.
* The patches are expected to be encoded in text format used by IDA.
* See `this commit <https://github.com/DFHack/dfhack/commit/8a9e3d1a728>`_,
when the 0.34.11 patches were discarded, for example patches.
* See :commit:`8a9e3d1a728` for examples.
* :issue:`546` is about the future of the binpatches, and may be useful reading.
@ -50,7 +49,7 @@ directly in memory at runtime::
binpatch [check|apply|remove] <patchname>
If the name of the patch has no extension or directory separators, the
script uses ``hack/patches/<df-version>/<name>.dif``, thus auto-selecting
script uses :file:`hack/patches/<df-version>/<name>.dif`, thus auto-selecting
the version appropriate for the currently loaded executable.
This is the preferred method; it's easier to debug, does not cause persistent

@ -42,9 +42,11 @@ Contributing to DFHack
If you want to get involved with the development, create an account on
GitHub, make a clone there and then use that as your remote repository instead.
We'd love that; join us on IRC (#dfhack channel on freenode) for discussion,
We'd love that; join us on IRC_ (#dfhack channel on freenode) for discussion,
and whenever you need help.
.. _IRC: https://webchat.freenode.net/?channels=dfhack
For lots more details on contributing to DFHack, including pull requests, code format,
and more, please see `contributing-code`.
@ -101,7 +103,7 @@ Before you can build anything, you'll also need ``cmake``. It is advisable to al
You also need perl and the XML::LibXML and XML::LibXSLT perl packages (for the code generation parts).
You should be able to find them in your distro repositories.
To build Stonesense, you'll also need OpenGL headers.
To build `stonesense`, you'll also need OpenGL headers.
Here are some package install commands for various platforms:
@ -109,15 +111,15 @@ Here are some package install commands for various platforms:
* For the required Perl modules: ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``)
* On 64-bit Ubuntu:
* On 64-bit Ubuntu::
* ``apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev:i386 libxml-libxml-perl libxml-libxslt-perl``.
apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev:i386 libxml-libxml-perl libxml-libxslt-perl
* On 32-bit Ubuntu:
* On 32-bit Ubuntu::
* ``apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev libxml-libxml-perl libxml-libxslt-perl``.
apt-get install gcc cmake git gcc-multilib g++-multilib zlib1g-dev libxml-libxml-perl libxml-libxslt-perl
* Debian-derived distros should have similar requirements.
* Debian and derived distros should have similar requirements to Ubuntu.
Build
@ -505,9 +507,8 @@ or ``RelWithDebInfo``.
Then build the ``INSTALL`` target listed under ``CMakePredefinedTargets``.
##########################
Building the documentation
##########################
==========================
DFHack documentation, like the file you are reading now, is created as .rst files,
which are in `reStructuredText (reST) <http://sphinx-doc.org/rest.html>`_ format.
@ -530,7 +531,7 @@ The main thing you lose in plain text format is hyperlinking.)
Enabling documentation building
===============================
-------------------------------
First, make sure you have followed all the necessary steps for your platform as
outlined in the rest of this document.
@ -550,7 +551,7 @@ through the GUI, or else if you want to use an alternate file, such as
Or you could just run ``cmake`` on the command line like in other platforms.
Required dependencies
=====================
---------------------
In order to build the documentation, you must have Python with Sphinx
version 1.3.1 or later. Both Python 2.x and 3.x are supported.

@ -69,6 +69,7 @@ on how to write one yet, but it should be easy enough to copy one and just follo
Other than through plugins, it is possible to use DFHack via remote access interface,
or by writing scripts in Lua or Ruby. There are plenty of examples in the scripts folder.
The `lua-api` is quite well documented.
The most important parts of DFHack are the Core, Console, Modules and Plugins.
@ -85,7 +86,9 @@ The most important parts of DFHack are the Core, Console, Modules and Plugins.
Rudimentary API documentation can be built using doxygen (see build options
in ``CMakeCache.txt`` or with ``ccmake`` or ``cmake-gui``). The full DFHack
documentation is built with Sphinx, which runs automatically at compile time.
documentation is built with Sphinx_, which runs automatically at compile time.
.. _Sphinx: http://www.sphinx-doc.org
DFHack consists of variously licensed code, but invariably weak copyleft.
The main license is zlib/libpng, some bits are MIT licensed, and some are
@ -101,14 +104,14 @@ in the ``library/xml/`` submodule.
See https://github.com/DFHack/df-structures, and the documentation linked in the index.
Data structure layouts are described in files following the ``df.\*.xml`` name pattern.
Data structure layouts are described in files following the ``df.*.xml`` name pattern.
This information is transformed by a perl script into C++ headers describing the
structures, and associated metadata for the Lua wrapper. These headers and data
are then compiled into the DFHack libraries, thus necessitating a compatibility
break every time layouts change; in return it significantly boosts the efficiency
and capabilities of DFHack code.
Global object addresses are stored in ``symbols.xml``, which is copied to the dfhack
Global object addresses are stored in :file:`symbols.xml`, which is copied to the dfhack
release package and loaded as data at runtime.
Remote access interface
@ -118,13 +121,13 @@ socket. Both the core and plugins can define remotely accessible methods. The
``dfhack-run`` command uses this interface to invoke ordinary console commands.
Currently the supported set of requests is limited, because the developers don't
know what exactly is most useful. ``remotefortressreader`` provides a fairly
comprehensive interface for visualisers such as Armok Vision.
know what exactly is most useful. `remotefortressreader` provides a fairly
comprehensive interface for visualisers such as :forums:`Armok Vision <146473>`.
Documentation Standards
=======================
DFHack documentation is built with Sphinx, and configured automatically
DFHack documentation is built with Sphinx_, and configured automatically
through CMake. If you want to build the docs *only*, use this command::
sphinx-build . docs/html
@ -134,10 +137,10 @@ there are a few important standards for completeness and consistent style. Trea
this section as a guide rather than iron law, match the surrounding text, and you'll
be fine.
Everything should be documented! For plugins, it's a work in progress - use
``docs/Plugins.rst`` for now. Core functions and general explanations should
go in the documents for that component; if it's not clear add a new section
as some may be missing.
Everything should be documented! If it's not clear *where* a particular
thing should be documented, ask on IRC or in the DFHack thread on Bay12 -
as well as getting help, you'll be providing valuable feedback that
makes it easier for future readers!
Scripts can use a custom autodoc function, based on the Sphinx ``include``
directive and Ruby docstring conventions - any lines between ``=begin`` and

@ -312,6 +312,7 @@ All matching init files will be executed in alphebetical order.
Modders often use such scripts to disable tools which should not affect
an unmodded save.
.. _other_init_files:
Other init files
----------------

@ -0,0 +1,605 @@
:orphan:
.. _HISTORY:
########################
HISTORY - old changelogs
########################
This file is where old changelogs live, so the current `changelog`
in ``NEWS.rst`` doesn't get too long.
.. contents::
:depth: 2
DFHack 0.40.23-r1
=================
Internals
- plugins will not be loaded if globals they specify as required are not located (should prevent some crashes)
Fixes
-----
- Fixed numerous (mostly Lua-related) crashes on OS X by including a more up-to-date libstdc++
- :kbd:`Alt` should no longer get stuck on Windows (and perhaps other platforms as well)
- `gui/advfort` works again
- `autobutcher`: takes sexualities into account
- devel/export-dt-ini: Updated for 0.40.20+
- `digfort`: now checks file type and existence
- `exportlegends`: Fixed map export
- `full-heal`: Fixed a problem with selecting units in the GUI
- `gui/hack-wish`: Fixed restrictive material filters
- `mousequery`: Changed box-select key to Alt+M
- `dwarfmonitor`: correct date display (month index, separator)
- `putontable`: added to the readme
- `siren` should work again
- stderr.log: removed excessive debug output on OS X
- `trackstop`: No longer prevents cancelling the removal of a track stop or roller.
- Fixed a display issue with ``PRINT_MODE:TEXT``
- Fixed a symbol error (MapExtras::BiomeInfo::MAX_LAYERS) when compiling DFHack in Debug mode
New Plugins
-----------
- `fortplan`: designate construction of (limited) buildings from .csv file, quickfort-style
New Scripts
-----------
- `gui/stockpiles`: an in-game interface for saving and loading stockpile settings files.
- `position`: Reports the current date, time, month, and season, plus some location info. Port/update of position.py
- `hfs-pit`: Digs a hole to hell under the cursor. Replaces needs_porting/hellhole.cpp
Removed
-------
- embark.lua: Obsolete, use `embark-tools`
New tweaks
----------
- `eggs-fertile <tweak>`: Displays an egg fertility indicator on nestboxes
- `max-wheelbarrow <tweak>`: Allows assigning more than 3 wheelbarrows to a stockpile
Misc Improvements
-----------------
- `embark-tools`: Added basic mouse support on the local map
- Made some adventure mode keybindings in :file:`dfhack.init-example` only work in adventure mode
- `gui/companion-order`: added a default keybinding
- further work on needs_porting
DFHack 0.40.19-r1
=================
Fixes
-----
- `modtools/reaction-trigger`: fixed typo
- `modtools/item-trigger`: should now work with item types
New plugins
-----------
- `savestock, loadstock <stocksettings>`: save and load stockpile settings across worlds and saves
New scripts
-----------
- `remove-stress`: set selected or all units unit to -1,000,000 stress (this script replaces removebadthoughts)
Misc improvements
-----------------
- `command-prompt`: can now access selected items, units, and buildings
- `autolabor`: add an optional talent pool parameter
DFHack 0.40.16-r1
=================
Internals
---------
- `EventManager` should handle INTERACTION triggers a little better. It still can get confused about who did what but only rarely.
- `EventManager` should no longer trigger REPORT events for old reports after loading a save.
- lua/persist-table: a convenient way of using persistent tables of arbitrary structure and dimension in Lua
Fixes
-----
- `mousequery`: Disabled when linking levers
- `stocks`: Melting should work now
- `full-heal`: Updated with proper argument handling
- `modtools/reaction-trigger-transition`: should produce the correct syntax now
- `superdwarf`: should work better now
- `forum-dwarves`: update for new df-structures changes
New Scripts
-----------
- `adaptation`: view or set the cavern adaptation level of your citizens
- `add-thought`: allows the user to add thoughts to creatures.
- `gaydar`: detect the sexual orientation of units on the map
- `markdown`: Save a copy of a text screen in markdown (for reddit among others).
- devel/all-bob: renames everyone Bob to help test interaction-trigger
Misc Improvements
-----------------
- `autodump`: Can now mark a stockpile for auto-dumping (similar to `automelt` and `autotrade`)
- `buildingplan`: Can now auto-allocate rooms to dwarves with specific positions (e.g. expedition leader, mayor)
- `dwarfmonitor`: now displays a weather indicator and date
- lua/syndrome-util, `modtools/add-syndrome`: now you can remove syndromes by SYN_CLASS
- No longer write empty :file:`.history` files
DFHack 0.40.15-r1
=================
Fixes
-----
- mousequery: Fixed behavior when selecting a tile on the lowest z-level
Misc Improvements
-----------------
- `EventManager`: deals with frame_counter getting reset properly now.
- `modtools/item-trigger`: fixed equip/unequip bug and corrected minor documentation error
- `teleport`: Updated with proper argument handling and proper unit-at-destination handling.
- `autotrade`: Removed the newly obsolete :guilabel:`Mark all` functionality.
- `search-plugin`: Adapts to the new trade screen column width
- `tweak fast-trade <tweak>`: Switching the fast-trade keybinding to Shift-Up/Shift-Down, due to Select All conflict
DFHack 0.40.14-r1
=================
Internals
---------
- The DFHack console can now be disabled by setting the DFHACK_DISABLE_CONSOLE environment variable: ``DFHACK_DISABLE_CONSOLE=1 ./dfhack``
Fixes
-----
- Stopped duplicate load/unload events when unloading a world
- Stopped ``-e`` from being echoed when DFHack quits on Linux
- `automelt`: now uses a faster method to locate items
- `autotrade`: "Mark all" no longer double-marks bin contents
- `drain-aquifer`: new script replaces the buggy plugin
- `embark-tools`: no longer conflicts with keys on the notes screen
- `fastdwarf`: Fixed problems with combat/attacks
- `forum-dwarves`: should work now
- `manipulator`: now uses a stable sort, allowing sorting by multiple categories
- `rendermax`: updated to work with 0.40
New Plugins
-----------
- `trackstop`: Shows track stop friction and dump direction in its :kbd:`q` menu
New Tweaks
----------
- farm-plot-select: Adds "Select all" and "Deselect all" options to farm plot menus
- import-priority-category: Allows changing the priority of all goods in a category when discussing an import agreement with the liaison
- manager-quantity: Removes the limit of 30 jobs per manager order
- civ-view-agreement: Fixes overlapping text on the "view agreement" screen
- nestbox-color: Fixes the color of built nestboxes
Misc Improvements
-----------------
- `exportlegends`: can now handle site maps
DFHack 0.40.13-r1
=================
Internals
---------
- unified spatter structs
- added ruby df.print_color(color, string) method for dfhack console
Fixes
-----
- no more ``-e`` after terminating
- fixed `superdwarf`
DFHack 0.40.12-r1
=================
Internals
---------
- support for global `onLoad.init` and `onUnload.init` files, called when loading and unloading a world
- Close file after loading a `binary patch <binpatches>`.
New Plugins
-----------
- `hotkeys`: Shows ingame viewscreen with all dfhack keybindings active in current mode.
- `automelt`: allows marking stockpiles so any items placed in them will be designated for melting
Fixes
-----
- possible crash fixed for `gui/hack-wish`
- `search-plugin`: updated to not conflict with BUILDJOB_SUSPEND
- `workflow`: job_material_category -> dfhack_material_category
Misc Improvements
-----------------
- now you can use ``@`` to print things in interactive Lua with subtley different semantics
- optimizations for stockpiles for `autotrade` and `stockflow`
- updated `exportlegends` to work with new maps, dfhack 40.11 r1+
DFHack 0.40.11-r1
=================
Internals
- Plugins on OS X now use ``.plug.dylib` as an extension instead of ``.plug.so``
Fixes
-----
- `3dveins`: should no longer hang/crash on specific maps
- `autotrade`, `search-plugin`: fixed some layout issues
- `deathcause`: updated
- `gui/hack-wish`: should work now
- `reveal`: no longer allocates data for nonexistent map blocks
- Various documentation fixes and updates
DFHack v0.40.10-r1
==================
A few bugfixes.
DFHack v0.40.08-r2
==================
Internals
---------
- supported per save script folders
- Items module: added createItem function
- Sorted CMakeList for plugins and plugins/devel
- `diggingInvaders` no longer builds if plugin building is disabled
- `EventManager`: EQUIPMENT_CHANGE now triggers for new units. New events::
ON_REPORT
UNIT_ATTACK
UNLOAD
INTERACTION
New Scripts
-----------
- lua/repeat-util: makes it easier to make things repeat indefinitely
- lua/syndrome-util: makes it easier to deal with unit syndromes
- `forum-dwarves`: helps copy df viewscreens to a file
- `full-heal`: fully heal a unit
- `remove-wear`: removes wear from all items in the fort
- `repeat`: repeatedly calls a script or a plugin
- ShowUnitSyndromes: shows syndromes affecting units and other relevant info
- `teleport`: teleports units
- `devel/print-args`
- `fix/blood-del`: makes it so civs don't bring barrels full of blood ichor or goo
- `fix/feeding-timers`: reset the feeding timers of all units
- `gui/hack-wish`: creates items out of any material
- `gui/unit-info-viewer`: displays information about units
- `modtools/add-syndrome`: add a syndrome to a unit or remove one
- `modtools/anonymous-script`: execute an lua script defined by a string. Useful for the ``*-trigger`` scripts.
- `modtools/force`: forces events: caravan, migrants, diplomat, megabeast, curiousbeast, mischievousbeast, flier, siege, nightcreature
- `modtools/item-trigger`: triggers commands based on equipping, unequipping, and wounding units with items
- `modtools/interaction-trigger`: triggers commands when interactions happen
- `modtools/invader-item-destroyer`: destroys invaders' items when they die
- `modtools/moddable-gods`: standardized version of Putnam's moddable gods script
- `modtools/projectile-trigger`: standardized version of projectileExpansion
- `modtools/reaction-trigger`: trigger commands when custom reactions complete; replaces autoSyndrome
- `modtools/reaction-trigger-transition`: a tool for converting mods from autoSyndrome to reaction-trigger
- `modtools/random-trigger`: triggers random scripts that you register
- `modtools/skill-change`: for incrementing and setting skills
- `modtools/spawn-flow`: creates flows, like mist or dragonfire
- `modtools/syndrome-trigger`: trigger commands when syndromes happen
- `modtools/transform-unit`: shapeshifts a unit, possibly permanently
Misc improvements
-----------------
- new function in utils.lua for standardized argument processing
Removed
-------
- digmat.rb: digFlood does the same functionality with less FPS impact
- invasionNow: `modtools/force` does it better
- autoSyndrome replaced with `modtools/reaction-trigger`
- syndromeTrigger replaced with `modtools/syndrome-trigger`
- devel/printArgs plugin converted to `devel/print-args`
- outsideOnly plugin replaced by `modtools/outside-only`
DFHack v0.40.08-r1
==================
Was a mistake. Don't use it.
DFHack v0.34.11-r5
==================
Internals
---------
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
- support for basic filesystem operations (e.g. chdir, mkdir, rmdir, stat) in C++ and Lua
- Lua API for listing files in directory. Needed for `gui/mod-manager`
- Lua API for creating unit combat reports and writing to gamelog.
- Lua API for running arbitrary DFHack commands
- support for multiple ``raw/init.d/*.lua`` init scripts in one save.
- eventful now has a more friendly way of making custom sidebars
- on Linux and OS X the console now supports moving the cursor back and forward by a whole word.
New scripts
-----------
- `gui/mod-manager`: allows installing/uninstalling mods into df from ``df/mods`` directory.
- `gui/clone-uniform`: duplicates the currently selected uniform in the military screen.
- `fix/build-location`: partial work-around for :bug:`5991` (trying to build wall while standing on it)
- `undump-buildings`: removes dump designation from materials used in buildings.
- `exportlegends`: exports data from legends mode, allowing a set-and-forget export of large worlds.
- log-region: each time a fort is loaded identifying information will be written to the gamelog.
- `dfstatus <gui/dfstatus>`: show an overview of critical stock quantities, including food, drinks, wood, and bars.
- `command-prompt`: a dfhack command prompt in df.
New plugins
-----------
- `rendermax`: replace the renderer with something else, eg ``rendermax light``- a lighting engine
- `automelt`: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting)
- `embark-tools`: implementations of Embark Anywhere, Nano Embark, and a few other embark-related utilities
- `building-hacks`: Allows to add custom functionality and/or animations to buildings.
- `petcapRemover`: triggers pregnancies in creatures so that you can effectively raise the default pet population cap
- `plant create <plant>`: spawn a new shrub under the cursor
New tweaks
----------
- craft-age-wear: make crafted items wear out with time like in old versions (:bug:`6003`)
- adamantine-cloth-wear: stop adamantine clothing from wearing out (:bug:`6481`)
- confirm-embark: adds a prompt before embarking (on the "prepare carefully" screen)
Misc improvements
-----------------
- `plant`: move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- `digfort`: improved csv parsing, add start() comment handling
- `exterminate`: allow specifying a caste (exterminate gob:male)
- `createitem`: in adventure mode it now defaults to the controlled unit as maker.
- `autotrade`: adds "(Un)mark All" options to both panes of trade screen.
- `mousequery`: several usability improvements; show live overlay (in menu area) of what's on the tile under the mouse cursor.
- `search`: workshop profile search added.
- `dwarfmonitor`: add screen to summarise preferences of fortress dwarfs.
- `getplants`: add autochop function to automate woodcutting.
- `stocks`: added more filtering and display options.
- `siege-engine`:
- engine quality and distance to target now affect accuracy
- firing the siege engine at a target produces a combat report
- improved movement speed computation for meandering units
- operators in Prepare To Fire mode are released from duty once hungry/thirsty if there is a free replacement
DFHack v0.34.11-r4
==================
New commands
------------
- `diggingInvaders` - allows invaders to dig and/or deconstruct walls and buildings in order to get at your dwarves.
- `digFlood` - automatically dig out specified veins as they are revealed
- `enable, disable <enable>` - Built-in commands that can be used to enable/disable many plugins.
- `restrictice` - Restrict traffic on squares above visible ice.
- `restrictliquids` - Restrict traffic on every visible square with liquid.
- treefarm - automatically chop trees and dig obsidian
New Scripts
-----------
- `autobutcher`: A GUI front-end for the autobutcher plugin.
- invasionNow: trigger an invasion, or many
- `locate-ore`: scan the map for unmined ore veins
- `masspit`: designate caged creatures in a zone for pitting
- `multicmd`: run a sequence of dfhack commands, separated by ';'
- `startdwarf`: change the number of dwarves for a new embark
- digmat: dig veins/layers tile by tile, as discovered
Misc improvements
-----------------
- autoSyndrome:
- disable by default
- reorganized special tags
- minimized error spam
- reset policies: if the target already has an instance of the syndrome you can skip,
add another instance, reset the timer, or add the full duration to the time remaining
- core: fix SC_WORLD_(UN)LOADED event for arena mode
- `exterminate`: renamed from slayrace, add help message, add butcher mode
- `fastdwarf`: fixed bug involving fastdwarf and teledwarf being on at the same time
- magmasource: rename to `source`, allow water/magma sources/drains
- Add df.dfhack_run "somecommand" to Ruby
- syndromeTrigger: replaces and extends trueTransformation. Can trigger things when syndromes are added for any reason.
- `tiletypes`: support changing tile material to arbitrary stone.
- `workNow`: can optionally look for jobs when jobs are completed
New tweaks
----------
- hive-crash: Prevent crash if bees die in a hive with ungathered products (:bug:`6368`).
New plugins
-----------
- `3dveins`: Reshapes all veins on the map in a way that flows between Z levels. May be unstable. Backup before using.
- `autotrade`: Automatically send items in marked stockpiles to trade depot, when trading is possible.
- `buildingplan`: Place furniture before it's built
- `dwarfmonitor`: Records dwarf activity to measure fort efficiency
- `mousequery`: Look and poke at the map elements with the mouse.
- outsideOnly: make raw-specified buildings impossible to build inside
- `resume`: A plugin to help display and resume suspended constructions conveniently
- `stocks`: An improved stocks display screen.
Internals
---------
- Core: there is now a per-save dfhack.init file for when the save is loaded, and another for when it is unloaded
- EventManager: fixed job completion detection, fixed removal of TICK events, added EQUIPMENT_CHANGE event
- Lua API for a better `random number generator <lua_api_random>` and perlin noise functions.
- Once: easy way to make sure something happens once per run of DF, such as an error message
DFHack v0.34.11-r3
==================
Internals
---------
- support for displaying active keybindings properly.
- support for reusable widgets in lua screen library.
- Maps::canStepBetween: returns whether you can walk between two tiles in one step.
- EventManager: monitors various in game events centrally so that individual plugins
don't have to monitor the same things redundantly.
- Now works with OS X 10.6.8
Notable bugfixes
----------------
- `autobutcher` can be re-enabled again after being stopped.
- stopped `Dwarf Manipulator <manipulator>` from unmasking vampires.
- `stonesense` is now fixed on OS X
Misc improvements
-----------------
- `fastdwarf`: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing `binary patches <binpatches>`.
- removebadthoughts: add --dry-run option
- `superdwarf`: work in adventure mode too
- `tweak` stable-cursor: carries cursor location from/to Build menu.
- `deathcause`: allow selection from the unitlist screen
- slayrace: allow targetting undeads
- `workflow` plugin:
- properly considers minecarts assigned to routes busy.
- code for deducing job outputs rewritten in lua for flexibility.
- logic fix: collecting webs produces silk, and ungathered webs are not thread.
- items assigned to squads are considered busy, even if not in inventory.
- shearing and milking jobs are supported, but only with generic MILK or YARN outputs.
- workflow announces when the stock level gets very low once a season.
- Auto syndrome plugin: A way of automatically applying boiling rock syndromes and calling dfhack commands controlled by raws.
- `infiniteSky` plugin: Create new z-levels automatically or on request.
- True transformation plugin: A better way of doing permanent transformations that allows later transformations.
- `workNow` plugin: Makes the game assign jobs every time you pause.
New tweaks
----------
- tweak military-training: speed up melee squad training up to 10x (normally 3-5x).
New scripts
-----------
- `binpatch`: the same as the stand-alone binpatch.exe, but works at runtime.
- region-pops: displays animal populations of the region and allows tweaking them.
- `lua`: lua interpreter front-end converted to a script from a native command.
- dfusion: misc scripts with a text based menu.
- embark: lets you embark anywhere.
- `lever`: list and pull fort levers from the dfhack console.
- `stripcaged`: mark items inside cages for dumping, eg caged goblin weapons.
- soundsense-season: writes the correct season to gamelog.txt on world load.
- create-items: spawn items
- fix/cloth-stockpile: fixes :bug:`5739`; needs to be run after savegame load every time.
New GUI scripts
---------------
- `gui/guide-path`: displays the cached path for minecart Guide orders.
- `gui/workshop-job`: displays inputs of a workshop job and allows tweaking them.
- `gui/workflow`: a front-end for the workflow plugin (part inspired by falconne).
- `gui/assign-rack`: works together with a binary patch to fix weapon racks.
- `gui/gm-editor`: an universal editor for lots of dfhack things.
- `gui/companion-order`: a adventure mode command interface for your companions.
- `gui/advfort`: a way to do jobs with your adventurer (e.g. build fort).
New binary patches
------------------
(for use with `binpatch`)
- armorstand-capacity: doubles the capacity of armor stands.
- custom-reagent-size: lets custom reactions use small amounts of inputs.
- deconstruct-heapfall: stops some items still falling on head when deconstructing.
- deconstruct-teleport: stops items from 16x16 block teleporting when deconstructing.
- hospital-overstocking: stops hospital overstocking with supplies.
- training-ammo: lets dwarves with quiver full of combat-only ammo train.
- weaponrack-unassign: fixes bug that negates work done by gui/assign-rack.
New Plugins
-----------
- `fix-armory`: Together with a couple of binary patches and the `gui/assign-rack` script, this plugin makes weapon racks, armor stands, chests and cabinets in properly designated barracks be used again for storage of squad equipment.
- `search`: Adds an incremental search function to the Stocks, Trading, Stockpile and Unit List screens.
- `automaterial`: Makes building constructions (walls, floors, fortifications, etc) a little bit easier by saving you from having to trawl through long lists of materials each time you place one.
- Dfusion: Reworked to make use of lua modules, now all the scripts can be used from other scripts.
- Eventful: A collection of lua events, that will allow new ways to interact with df world.
DFHack v0.34.11-r2
==================
Internals
---------
- full support for Mac OS X.
- a plugin that adds scripting in `ruby <rb>`.
- support for interposing virtual methods in DF from C++ plugins.
- support for creating new interface screens from C++ and lua.
- added various other API functions.
Notable bugfixes
----------------
- better terminal reset after exit on linux.
- `seedwatch` now works on reclaim.
- the sort plugin won't crash on cages anymore.
Misc improvements
-----------------
- `autodump`: can move items to any walkable tile, not just floors.
- `stripcaged`: by default keep armor, new dumparmor option.
- `zone`: allow non-domesticated birds in nestboxes.
- `workflow`: quality range in constraints.
- cleanplants: new command to remove rain water from plants.
- `liquids`: can paint permaflow, i.e. what makes rivers power water wheels.
- `prospect`: pre-embark prospector accounts for caves & magma sea in its estimate.
- `rename`: supports renaming stockpiles, workshops, traps, siege engines.
- `fastdwarf`: now has an additional option to make dwarves teleport to their destination.
- `autolabor`:
- can set nonidle hauler percentage.
- broker excluded from all labors when needed at depot.
- likewise, anybody with a scheduled diplomat meeting.
New commands
------------
- misery: multiplies every negative thought gained (2x by default).
- `digtype`: designates every tile of the same type of vein on the map for 'digging' (any dig designation).
New tweaks
----------
- tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus.
- tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does.
- tweak readable-build-plate: fix unreadable truncation in unit pressure plate build ui.
- tweak stable-temp: fixes bug 6012; may improve FPS by 50-100% on a slow item-heavy fort.
- tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster.
- tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc.
- tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode.
- tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens.
- tweak military-stable-assign: Stop rightmost list of military->Positions from jumping to top.
- tweak military-color-assigned: In same list, color already assigned units in brown & green.
New scripts
-----------
- `fixnaked`: removes thoughts about nakedness.
- `setfps`: set FPS cap at runtime, in case you want slow motion or speed-up.
- `siren`: wakes up units, stops breaks and parties - but causes bad thoughts.
- `fix/population-cap`: run after every migrant wave to prevent exceeding the cap.
- `fix/stable-temp`: counts items with temperature updates; does instant one-shot stable-temp.
- `fix/loyaltycascade`: fix units allegiance, eg after ordering a dwarf merchant kill.
- `deathcause`: shows the circumstances of death for a given body.
- `digfort`: designate areas to dig from a csv file.
- `drain-aquifer`: remove aquifers from the map.
- `growcrops`: cheat to make farm crops instantly grow.
- magmasource: continuously spawn magma from any map tile.
- removebadthoughts: delete all negative thoughts from your dwarves.
- slayrace: instakill all units of a given race, optionally with magma.
- `superdwarf`: per-creature `fastdwarf`.
- `gui/mechanisms`: browse mechanism links of the current building.
- `gui/room-list`: browse other rooms owned by the unit when assigning one.
- `gui/liquids`: a GUI front-end for the liquids plugin.
- `gui/rename`: renaming stockpiles, workshops and units via an in-game dialog.
- `gui/power-meter`: front-end for the Power Meter plugin.
- `gui/siege-engine`: front-end for the Siege Engine plugin.
- `gui/choose-weapons`: auto-choose matching weapons in the military equip screen.
New Plugins
-----------
- `manipulator`: a Dwarf Therapist like UI in the game (:kbd:`u`:kbd:`l`)
- `steam-engine`: an alternative to Water Reactors which make more sense.
See ``hack/raw/*_steam_engine.txt`` for the necessary raw definitions.
- `power-meter`: a pressure plate modification to detect powered gear
boxes on adjacent tiles. `gui/power-meter` implements
the build configuration UI.
- `siege-engine`: massive overhaul for siege engines, configured via `gui/siege-engine`
- `add-spatter`: allows poison coatings via raw reactions, among other things.

@ -1,11 +1,13 @@
.. _lua-api:
##############
DFHack Lua API
##############
.. contents::
DFHack has extensive support for
the Lua_ scripting language, providing access to:
The current version of DFHack has extensive support for
the Lua scripting language, providing access to:
.. _Lua: http://www.lua.org
1. Raw data structures used by the game.
2. Many C++ functions for high-level access to these
@ -14,26 +16,36 @@ the Lua scripting language, providing access to:
Lua code can be used both for writing scripts, which
are treated by DFHack command line prompt almost as
native C++ commands, and invoked by plugins written in c++.
native C++ commands, and invoked by plugins written in C++.
This document describes native API available to Lua in detail.
It does not describe all of the utility functions
implemented by Lua files located in hack/lua/...
implemented by Lua files located in :file:`hack/lua/*`
(:file:`library/lua/*` in the git repo).
.. contents::
:depth: 3
=========================
DF data structure wrapper
=========================
Data structures of the game are defined in XML files located in library/xml
(and online at http://github.com/DFHack/df-structures), and automatically exported
.. contents::
:local:
Data structures of the game are defined in XML files located in :file:`library/xml`
(and `online <http://github.com/DFHack/df-structures>`_, and automatically exported
to lua code as a tree of objects and functions under the ``df`` global, which
also broadly maps to the ``df`` namespace in the headers generated for C++.
**WARNING**: The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
crash the game as equivalent plain C++ code would be. E.g. NULL
pointer access is safely detected, but dangling pointers aren't.
.. warning::
The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
crash the game as equivalent plain C++ code would be.
eg. NULL pointer access is safely detected, but dangling pointers aren't.
Objects managed by the wrapper can be broadly classified into the following groups:
@ -86,10 +98,11 @@ All typed objects have the following built-in features:
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.
**WARNING**: a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.
.. warning::
a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.
* ``ref._kind``
@ -115,8 +128,9 @@ All typed objects have the following built-in features:
Destroys the object with the C++ ``delete`` operator.
If destructor is not available, returns *false*.
**WARNING**: the lua reference object remains as a dangling
pointer, like a raw C++ pointer would.
.. warning::
the lua reference object remains as a dangling
pointer, like a raw C++ pointer would.
* ``ref:assign(object)``
@ -158,16 +172,18 @@ They implement the following features:
Primitive typed fields, i.e. numbers & strings, are converted
to/from matching lua values. The value of a pointer is a reference
to the target, or nil/NULL. Complex types are represented by
to the target, or ``nil``/NULL. Complex types are represented by
a reference to the field within the structure; unless recursive
lua table assignment is used, such fields can only be read.
**NOTE:** In case of inheritance, *superclass* fields have precedence
over the subclass, but fields shadowed in this way can still
be accessed as ``ref['subclasstype.field']``.
This shadowing order is necessary because vtable-based classes
are automatically exposed in their exact type, and the reverse
rule would make access to superclass fields unreliable.
.. note::
In case of inheritance, *superclass* fields have precedence
over the subclass, but fields shadowed in this way can still
be accessed as ``ref['subclasstype.field']``.
This shadowing order is necessary because vtable-based classes
are automatically exposed in their exact type, and the reverse
rule would make access to superclass fields unreliable.
* ``ref._field(field)``
@ -183,7 +199,7 @@ They implement the following features:
* ``pairs(ref)``
Enumerates all real fields (but not methods) in memory
(= declaration) order.
order, which is the same as declaration order.
Container references
--------------------
@ -278,7 +294,7 @@ All types and the global object have the following features:
* ``type._identity``
Contains a lightuserdata pointing to the underlying
DFHack::type_instance object.
``DFHack::type_instance`` object.
Types excluding the global object also support:
@ -294,8 +310,8 @@ Types excluding the global object also support:
Returns true if object is same or subclass type, or a reference
to an object of same or subclass type. It is permissible to pass
nil, NULL or non-wrapper value as object; in this case the
method returns nil.
``nil``, NULL or non-wrapper value as object; in this case the
method returns ``nil``.
In addition to this, enum and bitfield types contain a
bi-directional mapping between key strings and values, and
@ -362,6 +378,7 @@ The ``df`` table itself contains the following functions and values:
Returns *nil* if NULL, or a ref.
.. _lua-api-table-assignment:
Recursive table assignment
==========================
@ -437,7 +454,7 @@ on the type of the field being assigned to:
{ resize=false, [idx]=value }
Since nil inside a table is indistinguishable from missing key,
Since ``nil`` inside a table is indistinguishable from missing key,
it is necessary to use ``df.NULL`` as a null pointer value.
This system is intended as a way to define a nested object
@ -452,6 +469,9 @@ cleanup.
DFHack API
==========
.. contents::
:local:
DFHack utility functions are placed in the ``dfhack`` global tree.
Native utilities
@ -722,6 +742,8 @@ Functions:
Checks if the material matches job_material_category or job_item.
Accept dfhack_material_category auto-assign table.
.. _lua_api_random:
Random number generation
------------------------
@ -798,11 +820,12 @@ can be omitted.
Return information about the DFHack build in use.
**Note:** ``getCompiledDFVersion()`` returns the DF version specified at compile time,
while ``getDFVersion()`` returns the version and typically the OS as well.
These do not necessarily match - for example, DFHack 0.34.11-r5 worked with
DF 0.34.10 and 0.34.11, so the former function would always return ``0.34.11``
while the latter would return ``v0.34.10 <platform>`` or ``v0.34.11 <platform>``.
.. note::
``getCompiledDFVersion()`` returns the DF version specified at compile time,
while ``getDFVersion()`` returns the version and typically the OS as well.
These do not necessarily match - for example, DFHack 0.34.11-r5 worked with
DF 0.34.10 and 0.34.11, so the former function would always return ``0.34.11``
while the latter would return ``v0.34.10 <platform>`` or ``v0.34.11 <platform>``.
* ``dfhack.getDFPath()``
@ -875,7 +898,7 @@ Gui module
* ``dfhack.gui.getSelectedWorkshopJob([silent])``
When a job is selected in *'q'* mode, returns the job, else
When a job is selected in :kbd:`q` mode, returns the job, else
prints error unless silent and returns *nil*.
* ``dfhack.gui.getSelectedJob([silent])``
@ -884,31 +907,31 @@ Gui module
* ``dfhack.gui.getSelectedUnit([silent])``
Returns the unit selected via *'v'*, *'k'*, unit/jobs, or
Returns the unit selected via :kbd:`v`, :kbd:`k`, unit/jobs, or
a full-screen item view of a cage or suchlike.
* ``dfhack.gui.getSelectedItem([silent])``
Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or
Returns the item selected via :kbd:`v` ->inventory, :kbd:`k`, :kbd:`t`, or
a full-screen item view of a container. Note that in the
last case, the highlighted *contained item* is returned, not
the container itself.
* ``dfhack.gui.getSelectedBuilding([silent])``
Returns the building selected via *'q'*, *'t'*, *'k'* or *'i'*.
Returns the building selected via :kbd:`q`, :kbd:`t`, :kbd:`k` or :kbd:`i`.
* ``dfhack.gui.writeToGamelog(text)``
Writes a string to gamelog.txt without doing an announcement.
Writes a string to :file:`gamelog.txt` without doing an announcement.
* ``dfhack.gui.makeAnnouncement(type,flags,pos,text,color[,is_bright])``
Adds an announcement with given announcement_type, text, color, and brightness.
The is_bright boolean actually seems to invert the brightness.
The announcement is written to gamelog.txt. The announcement_flags
argument provides a custom set of announcements.txt options,
The announcement is written to :file:`gamelog.txt`. The announcement_flags
argument provides a custom set of :file:`announcements.txt` options,
which specify if the message should actually be displayed in the
announcement list, and whether to recenter or show a popup.
@ -1003,7 +1026,7 @@ Job module
* ``dfhack.job.linkIntoWorld(job,new_id)``
Adds job into ``df.global.job_list``, and if new_id
is true, then also sets it's id and increases
is true, then also sets its id and increases
``df.global.job_next_id``
* ``dfhack.job.listNewlyCreated(first_id)``
@ -1161,7 +1184,7 @@ Items module
* ``dfhack.items.getDescription(item, type[, decorate])``
Returns the string description of the item, as produced by the getItemDescription
Returns the string description of the item, as produced by the ``getItemDescription``
method. If decorate is true, also adds markings for quality and improvements.
* ``dfhack.items.getGeneralRef(item, type)``
@ -1205,10 +1228,15 @@ Items module
Move the item to the container. Returns *false* if impossible.
* ``dfhack.items.moveToBuilding(item,building,use_mode)``
* ``dfhack.items.moveToBuilding(item,building[,use_mode[,force_in_building])``
Move the item to the building. Returns *false* if impossible.
``use_mode`` defaults to 0. If set to 2, the item will be treated as part of the building.
If ``force_in_building`` is true, the item will be considered to be stored by the building
(used for items temporarily used in traps in vanilla DF)
* ``dfhack.items.moveToInventory(item,unit,use_mode,body_part)``
Move the item to the unit inventory. Returns *false* if impossible.
@ -1305,12 +1333,14 @@ Maps module
* ``dfhack.maps.canWalkBetween(pos1, pos2)``
Checks if a dwarf may be able to walk between the two tiles,
using a pathfinding cache maintained by the game. Note that
this cache is only updated when the game is unpaused, and thus
can get out of date if doors are forbidden or unforbidden, or
tools like liquids or tiletypes are used. It also cannot possibly
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.
using a pathfinding cache maintained by the game.
.. note::
This cache is only updated when the game is unpaused, and thus
can get out of date if doors are forbidden or unforbidden, or
tools like `liquids` or `tiletypes` are used. It also cannot possibly
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.
* ``dfhack.maps.hasTileAssignment(tilemask)``
@ -1376,6 +1406,9 @@ Burrows module
Buildings module
----------------
General
~~~~~~~
* ``dfhack.buildings.getGeneralRef(building, type)``
Searches for a general_ref with the given type.
@ -1434,7 +1467,9 @@ Buildings module
Returns a list of items stored on the given stockpile.
Ignores empty bins, barrels, and wheelbarrows assigned as storage and transport for that stockpile.
Low-level building creation functions;
Low-level
~~~~~~~~~
Low-level building creation functions:
* ``dfhack.buildings.allocInstance(pos, type, subtype, custom)``
@ -1477,6 +1512,8 @@ Low-level building creation functions;
Destroys the building, or queues a deconstruction job.
Returns *true* if the building was destroyed and deallocated immediately.
High-level
~~~~~~~~~~
More high-level functions are implemented in lua and can be loaded by
``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``.
@ -1503,12 +1540,13 @@ Among them are:
Creates a building in one call, using options contained
in the argument table. Returns the building, or *nil, error*.
**NOTE:** Despite the name, unless the building is abstract,
the function creates it in an 'unconstructed' stage, with
a queued in-game job that will actually construct it. I.e.
the function replicates programmatically what can be done
through the construct building menu in the game ui, except
that it does less environment constraint checking.
.. note::
Despite the name, unless the building is abstract,
the function creates it in an 'unconstructed' stage, with
a queued in-game job that will actually construct it. I.e.
the function replicates programmatically what can be done
through the construct building menu in the game ui, except
that it does less environment constraint checking.
The following options can be used:
@ -1707,8 +1745,9 @@ In order to actually be able to paint to the screen, it is necessary
to create and register a viewscreen (basically a modal dialog) with
the game.
**NOTE**: As a matter of policy, in order to avoid user confusion, all
interface screens added by dfhack should bear the "DFHack" signature.
.. warning::
As a matter of policy, in order to avoid user confusion, all
interface screens added by dfhack should bear the "DFHack" signature.
Screens are managed with the following functions:
@ -1730,7 +1769,7 @@ Apart from a native viewscreen object, these functions accept a table
as a screen. In this case, ``show`` creates a new native viewscreen
that delegates all processing to methods stored in that table.
**NOTE**: Lua-implemented screens are only supported in the core context.
.. note:: Lua-implemented screens are only supported in the core context.
Supported callbacks and fields are:
@ -2017,8 +2056,9 @@ and are only documented here for completeness:
Searches script paths for the script ``name`` and returns the path of the first
file found, or ``nil`` on failure.
Note: This requires an extension to be specified (``.lua`` or ``.rb``) -
use ``dfhack.findScript()`` to include the ``.lua`` extension automatically.
.. note::
This requires an extension to be specified (``.lua`` or ``.rb``) - use
``dfhack.findScript()`` to include the ``.lua`` extension automatically.
Core interpreter context
========================
@ -2078,8 +2118,9 @@ Features:
Sets the function as one of the listeners. Assign *nil* to remove it.
**NOTE**: The ``df.NULL`` key is reserved for the use by
the C++ owner of the event; it is an error to try setting it.
.. note::
The ``df.NULL`` key is reserved for the use by
the C++ owner of the event; it is an error to try setting it.
* ``#event``
@ -2099,8 +2140,11 @@ Features:
Lua Modules
===========
.. contents::
:local:
DFHack sets up the lua interpreter so that the built-in ``require``
function can be used to load shared lua code from hack/lua/.
function can be used to load shared lua code from :file:`hack/lua/`.
The ``dfhack`` namespace reference itself may be obtained via
``require('dfhack')``, although it is initially created as a
global by C++ bootstrap code.
@ -2138,12 +2182,12 @@ environment by the mandatory init file dfhack.lua:
* Color constants
These are applicable both for ``dfhack.color()`` and color fields
in DF functions or structures:
in DF functions or structures::
COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY,
COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED,
COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE
COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY,
COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED,
COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE
* ``dfhack.onStateChange`` event codes
@ -2158,8 +2202,10 @@ environment by the mandatory init file dfhack.lua:
* Miscellaneous constants
:NEWLINE, COMMA, PERIOD: evaluate to the relevant character strings.
:DEFAULT_NIL: is an unspecified unique token used by the class module below.
``NEWLINE``, ``COMMA``, ``PERIOD``
evaluate to the relevant character strings.
``DEFAULT_NIL``
is an unspecified unique token used by the class module below.
* ``printall(obj)``
@ -2266,7 +2312,7 @@ utils
Performs a shallow, or semi-deep copy of the object as a lua table tree.
The deep mode recurses into lua tables and subobjects, except pointers
to other heap objects.
Null pointers are represented as df.NULL. Zero-based native containers
Null pointers are represented as ``df.NULL``. Zero-based native containers
are converted to 1-based lua sequences.
* ``utils.clone_with_default(obj, default, force)``
@ -2323,7 +2369,7 @@ utils
utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
(For an explanation of ``new=true``, see table assignment in the wrapper section)
(For an explanation of ``new=true``, see `lua-api-table-assignment`)
* ``utils.erase_sorted_key(vector,key,field,cmpfun)``
@ -2485,6 +2531,9 @@ To avoid confusion, these methods cannot be redefined.
In-game UI Library
==================
.. contents::
:local:
A number of lua modules with names starting with ``gui`` are dedicated
to wrapping the natives of the ``dfhack.screen`` module in a way that
is easy to use. This allows relatively easily and naturally creating
@ -3214,6 +3263,9 @@ The widget implements:
Plugins
=======
.. contents::
:local:
DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
``mkmodule('plugins.<name>')``; this means that a lua
@ -3444,23 +3496,45 @@ Functions
---------
``registerBuilding(table)`` where table must contain name, as a workshop raw name, the rest are optional:
1. name -- custom workshop id e.g. ``SOAPMAKER``
2. fix_impassible -- if true make impassible tiles impassible to liquids too
3. consume -- how much machine power is needed to work. Disables reactions if not supplied enough and needs_power=1
4. produce -- how much machine power is produced.
5. needs_power -- if produced in network < consumed stop working, default true
6. gears -- a table or ``{x=?,y=?}`` of connection points for machines
7. action -- a table of number (how much ticks to skip) and a function which gets called on shop update
8. animate -- a table of frames which can be a table of:
a. tables of 4 numbers ``{tile,fore,back,bright}`` OR
b. empty table (tile not modified) OR
c. ``{x=<number> y=<number> + 4 numbers like in first case}``, this generates full frame useful for animations that change little (1-2 tiles)
9. canBeRoomSubset -- a flag if this building can be counted in room. 1 means it can, 0 means it can't and -1 default building behaviour
Animate table also might contain:
1. frameLenght -- how many ticks does one frame take OR
2. isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning)
:name:
custom workshop id e.g. ``SOAPMAKER``
.. note:: this is the only mandatory field.
:fix_impassible:
if true make impassible tiles impassible to liquids too
:consume:
how much machine power is needed to work.
Disables reactions if not supplied enough and ``needs_power==1``
:produce:
how much machine power is produced.
:needs_power:
if produced in network < consumed stop working, default true
:gears:
a table or ``{x=?,y=?}`` of connection points for machines.
:action:
a table of number (how much ticks to skip) and a function which
gets called on shop update
:animate:
a table of frames which can be a table of:
a. tables of 4 numbers ``{tile,fore,back,bright}`` OR
b. empty table (tile not modified) OR
c. ``{x=<number> y=<number> + 4 numbers like in first case}``,
this generates full frame useful for animations that change little (1-2 tiles)
:canBeRoomSubset:
a flag if this building can be counted in room. 1 means it can, 0 means it can't and -1 default building behaviour
:auto_gears:
a flag that automatically fills up gears and animate. It looks over building definition for gear icons and maps them.
Animate table also might contain:
:frameLength:
how many ticks does one frame take OR
:isMechanical:
a bool that says to try to match to mechanical system (i.e. how gears are turning)
``getPower(building)`` returns two number - produced and consumed power if building can be modified and returns nothing otherwise
@ -3475,13 +3549,20 @@ Simple mechanical workshop::
consume=15,
gears={x=0,y=0}, --connection point
animate={
isMechanical=true, --animate the same connection point as vanilla gear
isMechanical=true, --animate the same conn. point as vanilla gear
frames={
{{x=0,y=0,42,7,0,0}}, --first frame, 1 changed tile
{{x=0,y=0,15,7,0,0}} -- second frame, same
}
}
Or with auto_gears::
require('plugins.building-hacks').registerBuilding{name="BONE_GRINDER",
consume=15,
auto_gears=true
}
Luasocket
=========
@ -3512,15 +3593,12 @@ from ``server:accept()``. It's a subclass of ``socket``.
* ``client:receive(pattern)``
Receives data. If ``pattern`` is a number, it receives that much data. Other supported patterns:
* ``*a``
Receives data. Pattern is one of:
Read all available data.
:``*l``: read one line (default, if pattern is *nil*)
:<number>: read specified number of bytes
:``*a``: read all available data
* ``*l``
Read one line. This is the default mode (if pattern is nil).
* ``client:send(data)``
Sends data. Data is a string.
@ -3529,11 +3607,13 @@ from ``server:accept()``. It's a subclass of ``socket``.
Server class
------------
Server is a socket that is waiting for clients. You can get this object from ``tcp:bind(address,port)``.
Server is a socket that is waiting for clients.
You can get this object from ``tcp:bind(address,port)``.
* ``server:accept()``
Accepts an incoming connection if it exists. Returns a ``client`` object representing that socket.
Accepts an incoming connection if it exists.
Returns a ``client`` object representing that socket.
Tcp class
---------
@ -3553,19 +3633,25 @@ A class with all the tcp functionality.
Scripts
=======
Any files with the .lua extension placed into hack/scripts/*
.. contents::
:local:
Any files with the .lua extension placed into :file:`hack/scripts/*`
are automatically used by the DFHack core as commands. The
matching command name consists of the name of the file without
the extension. First DFHack searches for the script in the save folder/raw/scripts folder. If it is not found there, it searches in the DF/raw/scripts folder. If it is not there, it searches in DF/hack/scripts. If it is not there, it gives up.
the extension. First DFHack searches for the script in the :file:`<save_folder>/raw/scripts/` folder. If it is not found there, it searches in the :file:`<DF>/raw/scripts/` folder. If it is not there, it searches in
:file:`<DF>/hack/scripts/`. If it is not there, it gives up.
If the first line of the script is a one-line comment, it is
used by the built-in ``ls`` and ``help`` commands.
Such a comment is required for every script in the official DFHack repository.
**NOTE:** Scripts placed in subdirectories still can be accessed, but
do not clutter the ``ls`` command list; thus it is preferred
for obscure developer-oriented scripts and scripts used by tools.
When calling such scripts, always use '/' as the separator for
directories, e.g. ``devel/lua-example``.
.. note::
Scripts placed in subdirectories still can be accessed, but
do not clutter the `ls` command list (unless ``ls -a``; thus it is preferred
for obscure developer-oriented scripts and scripts used by tools.
When calling such scripts, always use '/' as the separator for
directories, e.g. ``devel/lua-example``.
Scripts are re-read from disk if they have changed since the last time they were read.
Global variable values persist in memory between calls, unless the file has changed.
@ -3592,12 +3678,15 @@ Note that this function lets errors propagate to the caller.
Run an Lua script and return its environment.
This command allows you to use scripts like modules for increased portability.
It is highly recommended that if you are a modder you put your custom modules in ``raw/scripts`` and use ``script_environment`` instead of ``require`` so that saves with your mod installed will be self-contained and can be transferred to people who do have DFHack but do not have your mod installed.
You can say ``dfhack.script_environment('add-thought').addEmotionToUnit([arguments go here])`` and it will have the desired effect.
It will call the script in question with the global ``moduleMode`` set to ``true`` so that the script can return early.
This is useful because if the script is called from the console it should deal with its console arguments and if it is called by ``script_environment`` it should only create its global functions and return.
You can also access global variables with, for example ``print(dfhack.script_environment('add-thought').validArgs)``
The function ``script_environment`` is fast enough that it is recommended that you not store its result in a nonlocal variable, because your script might need to load a different version of that script if the save is unloaded and a save with a different mod that overrides the same script with a slightly different functionality is loaded.
This will not be an issue in most cases.
This function also permits circular dependencies of scripts.
* ``dfhack.reqscript(name)`` or ``reqscript(name)``

@ -10,32 +10,25 @@ Most commands offered by plugins are listed here,
hopefully organised in a way you will find useful.
.. contents::
:depth: 3
:depth: 2
===============================
Data inspection and visualizers
===============================
.. _stonesense:
.. contents::
:local:
.. _plugin-stonesense:
stonesense
==========
An isometric visualizer that runs in a second window. This requires working
graphics acceleration and at least a dual core CPU (otherwise it will slow
down DF). Usage:
An isometric visualizer that runs in a second window. Usage:
:stonesense: Open the visualiser in a new window. Alias ``ssense``.
:ssense overlay: Overlay DF window, replacing the map area.
``PRINT_MODE:2D`` strongly recommended for stability.
Stonesense can be configured by editing the :file:`stonesense/init.txt`
and :file:`stonesense/keybinds.txt` files. Additional content, such as sprites
for modded creatures, is available from the content repository on the wiki.
For detailed information, see the `stonesense readme`_, the :wiki:`wiki page
<Utility:Stonesense>`, or the :forums:`Bay12 forums thread <106497>`.
.. _`stonesense readme`: https://github.com/DFHack/stonesense/blob/master/README.md
For more information, see `the full Stonesense README <stonesense>`.
.. _blueprint:
@ -173,6 +166,8 @@ Usage and related commands:
Only useful where (eg) you abandoned with the fort revealed
and no longer want the data.
.. _showmood:
showmood
========
Shows all items needed for the currently active strange mood.
@ -182,6 +177,9 @@ Shows all items needed for the currently active strange mood.
Bugfixes
========
.. contents::
:local:
fixdiplomats
============
Adds a Diplomat position to all Elven civilizations, allowing them to negotiate
@ -217,6 +215,8 @@ Removes invalid references to mineral inclusions and restores missing ones.
Use this if you broke your embark with tools like `tiletypes`, or if you
accidentally placed a construction on top of a valuable mineral floor.
.. _petcapRemover:
petcapRemover
=============
Allows you to remove or raise the pet population cap. In vanilla
@ -333,13 +333,15 @@ UI Upgrades
===========
.. note::
In order to avoid user confusion, as a matter of policy all GUI tools
display the word :guilabel:`DFHack` on the screen somewhere while active.
When that is not appropriate because they merely add keybinding hints to
existing DF screens, they deliberately use red instead of green for the key.
.. contents::
:local:
.. _automelt:
@ -612,6 +614,12 @@ resume
Allows automatic resumption of suspended constructions, along with colored
UI hints for construction status.
.. _title-version:
title-version
=============
Displays the DFHack version on DF's title screen when enabled.
.. _trackstop:
trackstop
@ -739,6 +747,9 @@ materials that color the light etc...
Job and Fortress management
===========================
.. contents::
:local:
.. _autolabor:
autolabor
@ -1187,6 +1198,8 @@ Some widgets support additional options:
displayed as ``-1`` when the cursor is outside of the DF window; otherwise,
nothing will be displayed.
.. _workNow:
workNow
=======
Force all dwarves to look for a job immediately, or as soon as the game is unpaused.
@ -1464,6 +1477,8 @@ add some new races with 'watch'. If you simply want to stop it completely use
autobutcher unwatch ALPACA CAT
.. _autochop:
autochop
========
Automatically manage tree cutting designation to keep available logs withing given
@ -1485,6 +1500,9 @@ open the dashboard from the chop designation menu.
Map modification
================
.. contents::
:local:
.. _3dveins:
3dveins
@ -1558,6 +1576,8 @@ Features:
Digging 1-wide corridors with the miner inside the burrow is SLOW.
.. _changelayer:
changelayer
===========
Changes material of the geology layer under cursor to the specified inorganic
@ -1632,6 +1652,8 @@ Example:
``changevein NATIVE_PLATINUM``
Convert vein at cursor position into platinum ore.
.. _changeitem:
changeitem
==========
Allows changing item material and base quality. By default the item currently
@ -1659,6 +1681,8 @@ Examples:
``changeitem q 5``
Change currently selected item to masterpiece quality.
.. _cleanconst:
cleanconst
==========
Cleans up construction materials.
@ -1667,14 +1691,7 @@ This utility alters all constructions on the map so that they spawn their
building component when they are disassembled, allowing their actual
build items to be safely deleted. This can improve FPS in extreme situations.
colonies
========
Allows listing all the vermin colonies on the map and optionally turning
them into honey bee colonies.
Options:
:bees: turn colonies into honey bee colonies
.. _deramp:
deramp
======
@ -1697,6 +1714,8 @@ Basic commands:
to remove designations, for if you accidentally set 50 levels at once.
:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``.
.. _digexp:
digexp
======
This command is for :wiki:`exploratory mining <Exploratory_mining>`.
@ -1729,6 +1748,8 @@ Examples:
``expdig ladder designated``
Take current designations and replace them with the ladder pattern
.. _digcircle:
digcircle
=========
A command for easy designation of filled and hollow circles.
@ -1765,6 +1786,8 @@ Examples:
``digcircle``
Do it again.
.. _digtype:
digtype
=======
For every tile on the map of the same vein type as the selected tile,
@ -1785,6 +1808,8 @@ Options:
:down: down stairs
:clear: clear designation
.. _digFlood:
digFlood
========
Automatically digs out specified veins as they are discovered. It runs once
@ -1860,6 +1885,8 @@ All of the building designation uses `Planning Mode <buildingplan>`, so you do n
have the items available to construct all the buildings when you run
fortplan with the .csv file.
.. _getplants:
getplants
=========
This tool allows plant gathering and tree cutting by RAW ID. Specify the types
@ -1878,6 +1905,8 @@ Options:
Specifying both ``-t`` and ``-s`` will have no effect. If no plant IDs are specified,
all valid plant IDs will be listed.
.. _infiniteSky:
infiniteSky
===========
Automatically allocates new z-levels of sky at the top of the map as you build up,
@ -1966,6 +1995,8 @@ settings in liquids were made it paints a point of 7/7 magma by default).
Intended to be used as keybinding. Requires an active in-game cursor.
.. _plant:
plant
=====
A tool for creating shrubs, growing, or getting rid of them.
@ -1985,6 +2016,8 @@ For mass effects, use one of the additional options:
:trees: affect all trees on the map
:all: affect every plant!
.. _regrass:
regrass
=======
Regrows all the grass. Not much to it ;)
@ -2087,21 +2120,29 @@ up.
For more details, use ``tiletypes help``.
.. _tiletypes-command:
tiletypes-command
-----------------
Runs tiletypes commands, separated by ;. This makes it possible to change
tiletypes modes from a hotkey or via dfhack-run.
.. _tiletypes-here:
tiletypes-here
--------------
Apply the current tiletypes options at the in-game cursor position, including
the brush. Can be used from a hotkey.
.. _tiletypes-here-point:
tiletypes-here-point
--------------------
Apply the current tiletypes options at the in-game cursor position to a single
tile. Can be used from a hotkey.
.. _tubefill:
tubefill
========
Fills all the adamantine veins again. Veins that were hollow will be left
@ -2120,6 +2161,9 @@ your miner when you dig into the region that used to be hollow.
Mods and Cheating
=================
.. contents::
:local:
.. _add-spatter:
add-spatter
@ -2240,6 +2284,8 @@ armor onto a war animal or to add unusual items (such as crowns) to any unit.
For more information run ``forceequip help``. See also `modtools/equip-item`.
.. _lair:
lair
====
This command allows you to mark the map as a monster lair, preventing item
@ -2253,6 +2299,8 @@ Options:
:lair: Mark the map as monster lair
:lair reset: Mark the map as ordinary (not lair)
.. _mode:
mode
====
This command lets you see and change the game mode directly.
@ -2303,9 +2351,11 @@ Known limitations: if the selected unit is currently performing a job, the mood
siege-engine
============
Siege engines in DF haven't been updated since the game was 2D, and can
only aim in four directions. This plugin allows you to:
only aim in four directions. To make them useful above-ground,
this plugin allows you to:
* link siege engines to stockpiles
* restrict operator skill levels (like workshops)
* load any object into a catapult, not just stones
* aim at a rectangular area in any direction, and across Z-levels

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<!--
DO NOT CHANGE THIS FILE - it redirects to the actual documentation.
If the page is not found, you can read the raw docs in the docs folder.
This file is an additional redirect for users in the DFHack code repo
who are visiting from ../README.html
For end-users, the real index.html will be in this relative location.
-->
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=./html/index.html">
<script type="text/javascript">
window.location.href = "./html/index.html"
</script>
<title>Page Redirection</title>
</head>
<body>
Follow this <a href='./html/index.html'>link to the documentation.</a>
</body>
</html>

@ -165,6 +165,26 @@ modules/Once.cpp
modules/Filesystem.cpp
)
SET(STATIC_FIELDS_FILES)
FOREACH(GROUP other a b c d e f g h i j k l m n o p q r s t u v w x y z)
SET(STATIC_FIELDS_FILENAME ${dfhack_SOURCE_DIR}/library/DataStaticsFields/${GROUP}.cpp)
IF(${GROUP} STREQUAL "other")
SET(STATIC_FIELDS_INC_FILENAME "df/static.fields.inc")
ELSE()
SET(STATIC_FIELDS_INC_FILENAME "df/static.fields-${GROUP}.inc")
ENDIF()
FILE(WRITE ${STATIC_FIELDS_FILENAME}.tmp
"#define STATIC_FIELDS_GROUP\n"
"#include \"../DataStaticsFields.cpp\"\n"
"#include \"${STATIC_FIELDS_INC_FILENAME}\"\n"
)
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${STATIC_FIELDS_FILENAME}.tmp ${STATIC_FIELDS_FILENAME})
FILE(REMOVE ${STATIC_FIELDS_FILENAME}.tmp)
LIST(APPEND STATIC_FIELDS_FILES ${STATIC_FIELDS_FILENAME})
ENDFOREACH()
LIST(APPEND MAIN_SOURCES ${STATIC_FIELDS_FILES})
IF(WIN32)
SOURCE_GROUP("Modules\\Headers" FILES ${MODULE_HEADERS})
SOURCE_GROUP("Modules\\Sources" FILES ${MODULE_SOURCES})
@ -231,10 +251,10 @@ ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codege
IF(UNIX)
# Don't produce debug info for generated stubs
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
ENDIF()

@ -511,6 +511,41 @@ string Core::findScript(string name)
return "";
}
bool loadScriptPaths(color_ostream &out, bool silent = false)
{
using namespace std;
string filename("dfhack-config/script-paths.txt");
ifstream file(filename);
if (!file)
{
if (!silent)
out.printerr("Could not load %s\n", filename.c_str());
return false;
}
string raw;
int line = 0;
while (getline(file, raw))
{
++line;
istringstream ss(raw);
char ch;
ss >> skipws;
if (!(ss >> ch) || ch == '#')
continue;
ss >> ws; // discard whitespace
string path;
getline(ss, path);
if (ch == '+' || ch == '-')
{
if (!Core::getInstance().addScriptPath(path, ch == '+') && !silent)
out.printerr("%s:%i: Failed to add path: %s\n", filename.c_str(), line, path.c_str());
}
else if (!silent)
out.printerr("%s:%i: Illegal character: %c\n", filename.c_str(), line, ch);
}
return true;
}
static std::map<std::string, state_change_event> state_change_event_map;
static void sc_event_map_init() {
if (!state_change_event_map.size())
@ -1505,6 +1540,8 @@ bool Core::Init()
}
}
loadScriptPaths(con);
// initialize common lua context
if (!Lua::Core::Init(con))
{

@ -1,7 +1,6 @@
#define NO_DFHACK_VERSION_MACROS
#include "DFHackVersion.h"
#include "git-describe.h"
#include "Export.h"
namespace DFHack {
namespace Version {
const char *dfhack_version()
@ -24,6 +23,23 @@ namespace DFHack {
{
return DFHACK_GIT_COMMIT;
}
const char *git_xml_commit()
{
return DFHACK_GIT_XML_COMMIT;
}
const char *git_xml_expected_commit()
{
return DFHACK_GIT_XML_EXPECTED_COMMIT;
}
bool git_xml_match()
{
#ifdef DFHACK_GIT_XML_MATCH
return true;
#else
return false;
#endif
}
bool is_release()
{

@ -3,9 +3,11 @@
#include "MiscUtils.h"
#include "VersionInfo.h"
#ifndef STATIC_FIELDS_GROUP
#include "df/world.h"
#include "df/world_data.h"
#include "df/ui.h"
#endif
#include "DataIdentity.h"
#include "DataFuncs.h"
@ -18,6 +20,7 @@ namespace df {
#define NUMBER_IDENTITY_TRAITS(type) \
number_identity<type> identity_traits<type>::identity(#type);
#ifndef STATIC_FIELDS_GROUP
NUMBER_IDENTITY_TRAITS(char);
NUMBER_IDENTITY_TRAITS(int8_t);
NUMBER_IDENTITY_TRAITS(uint8_t);
@ -48,7 +51,7 @@ namespace df {
sizeof(std::fstream), fstream_allocator_fn, "fstream");
buffer_container_identity buffer_container_identity::base_instance;
#endif
#undef NUMBER_IDENTITY_TRAITS
}
@ -58,6 +61,3 @@ namespace df {
#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
#define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name)
#define FLD_END struct_field_info::END
// Field definitions
#include "df/static.fields.inc"

@ -0,0 +1,2 @@
*.cpp
*.tmp

@ -47,6 +47,7 @@ typedef struct interpose_s
#include "DFHack.h"
#include "Core.h"
#include "Hooks.h"
#include "SDL_events.h"
#include <iostream>
/*static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) =
@ -105,7 +106,8 @@ DFhackCExport int DFH_SDL_PollEvent(SDL::Event* event)
pollevent_again:
// if SDL returns 0 here, it means there are no more events. return 0
int orig_return = SDL_PollEvent(event);
if(!orig_return || !(SDL_GetAppState() & SDL_APPINPUTFOCUS))
if(!orig_return || (!(SDL_GetAppState() & SDL_APPINPUTFOCUS) &&
(event->type == SDL::ET_KEYDOWN || event->type == SDL::ET_KEYUP)))
return 0;
// otherwise we have an event to filter
else if( event != 0 )

@ -1417,6 +1417,9 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP_VERSION_FUNC(getCompiledDFVersion, df_version),
WRAP_VERSION_FUNC(getGitDescription, git_description),
WRAP_VERSION_FUNC(getGitCommit, git_commit),
WRAP_VERSION_FUNC(getGitXmlCommit, git_xml_commit),
WRAP_VERSION_FUNC(getGitXmlExpectedCommit, git_xml_expected_commit),
WRAP_VERSION_FUNC(gitXmlMatch, git_xml_match),
WRAP_VERSION_FUNC(isRelease, is_release),
WRAP_VERSION_FUNC(isPrerelease, is_prerelease),
{ NULL, NULL }
@ -1518,6 +1521,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDwarf),
WRAPM(Units, isCitizen),
WRAPM(Units, getAge),
WRAPM(Units, getKillCount),
WRAPM(Units, getNominalSkill),
WRAPM(Units, getEffectiveSkill),
WRAPM(Units, getExperience),
@ -1528,10 +1532,12 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getCasteProfessionName),
WRAPM(Units, getProfessionColor),
WRAPM(Units, getCasteProfessionColor),
WRAPM(Units, getSquadName),
WRAPM(Units, isWar),
WRAPM(Units, isHunter),
WRAPM(Units, isAvailableForAdoption),
WRAPM(Units, isOwnCiv),
WRAPM(Units, isOwnGroup),
WRAPM(Units, isOwnRace),
WRAPM(Units, getRaceName),
WRAPM(Units, getRaceNamePlural),
@ -1591,12 +1597,6 @@ static bool items_moveToContainer(df::item *item, df::item *container)
return Items::moveToContainer(mc, item, container);
}
static bool items_moveToBuilding(df::item *item, df::building_actual *building, int use_mode)
{
MapExtras::MapCache mc;
return Items::moveToBuilding(mc, item, building,use_mode);
}
static bool items_moveToInventory
(df::item *item, df::unit *unit, df::unit_inventory_item::T_mode mode, int body_part)
{
@ -1647,7 +1647,6 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, createItem),
WRAPN(moveToGround, items_moveToGround),
WRAPN(moveToContainer, items_moveToContainer),
WRAPN(moveToBuilding, items_moveToBuilding),
WRAPN(moveToInventory, items_moveToInventory),
WRAPN(makeProjectile, items_makeProjectile),
WRAPN(remove, items_remove),
@ -1669,9 +1668,22 @@ static int items_getContainedItems(lua_State *state)
return 1;
}
static int items_moveToBuilding(lua_State *state)
{
MapExtras::MapCache mc;
auto item = Lua::CheckDFObject<df::item>(state, 1);
auto building = Lua::CheckDFObject<df::building_actual>(state, 2);
int use_mode = luaL_optint(state, 3, 0);
bool force_in_building = lua_toboolean(state, 4);
lua_pushboolean(state, Items::moveToBuilding(mc, item, building, use_mode, force_in_building));
return 1;
}
static const luaL_Reg dfhack_items_funcs[] = {
{ "getPosition", items_getPosition },
{ "getContainedItems", items_getContainedItems },
{ "moveToBuilding", items_moveToBuilding },
{ NULL, NULL }
};

@ -448,19 +448,29 @@ Lua::ObjectClass Lua::IsDFObject(lua_State *state, int val_index)
static const char *const primitive_types[] = {
"string",
"ptr-string",
"int8_t", "uint8_t", "int16_t", "uint16_t",
"int32_t", "uint32_t", "int64_t", "uint64_t",
"bool", "float", "double",
"pointer",
"ptr-vector",
"bit-vector",
"bit-array",
NULL
};
static type_identity *const primitive_identities[] = {
df::identity_traits<std::string>::get(),
df::identity_traits<const char*>::get(),
df::identity_traits<int8_t>::get(), df::identity_traits<uint8_t>::get(),
df::identity_traits<int16_t>::get(), df::identity_traits<uint16_t>::get(),
df::identity_traits<int32_t>::get(), df::identity_traits<uint32_t>::get(),
df::identity_traits<int64_t>::get(), df::identity_traits<uint64_t>::get(),
df::identity_traits<bool>::get(),
df::identity_traits<float>::get(), df::identity_traits<double>::get(),
df::identity_traits<void*>::get(),
df::identity_traits<std::vector<void*> >::get(),
df::identity_traits<std::vector<bool> >::get(),
df::identity_traits<BitArray<int> >::get(),
NULL
};

@ -8,16 +8,34 @@ execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
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}\"\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}\"\n")
# Check library/xml for matching commits
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml"
OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT)
set(git_describe_tmp_h ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h)
set(git_describe_h ${dfhack_SOURCE_DIR}/library/include/git-describe.h)
file(WRITE ${git_describe_tmp_h} "")
macro(git_describe_definition var)
string(STRIP "${${var}}" GIT_DEF_VALUE)
file(APPEND ${git_describe_tmp_h} "#define ${var} \"${GIT_DEF_VALUE}\"\n")
endmacro()
git_describe_definition(DFHACK_GIT_DESCRIPTION)
git_describe_definition(DFHACK_GIT_COMMIT)
git_describe_definition(DFHACK_GIT_XML_EXPECTED_COMMIT)
git_describe_definition(DFHACK_GIT_XML_COMMIT)
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
file(APPEND ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
"#define DFHACK_GIT_TAGGED\n")
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_TAGGED\n")
endif()
if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT})
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_XML_MATCH\n")
endif()
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)
${git_describe_tmp_h} ${git_describe_h})

@ -6,6 +6,9 @@ namespace DFHack {
const char *dfhack_release();
const char *git_description();
const char *git_commit();
const char *git_xml_commit();
const char *git_xml_expected_commit();
bool git_xml_match();
bool is_release();
bool is_prerelease();
}
@ -17,6 +20,9 @@ namespace DFHack {
#define DFHACK_VERSION (DFHack::Version::dfhack_version())
#define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description())
#define DFHACK_GIT_COMMIT (DFHack::Version::git_commit())
#define DFHACK_GIT_XML_COMMIT (DFHack::Version::git_xml_commit())
#define DFHACK_GIT_XML_EXPECTED_COMMIT (DFHack::Version::git_xml_expected_commit())
#define DFHACK_GIT_XML_MATCH (DFHack::Version::git_xml_match())
#define DFHACK_IS_RELEASE (DFHack::Version::is_release())
#define DFHACK_IS_PRERELEASE (DFHack::Version::is_prerelease())
#endif

@ -527,3 +527,96 @@ namespace DFHack {namespace Lua {
name##_event.invoke(out, 7); \
} \
}
//No handler versions useful for vmethod events, when we already have a place to put code at triggering
#define DEFINE_LUA_EVENT_NH_0(name) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out) { \
if (name##_event.state_if_count()) { \
name##_event.invoke(out, 0); \
} \
}
#define DEFINE_LUA_EVENT_NH_1(name, arg_type1) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
name##_event.invoke(out, 1); \
} \
}
#define DEFINE_LUA_EVENT_NH_2(name, arg_type1, arg_type2) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
name##_event.invoke(out, 2); \
} \
}
#define DEFINE_LUA_EVENT_NH_3(name, arg_type1, arg_type2, arg_type3) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
name##_event.invoke(out, 3); \
} \
}
#define DEFINE_LUA_EVENT_NH_4(name, arg_type1, arg_type2, arg_type3, arg_type4) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
name##_event.invoke(out, 4); \
} \
}
#define DEFINE_LUA_EVENT_NH_5(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4, arg_type5 arg5) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
DFHack::Lua::Push(state, arg5); \
name##_event.invoke(out, 5); \
} \
}
#define DEFINE_LUA_EVENT_NH_6(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
DFHack::Lua::Push(state, arg5); \
DFHack::Lua::Push(state, arg6); \
name##_event.invoke(out, 6); \
} \
}
#define DEFINE_LUA_EVENT_NH_7(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6,arg_type7) \
static DFHack::Lua::Notification name##_event; \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6, arg_type7 arg7) { \
if (auto state = name##_event.state_if_count()) { \
DFHack::Lua::Push(state, arg1); \
DFHack::Lua::Push(state, arg2); \
DFHack::Lua::Push(state, arg3); \
DFHack::Lua::Push(state, arg4); \
DFHack::Lua::Push(state, arg5); \
DFHack::Lua::Push(state, arg6); \
DFHack::Lua::Push(state, arg7); \
name##_event.invoke(out, 7); \
} \
}

@ -148,6 +148,18 @@ namespace DFHack
return 0;
return (void*)i->second;
}
bool getVTableName (const void *vtable, std::string &out) const
{
for (auto i = VTables.begin(); i != VTables.end(); ++i)
{
if ((void*)i->second == vtable)
{
out = i->first;
return true;
}
}
return false;
}
void setOS(const OSType os)
{

@ -164,7 +164,8 @@ DFHACK_EXPORT std::string getDescription(df::item *item, int type = 0, bool deco
DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos);
DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container);
DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode);
DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,
int16_t use_mode = 0, bool force_in_building = false);
DFHACK_EXPORT bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *unit,
df::unit_inventory_item::T_mode mode = df::unit_inventory_item::Hauled, int body_part = -1);

@ -240,6 +240,7 @@ DFHACK_EXPORT bool isWar(df::unit* unit);
DFHACK_EXPORT bool isHunter(df::unit* unit);
DFHACK_EXPORT bool isAvailableForAdoption(df::unit* unit);
DFHACK_EXPORT bool isOwnCiv(df::unit* unit);
DFHACK_EXPORT bool isOwnGroup(df::unit* unit);
DFHACK_EXPORT bool isOwnRace(df::unit* unit);
DFHACK_EXPORT std::string getRaceNameById(int32_t race_id);
@ -274,6 +275,7 @@ DFHACK_EXPORT bool isGelded(df::unit* unit);
DFHACK_EXPORT bool isDomesticated(df::unit* unit);
DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false);
DFHACK_EXPORT int getKillCount(df::unit *unit);
DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false);
DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id);

@ -247,6 +247,14 @@ function safe_index(obj,idx,...)
end
end
function string:startswith(prefix)
return self:sub(1, #prefix) == prefix
end
function string:endswith(suffix)
return self:sub(-#suffix) == suffix or #suffix == 0
end
-- String conversions
function dfhack.persistent:__tostring()

@ -419,14 +419,23 @@ function MenuOverlay:render(dc)
end
--fakes a "real" workshop sidebar menu, but on exactly selected workshop
WorkshopOverlay = defclass(WorkshopOverlay, MenuOverlay)
WorkshopOverlay.focus_path="WorkshopOverlay"
WorkshopOverlay.ATTRS={
workshop=DEFAULT_NIL,
}
function WorkshopOverlay:onAboutToShow(below)
WorkshopOverlay.super.onAboutToShow(self,below)
if df.global.world.selected_building ~= self.workshop then
error("The workshop overlay tried to show up for incorrect workshop")
end
end
function WorkshopOverlay:onInput(keys)
local allowedKeys={ --TODO add options: job management, profile, etc...
local allowedKeys={ --TODO add options: job management, profile, etc...
"CURSOR_RIGHT","CURSOR_LEFT","CURSOR_UP","CURSOR_DOWN",
"CURSOR_UPRIGHT","CURSOR_UPLEFT","CURSOR_DOWNRIGHT","CURSOR_DOWNLEFT",
"CURSOR_UP_Z","CURSOR_DOWN_Z","DESTROYBUILDING","CHANGETAB"}
"CURSOR_UP_Z","CURSOR_DOWN_Z","DESTROYBUILDING","CHANGETAB","SUSPENDBUILDING"}
if keys.LEAVESCREEN then
self:dismiss()
self:sendInputToParent('LEAVESCREEN')
@ -448,4 +457,26 @@ function WorkshopOverlay:onInput(keys)
return
end
end
function WorkshopOverlay:onGetSelectedBuilding()
return self.workshop
end
local function is_slated_for_remove( bld )
for i,v in ipairs(bld.jobs) do
if v.job_type==df.job_type.DestroyBuilding then
return true
end
end
return false
end
function WorkshopOverlay:render(dc)
self:renderParent()
if df.global.world.selected_building ~= self.workshop then
return
end
if is_slated_for_remove(self.workshop) then
return
end
WorkshopOverlay.super.render(self, dc)
end
return _ENV

@ -1,5 +1,6 @@
local _ENV = mkmodule('json')
local internal = require 'json.internal'
local fs = dfhack.filesystem
encode_defaults = {
-- For compatibility with jsonxx (C++)
@ -14,11 +15,8 @@ function encode(data, options, msg)
return internal:encode(data, msg, opts)
end
function encode_file(data, path, overwrite, ...)
if dfhack.filesystem.exists(path) and not overwrite then
error('File exists: ' .. path)
end
if dfhack.filesystem.isdir(path) then
function encode_file(data, path, ...)
if fs.isdir(path) then
error('Is a directory: ' .. path)
end
local contents = encode(data, ...)
@ -41,4 +39,42 @@ function decode_file(path, ...)
return decode(contents, ...)
end
local _file = defclass()
function _file:init(opts)
self.path = opts.path
self.data = {}
self.exists = false
self:read(opts.strict)
end
function _file:read(strict)
if not fs.exists(self.path) then
if strict then
error('cannot read file: ' .. self.path)
else
self.data = {}
end
else
self.exists = true
self.data = decode_file(self.path)
end
return self.data
end
function _file:write(data)
if data then
self.data = data
end
encode_file(self.data, self.path)
self.exists = true
end
function _file:__tostring()
return ('<json file: %s>'):format(self.path)
end
function open(path, strict)
return _file{path=path, strict=strict}
end
return _ENV

@ -296,9 +296,14 @@ function field_ref(handle,...)
end
function field_offset(type,...)
local handle = df.reinterpret_cast(type,1)
local handle = df.new(type)
local _,haddr = df.sizeof(handle)
local _,addr = df.sizeof(field_ref(handle,...))
return addr-1
-- to aid in diagnosis of bad virtual dtors
io.stderr:write('memscan: deleting instance of '..tostring(type) .. '\n'):flush()
df.delete(handle)
io.stderr:write('successfully deleted\n'):flush()
return addr-haddr
end
function MemoryArea:object_by_field(addr,type,...)

@ -16,6 +16,7 @@
#include "df/general_ref_type.h"
#include "df/general_ref_unit_workerst.h"
#include "df/global_objects.h"
#include "df/historical_figure.h"
#include "df/interaction.h"
#include "df/item.h"
#include "df/item_actual.h"
@ -296,6 +297,11 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
eventLastTick[a] = -1;//-1000000;
}
for ( size_t a = 0; a < df::global::world->history.figures.size(); a++ ) {
df::historical_figure* unit = df::global::world->history.figures[a];
if ( unit->id < 0 && unit->name.language < 0 )
unit->name.language = 0;
}
gameLoaded = true;
}

@ -892,7 +892,8 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df:
return true;
}
bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode)
bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,
int16_t use_mode, bool force_in_building)
{
CHECK_NULL_POINTER(item);
CHECK_NULL_POINTER(building);
@ -915,7 +916,8 @@ bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::
item->pos.x=building->centerx;
item->pos.y=building->centery;
item->pos.z=building->z;
item->flags.bits.in_building=true;
if (use_mode == 2 || force_in_building)
item->flags.bits.in_building=true;
ref->building_id=building->id;
item->general_refs.push_back(ref);

@ -71,6 +71,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepEverything)
pnew->flags.bits.suspend = job->flags.bits.suspend;
pnew->completion_timer = -1;
pnew->posting_index = -1;
}
pnew->list_link = NULL;

@ -31,6 +31,7 @@ distribution.
#include <map>
#include <cstring>
#include <algorithm>
#include <numeric>
using namespace std;
#include "VersionInfo.h"
@ -47,30 +48,32 @@ using namespace std;
#include "Core.h"
#include "MiscUtils.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/job.h"
#include "df/unit_inventory_item.h"
#include "df/unit_soul.h"
#include "df/nemesis_record.h"
#include "df/historical_entity.h"
#include "df/burrow.h"
#include "df/caste_raw.h"
#include "df/creature_raw.h"
#include "df/curse_attr_change.h"
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/entity_raw.h"
#include "df/entity_raw_flags.h"
#include "df/game_mode.h"
#include "df/histfig_entity_link_positionst.h"
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.h"
#include "df/historical_kills.h"
#include "df/history_event_hist_figure_diedst.h"
#include "df/identity.h"
#include "df/burrow.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/game_mode.h"
#include "df/job.h"
#include "df/nemesis_record.h"
#include "df/squad.h"
#include "df/ui.h"
#include "df/unit_inventory_item.h"
#include "df/unit_misc_trait.h"
#include "df/unit_skill.h"
#include "df/unit_soul.h"
#include "df/unit_wound.h"
#include "df/curse_attr_change.h"
#include "df/squad.h"
#include "df/world.h"
using namespace DFHack;
using namespace df::enums;
@ -816,26 +819,22 @@ bool Units::isCitizen(df::unit *unit)
// 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)
unit->flags1.bits.diplomat ||
unit->flags2.bits.visitor ||
unit->flags2.bits.visitor_uninvited ||
unit->flags2.bits.underworld ||
unit->flags2.bits.resident)
return false;
if (unit->flags1.bits.tame)
return true;
if (!isSane(unit))
return false;
return unit->civ_id == ui->civ_id &&
unit->civ_id != -1 &&
!unit->flags2.bits.underworld &&
!unit->flags2.bits.resident &&
!unit->flags2.bits.visitor_uninvited &&
!unit->flags2.bits.visitor;
return isOwnGroup(unit);
}
bool Units::isDwarf(df::unit *unit)
@ -889,6 +888,22 @@ bool Units::isOwnCiv(df::unit* unit)
return unit->civ_id == ui->civ_id;
}
// check if creature belongs to the player's group
bool Units::isOwnGroup(df::unit* unit)
{
CHECK_NULL_POINTER(unit);
auto histfig = df::historical_figure::find(unit->hist_figure_id);
if (!histfig)
return false;
for (size_t i = 0; i < histfig->entity_links.size(); i++)
{
auto link = histfig->entity_links[i];
if (link->entity_id == ui->group_id && link->getType() == df::histfig_entity_link_type::MEMBER)
return true;
}
return false;
}
// check if creature belongs to the player's race
// (in combination with check for civ helps to filter out own dwarves)
bool Units::isOwnRace(df::unit* unit)
@ -1085,6 +1100,25 @@ double Units::getAge(df::unit *unit, bool true_age)
return cur_time - birth_time;
}
int Units::getKillCount(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
auto histfig = df::historical_figure::find(unit->hist_figure_id);
int count = 0;
if (histfig && histfig->info->kills)
{
auto kills = histfig->info->kills;
count += std::accumulate(kills->killed_count.begin(), kills->killed_count.end(), 0);
for (auto it = kills->events.begin(); it != kills->events.end(); ++it)
{
if (virtual_cast<df::history_event_hist_figure_diedst>(df::history_event::find(*it)))
++count;
}
}
return count;
}
inline void adjust_skill_rating(int &rating, bool is_adventure, int value, int dwarf3_4, int dwarf1_2, int adv9_10, int adv3_4, int adv1_2)
{
if (is_adventure)
@ -1784,6 +1818,7 @@ int8_t Units::getCasteProfessionColor(int race, int casteid, df::profession pid)
std::string Units::getSquadName(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
if (unit->military.squad_id == -1)
return "";
df::squad *squad = df::squad::find(unit->military.squad_id);

@ -1 +1 @@
Subproject commit 49eff8dca662bff83ff2191ac5b611504c72ae1c
Subproject commit 58806dc7e4bfcc1bb5f33b15395bdc9fd5a1ea28

@ -1,11 +1,5 @@
INCLUDE(Plugins.cmake)
# Dfusion plugin
OPTION(BUILD_DFUSION "Build DFusion." ON)
if(BUILD_DFUSION)
add_subdirectory (Dfusion)
endif()
OPTION(BUILD_STONESENSE "Build stonesense (needs a checkout first)." OFF)
if(BUILD_STONESENSE)
add_subdirectory (stonesense)
@ -30,22 +24,6 @@ if(BUILD_DEV_PLUGINS)
add_subdirectory (devel)
endif()
#It's dead :<
#OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." OFF)
#if(BUILD_DF2MC)
# add_subdirectory (df2mc)
#endif()
OPTION(BUILD_MAPEXPORT "Build map exporter." ON)
if (BUILD_MAPEXPORT)
# add_subdirectory (mapexport)
endif()
OPTION(BUILD_DWARFEXPORT "Build dwarf exporter." ON)
if (BUILD_DWARFEXPORT)
# add_subdirectory (dwarfexport)
endif()
OPTION(BUILD_RUBY "Build ruby binding." ON)
if (BUILD_RUBY)
add_subdirectory (ruby)
@ -107,7 +85,6 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(cleanconst cleanconst.cpp)
DFHACK_PLUGIN(cleaners cleaners.cpp)
DFHACK_PLUGIN(cleanowned cleanowned.cpp)
DFHACK_PLUGIN(colonies colonies.cpp)
DFHACK_PLUGIN(command-prompt command-prompt.cpp)
DFHACK_PLUGIN(confirm confirm.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(createitem createitem.cpp)

@ -1,11 +0,0 @@
include_directories(include)
FILE(GLOB DFUSION_CPPS src/*.c*)
set(
DFUSION_CPPS_ALL
dfusion.cpp
${DFUSION_CPPS}
)
FILE(GLOB DFUSION_HS include/*)
SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE )
DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread)

@ -1,88 +0,0 @@
#include "Core.h"
#include "Export.h"
#include "PluginManager.h"
#include "MemAccess.h"
#include "MiscUtils.h"
#include <vector>
#include <string>
#include "luamain.h"
#include "lua_Process.h"
#include "lua_Hexsearch.h"
#include "lua_Misc.h"
#include "DataDefs.h"
#include "LuaTools.h"
using std::vector;
using std::string;
using namespace DFHack;
DFHACK_PLUGIN("dfusion")
static int loadObjectFile(lua_State* L)
{
std::string path;
path=luaL_checkstring(L,1);
OutFile::File f(path);
lua_newtable(L);
int table_pos=lua_gettop(L);
size_t size=f.GetTextSize();
Lua::Push(L,size);
lua_setfield(L,table_pos,"data_size");
char* buf=new char[size];
f.GetText(buf);
//Lua::PushDFObject(L,DFHack::,buf);
//Lua::Push(L,buf);
lua_pushlightuserdata(L,buf);
lua_setfield(L,table_pos,"data");
const OutFile::vSymbol &symbols=f.GetSymbols();
lua_newtable(L);
for(size_t i=0;i<symbols.size();i++)
{
Lua::Push(L,i);
lua_newtable(L);
Lua::Push(L,symbols[i].name);
lua_setfield(L,-2,"name");
Lua::Push(L,symbols[i].pos);
lua_setfield(L,-2,"pos");
lua_settable(L,-3);
}
lua_setfield(L,table_pos,"symbols");
return 1;
}
static int markAsExecutable(lua_State* L)
{
unsigned addr=luaL_checkunsigned(L,1);
std::vector<DFHack::t_memrange> ranges;
DFHack::Core::getInstance().p->getMemRanges(ranges);
for(size_t i=0;i<ranges.size();i++)
{
if(ranges[i].isInRange((void*)addr))
{
DFHack::t_memrange newperm=ranges[i];
newperm.execute=true;
DFHack::Core::getInstance().p->setPermisions(ranges[i],newperm);
return 0;
}
}
lua_pushlstring(L,"Memory range not found",23);
lua_error(L);
return 0;
}
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(loadObjectFile),
DFHACK_LUA_COMMAND(markAsExecutable),
DFHACK_LUA_END
};
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
return CR_OK;
}

@ -1,126 +0,0 @@
#ifndef OUTFILE_H
#define OUTFILE_H
#include <string>
#include <fstream>
#include <iostream>
#include <map>
#include <vector>
namespace OutFile
{
struct Header
{
unsigned short machinetype;
unsigned short sectioncount;
unsigned long time;
unsigned long symbolptr;
unsigned long symbolcount;
unsigned short opthead;
unsigned short flags;
void PrintData()
{
std::cout<<"Symbol start:"<<symbolptr<<"\n";
}
};
struct Section
{
char name[8];
unsigned long Vsize;
unsigned long Vstart;
unsigned long size;
unsigned long start;
unsigned long ptrRel;
unsigned long ptrLine;
unsigned short numRel;
unsigned short numLine;
unsigned long flags;
void PrintData()
{
std::cout<<name<<" size:"<<size<<" start:"<<start<<"\n";
}
};
struct Symbol
{
std::string name;
unsigned long pos;
unsigned short sectnumb;
unsigned short type;
unsigned char storageclass;
unsigned char auxsymbs;
//char unk2[6];
void Read(std::iostream &s,unsigned long strptr)
{
union
{
char buf[8];
struct
{
unsigned long zeros;
unsigned long strptr;
};
}data;
s.read((char*)&data,8);
s.read((char*)&pos,4);
s.read((char*)&sectnumb,2);
s.read((char*)&type,2);
s.read((char*)&storageclass,1);
s.read((char*)&auxsymbs,1);
if(data.zeros!=0)
{
name=data.buf;
name=name.substr(0,8);
}
else
{
//name="";
//std::cout<<"Name in symbol table\n";
char buf[256];
s.seekg(strptr+data.strptr);
s.get(buf,256,'\0');
name=buf;
}
//s.seekp(6,std::ios::cur);
}
void PrintData()
{
std::cout<<name<<" section:"<<sectnumb<<" pos:"<<pos<<"\n";
}
};
struct Relocation
{
unsigned long ptr;
unsigned long tblIndex;
unsigned short type;
};
typedef std::vector<Symbol> vSymbol;
class File
{
public:
File(std::string path);
virtual ~File();
void GetText(char *ptr);
size_t GetTextSize();
void LoadSymbols();
const vSymbol& GetSymbols(){LoadSymbols();return symbols;};
void PrintSymbols();
void PrintRelocations();
protected:
private:
typedef std::map<std::string,Section> secMap;
secMap sections;
vSymbol symbols;
Section &GetSection(std::string name);
std::fstream mystream;
Header myhead;
// Section Text;
//Section Data;
// Section Bss;
};
}
#endif // OUTFILE_H

@ -1,37 +0,0 @@
#ifndef HEXSEARCH_H
#define HEXSEARCH_H
#include <vector>
#include "Core.h" //for some reason process.h needs core
#include "MemAccess.h"
//(not yet)implemented using Boyer-Moore algorithm
class Hexsearch
{
public:
typedef std::vector<int> SearchArgType;
enum SearchConst //TODO add more
{
ANYBYTE=0x101,DWORD_,ANYDWORD,ADDRESS
};
Hexsearch(const SearchArgType &args,char * startpos,char * endpos);
~Hexsearch();
void Reset(){pos_=startpos_;};
void SetStart(char * pos){pos_=pos;};
void * FindNext();
std::vector<void *> FindAll();
private:
bool Compare(int a,int b);
void ReparseArgs();
SearchArgType args_;
char * pos_,* startpos_,* endpos_;
std::vector<int> BadCharShifts,GoodSuffixShift;
void PrepareGoodSuffixTable();
void PrepareBadCharShift();
};
#endif

@ -1,33 +0,0 @@
#ifndef LUA_HEXSEARCH_H
#define LUA_HEXSEARCH_H
#include "hexsearch.h"
#include "luamain.h"
namespace lua
{
class Hexsearch
{
int tblid;
::Hexsearch *p;
public:
Hexsearch(lua_State *L,int id);
~Hexsearch();
int GetTableId(){return tblid;};
int find(lua_State *L);
int findall(lua_State *L);
int reset(lua_State *L);
DEF_LUNE(Hexsearch);
};
void RegisterHexsearch(lua::state &st);
}
#endif

@ -1,46 +0,0 @@
#ifndef LUA_MISC_H
#define LUA_MISC_H
#include <map>
#include "Core.h"
#include <MemAccess.h>
#include "luamain.h"
#include "OutFile.h"
#include "LuaTools.h"
namespace lua
{
typedef std::map<std::string,void *> mapPlugs;
class PlugManager
{
public:
mapPlugs GetList(){return plugs;};
uint32_t AddNewPlug(std::string name,uint32_t size,uint32_t loc=0);
uint32_t FindPlugin(std::string name);
static PlugManager &GetInst()
{
void *p;
p=DFHack::Core::getInstance().GetData("dfusion_manager");
if(p==0)
{
p=new PlugManager;
DFHack::Core::getInstance().RegisterData(p,"dfusion_manager");
}
return *static_cast<PlugManager*>(p);
};
protected:
private:
PlugManager(){};
mapPlugs plugs;
};
void RegisterMisc(lua::state &st);
}
#endif

@ -1,13 +0,0 @@
#ifndef LUA_PROCESS_H
#define LUA_PROCESS_H
#include "Core.h"
#include <MemAccess.h>
#include "luamain.h"
namespace lua
{
void RegisterProcess(lua::state &st);
}
#endif

@ -1,40 +0,0 @@
#ifndef LUAMAIN_H
#define LUAMAIN_H
#include <string>
using std::string;
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lune.h"
#include "luaxx.hpp"
namespace lua
{
//global lua state singleton
class glua
{
public:
static state &Get();
private:
glua();
static glua *ptr;
state mystate;
};
//registers basic lua commands
void RegBasics(lua::state &L);
//dumps lua function trace, useless unless called from lua.
string DebugDump(lua::state &L);
//register functions, first registers into global scope, second into current table
void RegFunctions(lua::state &L,luaL_Reg const *arr);
void RegFunctionsLocal(lua::state &L,luaL_Reg const *arr);
}
#endif // LUAMAIN_H

@ -1,530 +0,0 @@
/* vim: set et sw=3 tw=0 fo=croqlaw cino=t0:
*
* Luaxx, the C++ Lua wrapper library.
* Copyright (c) 2006-2008 Matthew Nicholson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef LUAXX_H
#define LUAXX_H
#define lua_Integer_long 1
#define lua_Integer_int 1
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include <string>
#include <vector>
#include <new>
#include <exception>
/** @file
* Luaxx header file.
*/
/** @mainpage Luaxx
*
* Luaxx is a thin wrapper around the Lua C API. The wrapper adds some
* convenience functions and integrates well with modern C++.
*
* Luaxx is not designed like toLua, instead Luaxx is more of a 1 to 1
* logical mapping of the lua API in C++. For example: in C you would write
* 'lua_pushnumber(L, 3)', in C++ with Luaxx you would write
* 'L.push(3)'.
*
* Every thing is contained in the 'lua' namespace and exceptions are thrown
* when a lua API function returns an error. Most of the functionality is
* contained in the lua::state class, which can be passed directly to lua C API
* functions (the compiler will automatically use the internal lua_State
* pointer). See the documentation for that class for more information.
*/
namespace lua
{
void StackDump(lua_State *L);
/** A generic lua exception.
*/
class exception : public std::exception {
public:
/// Constructor.
exception() : std::exception() { }
/// Constructor.
explicit exception(const char* desc) : std::exception(), description(desc) { }
virtual ~exception() throw() { }
/** Get a description of the error.
* @returns a C-string describing the error
*/
virtual const char* what() const throw() {
return description.c_str();
}
private:
std::string description;
};
/** A lua runtime error.
* This is thrown when there was an error executing some lua code.
* @note This is not an std::runtime error.
*/
class runtime_error : public exception {
public:
/// Constructor.
runtime_error() : exception() { }
/// Constructor.
explicit runtime_error(const char* desc) : exception(desc) { }
virtual ~runtime_error() throw() { }
};
/** A syntax error.
*/
class syntax_error : public exception {
public:
/// Constructor.
syntax_error() : exception() { }
/// Constructor.
explicit syntax_error(const char* desc) : exception(desc) { }
virtual ~syntax_error() throw() { }
};
/** An error loading a lua file.
* This is thrown when a call to lua::loadfile failed because the file could
* not be opened or read.
*/
class file_error : public exception {
public:
/// Constructor.
file_error() : exception() { }
/// Constructor.
explicit file_error(const char* desc) : exception(desc) { }
virtual ~file_error() throw() { }
};
/** A memory allocation error.
*/
class bad_alloc : public exception, std::bad_alloc {
public:
/// Constructor.
bad_alloc() : lua::exception(), std::bad_alloc() { }
/// Constructor.
explicit bad_alloc(const char* desc) : lua::exception(desc), std::bad_alloc() { }
virtual ~bad_alloc() throw() { }
};
/** An error converting a lua type.
*/
class bad_conversion : public exception {
public:
/// Constructor.
bad_conversion() : exception() { }
/// Constructor.
explicit bad_conversion(const char* desc) : exception(desc) { }
virtual ~bad_conversion() throw() { }
};
/// A Lua table (this class does not have any data).
class table { };
/// A Lua nil (this class does not have any data).
class nil { };
/// A lua function (not a cfunction).
class function { };
/// A lua userdatum
class userdata { };
/// A lua light userdatum
class lightuserdata { };
typedef lua_CFunction cfunction; ///< A cfunction on the lua statck
typedef lua_Integer integer; ///< The default lua integer type
typedef lua_Number number; ///< The default lua number type
typedef lua_Reader reader; ///< The type of function used by lua_load
const int multiret = LUA_MULTRET; ///< LUA_MULTIRET
/** This is the Luaxx equivalent of lua_State.
* The functions provided by this class, closely resemble those of the Lua C
* API.
*/
void StackDump(lua_State *L);
class state {
public:
state();
state(lua_State* L);
state(const state& t)
{
managed=false;
L=t.L;
}
state& operator = (const state& t);
~state();
operator lua_State*();
state& push();
state& push(nil);
state& push(bool boolean);
template<typename T> state& push(T number);
state& push(const char* s, size_t length);
state& push(const char* s);
state& push(const std::string& s);
state& push(cfunction f);
state& push(table);
state& push(void* p);
template<typename T> state& pushlightuserdata(T p);
template<typename T> state& to(T& number, int index = -1);
template<typename T> state& touserdata(T& p, int index = -1);
template<typename T> T as(T default_value, int index = -1);
template<typename T> T as(int index = -1);
template<typename T> T vpop()
{
T ret;
ret=as<T>();
pop();
return ret;
}
template<typename T> bool is(int index = -1);
state& check(int narg);
#ifndef lua_Integer_int
state& check(int& i, int narg);
#endif
state& check(integer& i, int narg);
#ifndef lua_Integer_long
state& check(long& l, int narg);
#endif
state& check(std::string& s, int narg);
state& check(number& n, int narg);
template<typename msg_t> void error(msg_t message);
#if 0
template<> void error(const std::string& message);
#endif
state& pcall(int nargs = 0, int nresults = 0, int on_error = 0);
state& call(int nargs = 0, int nresults = 0);
state& checkstack(int size);
state& settop(int index);
int gettop();
int size();
bool empty();
state& insert(int index);
state& replace(int index);
state& remove(int index);
state& pop(int elements = 1);
state& pushvalue(int index);
state& newtable();
bool newmetatable(const std::string& tname);
template<typename userdata_t> userdata_t* newuserdata();
void* newuserdata(size_t nbytes);
state& gettable(int index = -2);
state& getfield(const std::string& k, int index = -1);
state& settable(int index = -3);
state& setfield(const std::string& k, int index = -2);
state& getmetatable(const std::string& tname);
bool getmetatable(int index);
bool next(int index = -2);
state& getglobal(const std::string& name);
state& setglobal(const std::string& name);
state& loadfile(const std::string& filename);
state& loadstring(const std::string& s);
template<typename iterator_t> state& load(iterator_t begin, iterator_t end);
size_t objlen(int index = -1);
private:
lua_State* L;
bool managed;
int throw_error(int code);
};
// template functions
/** Push a number onto the stack.
* @tparam T the numeric type to push (should be automatically determined,
* if there is no specialization for the desired type, lua_pushnumber() will
* be used, and may fail)
* @param number the number to push
* @returns a reference to this lua::state
*/
template<typename T>
state& state::push(T number) {
lua_pushnumber(L, number);
return *this;
}
/** Push a light userdatum on to the stack.
* @tparam T the type of data to push (should be automatically determined)
* @param p the pointer to push
* @returns a reference to this lua::state
*/
template<typename T>
state& state::pushlightuserdata(T p) {
lua_pushlightuserdata(L, p);
return *this;
}
/** Check if the given index is of the given type (defaults to using
* lua_isnumber()).
* @tparam T the type to check for (the default, if no specializations
* match, does lua_isnumber())
* @param index the index to check
* @note the default version (used if no specialization is matched) will
* check if the given value is a number
* @returns whether the value at the given index is a nil
*/
template<typename T>
bool state::is(int index) {
return lua_isnumber(L, index);
}
/** Get the value at index as the given numeric type.
* @tparam T they numeric type to static_cast<T>() the numeric value on the
* stack to
* @param number where to store the value
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @todo Instead of throwing an exception here, we may just return an
* error code.
* @throws lua::bad_conversion if the value on the stack could not be
* converted to the indicated type
* @returns a reference to this lua::state
*/
template<typename T>
state& state::to(T& number, int index) {
if (lua_isnumber(L, index))
number = static_cast<T>(lua_tonumber(L, index));
else
throw bad_conversion("Cannot convert non 'number' value to number");
return *this;
}
/** Get the value at index as (light) userdata.
* @tparam T the type of data pointed to (pointer is returned as
* reinterpret_cast<T>())
* @param p the pointer to store the value in
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @todo Instead of throwing an exception here, we may just return an
* error code.
* @throws lua::bad_conversion if the value on the stack could not be
* converted to the indicated type
* @returns a reference to this lua::state
*/
template<typename T>
state& state::touserdata(T& p, int index) {
if (lua_isuserdata(L, index))
p = reinterpret_cast<T>(lua_touserdata(L, index));
else
throw bad_conversion("Cannot convert non 'userdata' or 'lightuserdata' value to userdata");
return *this;
}
/** Get the value at index as the given type.
* @tparam T the type to retrieve the value on the stack as (the default
* template function uses lua_tonumber(), specializations may cause
* different behavior)
* @param default_value this value is returned if the conversion fails
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @returns the indicated value from the stack or the default value if
* the conversion fails
*/
template<typename T>
T state::as(T default_value, int index) {
if (lua_isnumber(L, index))
return static_cast<T>(lua_tonumber(L, index));
else
return default_value;
}
/** Get the value at index as the given type.
* @tparam T the expected type of the value
* @param index the index to get
*
* @note This function does \em not pop the value from the stack.
* @note The default version of this function uses lua_tonumber() but
* specializations may cause different behavior.
*
* @todo Instead of throwing an exception here, we may just return an
* error code.
*
* @throws lua::bad_conversion if the value on the stack could not be
* converted to the indicated type
*
* This function will return the value on the stack as the given type. If
* the value is not of the given type <em>no conversion will be
* performed</em> and lua::bad_conversion will be thrown. There are some
* exceptions to this rule, for example, numbers will be converted to
* strings and vice-versa (conversion is only performed if the matching
* lua_is*() function returns true). The state::to() function should be
* used to perform automatic conversion.
*
* @returns the indicated value as the given type if possible
*/
template<typename T>
T state::as(int index) {
if (lua_isnumber(L, index))
return static_cast<T>(lua_tonumber(L, index));
else
throw bad_conversion("Cannot convert non 'number' value to number");
}
/** Create a new userdatum on the stack.
* @tparam userdata_t the type of the userdata (will be passed to sizeof())
*
* This function creates a new userdatum on the stack the size of
* userdata_t and return a pointer to it.
* @returns a pointer to the new userdatum
*/
template<typename userdata_t>
userdata_t* state::newuserdata() {
return reinterpret_cast<userdata_t*>(lua_newuserdata(L, sizeof(userdata_t)));
}
/** Generate a Lua error.
* @tparam msg_t the type of error message data (should be automatically
* determined)
* @param message the error message/value
* @note This function is used to raise errors from lua::cfunctions.
* @note This function never returns, instead it throws an exception
* caught by the intepreter.
*/
template<typename msg_t>
void state::error(msg_t message) {
push(message);
lua_error(L);
}
/** Load a sequence of data as a Lua chunk.
* @tparam iterator_t the type of iterator to use (should be automatically
* determined)
* @param begin an iterator to the start of the sequence
* @param end an iterator to the end of the sequence (one past the
* end)
*
* This function takes a sequence of data and attempts to convert it
* into a Lua chunk. The type of data passed must be able to be
* converted into an 8-bit char.
*
* @note This function should automatically detect if the data is text
* or binary.
*
* @returns a reference to this lua::state
*/
template<typename iterator_t>
state& state::load(iterator_t begin, iterator_t end) {
// convert the data to characters
std::vector<char> chunk(begin, end);
// Here we use the address of the first element of our vector.
// This works because the data in std::vectors is contiguous.
throw_error(luaL_loadbuffer(L, &(*chunk.begin()), chunk.size(), NULL));
return *this;
}
// template specializations
template<> state& state::to(bool& boolean, int index);
template<> state& state::to(std::string& string, int index);
template<> bool state::as(bool default_value, int index);
template<> std::string state::as(std::string default_value, int index);
template<> bool state::as(int index);
template<> std::string state::as(int index);
template<> bool state::is<nil>(int index);
template<> bool state::is<bool>(int index);
template<> bool state::is<std::string>(int index);
template<> bool state::is<table>(int index);
template<> bool state::is<cfunction>(int index);
template<> bool state::is<function>(int index);
template<> bool state::is<userdata>(int index);
template<> bool state::is<lightuserdata>(int index);
// inline functions
/** Convert a lua::state to a lua_State*.
* This operator allows lua::state to behave like a lua_State
* pointer.
*
* @note This should be used as a last result to interoperate with C
* code. This may be removed in future versions of Luaxx.
*/
inline state::operator lua_State*() {
return L;
}
/** Throws exceptions for error return codes.
* @param code the return code
*
* This function throws an exception based on the error it was passed.
* If it is passed a 0 it will not throw anything.
*
* @todo In the future this function may check an exception mask
* before throwing an error.
*
* @returns the code it was passed
*/
inline int state::throw_error(int code) {
std::string error;
// below, we package lua errors into exceptions
switch (code) {
case 0:
break;
case LUA_ERRSYNTAX:
to(error).pop();
throw syntax_error(error.c_str());
case LUA_ERRMEM:
to(error).pop();
throw bad_alloc(error.c_str());
case LUA_ERRRUN:
to(error).pop();
throw runtime_error(error.c_str());
case LUA_ERRFILE:
to(error).pop();
throw file_error(error.c_str());
default:
to(error).pop();
throw exception(error.c_str());
}
return code;
}
}
#endif

@ -1,360 +0,0 @@
#ifndef LUNE_H
#define LUNE_H
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "luaxx.hpp"
#include <string>
namespace lua
{
class object
{
state &myst;
int myref;
public:
object(state &myst):myst(myst)
{
myref=luaL_ref(myst,LUA_REGISTRYINDEX);
}
~object()
{
luaL_unref(myst,LUA_REGISTRYINDEX,myref);
}
void Get()
{
lua_rawgeti(myst,LUA_REGISTRYINDEX,myref);
}
state &GetState(){return myst;};
};
class local_object
{
state myst;
int myref;
static object *mytbl;
public:
local_object(lua_State *L)
{
myst=state(L);
//LOG<<"Creating local object...\n";
//StackDump(L);
if(!mytbl)
{
//LOG<<" Metable...\n";
myst.newtable(); //2
if(myst.newmetatable("WEAKTABLE"))//3
{
//StackDump(L);
myst.push("kv"); //4
myst.setfield("__mode");//3
//LOG<<" Setting Metable...\n";
//StackDump(L);
}
//LOG<<" Attaching to holder...\n";
//myst.setfield("__metatable");//2
lua_setmetatable(myst,-1);
mytbl=new object(myst);
//StackDump(L);
//LOG<<" Done Metatable...\n";
}
//StackDump(L);
mytbl->Get();
//LOG<<" Got my table...\n";
//StackDump(L);
myst.insert(-2);
myref=luaL_ref(myst,-2);
//LOG<<"Before pop:";
//StackDump(L);
myst.pop(1);
GetTable();
//LOG<<"========Done...\n"<<"Ref="<<myref<<"\n";
//mytbl->Get();
//StackDump(L);
//LOG<<"===========================\n";
}
~local_object()
{
//LOG<<"Deleting local object...\n";
ReleaseTable();
}
void ReleaseTable()
{
mytbl->Get();
int pos=myst.gettop();
luaL_unref(myst,pos,myref);
myst.remove(pos);
}
state GetState(){return myst;}
void GetTable()
{
//LOG<<"Getting ref="<<myref<<"\n";
//StackDump(myst);
//LOG<<"Tbl preget\n";
mytbl->Get();
int pos=myst.gettop();
//StackDump(myst);
//LOG<<"Tbl get\n";
//int pos=myst.gettop();
lua_rawgeti(myst,pos,myref);
//StackDump(myst);
//LOG<<"Done\n";
myst.remove(pos);
}
protected:
};
};
template <typename T,bool GC=true>
class Lune
{
public:
typedef struct
{
T *pT;
int tableref;
} userdataType;
typedef int (T::*mfp)(lua_State *L);
typedef struct
{
const char *name;
mfp mfunc;
} RegType;
static void Register(lua_State *L)
{
lua_newtable(L);
int methods = lua_gettop(L);
luaL_newmetatable(L, T::className);
int metatable = lua_gettop(L);
// store method table in globals so that
// scripts can add functions written in Lua.
lua_pushvalue(L, methods);
lua_setglobal(L, T::className);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methods);
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
lua_pushliteral(L, "__index");
lua_pushcfunction(L, index_T);
lua_settable(L, metatable);
//lua_pushliteral(L, "__name");
//lua_pushstring(L, T::className);
//lua_settable(L, metatable);
lua_pushliteral(L, "__newindex");
lua_pushcfunction(L, newindex_T);
lua_settable(L, metatable);
lua_pushliteral(L, "__instances");
lua_newtable(L);
lua_settable(L, metatable);
if(GC)
{
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, gc_T);
lua_settable(L, metatable);
}
lua_newtable(L); // metatable for method table
int mt = lua_gettop(L);
lua_pushliteral(L, "__call");
lua_pushcfunction(L, new_T);
lua_pushliteral(L, "new");
lua_pushvalue(L, -2); // dup new_T function
lua_settable(L, methods); // add new_T to method table
lua_settable(L, mt); // mt.__call = new_T
lua_setmetatable(L, methods);
//LOG<<"lune: registered class \""<<T::className<<"\"\n";
// fill method table with methods from class T
for (RegType *l = T::methods; l->name; l++)
{
/* edited by Snaily: shouldn't it be const RegType *l ... ? */
lua_pushstring(L, l->name);
lua_pushlightuserdata(L, (void*)l);
lua_pushcclosure(L, thunk, 1);
lua_settable(L, methods);
//LOG<<"lune: method \""<<l->name<<"\"\n";
}
lua_pop(L, 2); // drop metatable and method table
};
static void GetTable(lua_State *L,T *p)
{
GetTableEx(L,p->GetTableId());
}
static void GetTableEx(lua_State *L,int id)
{
lua::state s(L);
s.getmetatable(T::className);
s.getfield("__instances");
int ins=s.gettop();
lua_rawgeti(L, ins, id);
s.insert(-3);
s.pop(2);
}
static T *check(lua_State *L, int narg)
{
userdataType *ud =
static_cast<userdataType*>(luaL_checkudata(L, narg, T::className)); //TODO FIX THIs..
//(lua_touserdata(L, narg));//
if(!ud) luaL_error(L, "Bad argument %d: expected type %s", narg, T::className);
return ud->pT; // pointer to T object
}
protected:
private:
static int RegTable(lua_State *L)
{
// LOG<<"Regging....\n";
//lua::StackDump(L);
lua::state s(L);
int ssize=s.gettop();
//s.getglobal(T::className);
s.getmetatable(T::className);
s.getfield("__instances");
int ins=s.gettop();
s.newtable();
int id=luaL_ref(L,ins);
//LOG<<"After reg:\n";
//lua::StackDump(L);
s.settop(ssize);
return id;
}
static void UnregTable(lua_State *L,int id)
{
lua::state s(L);
s.getmetatable(T::className);
s.getfield("__instances");
int ins=s.gettop();
//LOG<<"Unreg table id:"<<id<<"stack dump:\n";
//lua::StackDump(L);
luaL_unref(L,ins,id);
}
static int index_T(lua_State *L) // calls with (table, key), return value
{
lua::state st(L);
std::string key=st.as<std::string>(-1);
T *p=check(L,1);
GetTable(L,p);
st.insert(-2);
//LOG<<"Index:\n";
//lua::StackDump(L);
lua_rawget(L,-2); //try getting from normal table
if(st.is<lua::nil>()) //failed
{
st.pop(2);
st.getglobal(T::className); //try class tables then
st.push(key);
st.gettable();
}
return 1;
}
static int newindex_T(lua_State *L)
{
//LOG<<"New index....\n";
//lua::StackDump(L);
lua::state st(L);
T *p=check(L,1);
GetTable(L,p);
//st.insert(-3);
//LOG<<"Before set:\n";
st.insert(-3);
//lua::StackDump(L);
lua_rawset(L,-3);
return 0;
}
static int thunk(lua_State *L)
{
//LOG<<"Size of stack:"<<lua_gettop(L)<<"\n";
//lua::StackDump(L);
if(lua_gettop(L)<1)
luaL_error(L,"Member function called without 'self'");
//LOG<<"Size of stack after:"<<lua_gettop(L)<<"\n";
// stack has userdata, followed by method args
T *obj = check(L, 1); // get 'self', or if you prefer, 'this'
//T *obj=static_cast<userdataType*>(lua_touserdata(L,1))->pT;
lua_remove(L, 1); // remove self so member function args start at index 1
// get member function from upvalue
RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
return (obj->*(l->mfunc))(L); // call member function
}
static int gc_T(lua_State *L)
{
//lua_getfield(L,,"__ud");
//LOG<<"Garbage collecting.\n";
//lua::StackDump(L);
userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
T *obj = ud->pT;
delete obj; // call destructor for T objects
UnregTable(L,ud->tableref);
return 0;
}
static int new_T(lua_State *L)
{
//LOG<<"Pre build:"<<lua_gettop(L)<<"\n";
//lua::StackDump(L);
lua_remove(L, 1); // use classname:new(), instead of classname.new()
//lua_newtable(L);
int id=RegTable(L);
//LOG<<"Registred as:"<<id<<"\n";
//int ssize=lua_gettop(L);
T *obj = new T(L,id); // call constructor for T objects
lua_settop(L,0); //no need for parameters later.
//LOG<<"Post build:"<<lua_gettop(L)<<"\t";
//lua::StackDump(L);
//LOG<<"TSOP\n";
userdataType *ud =
static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));
//lua::StackDump(L);
luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
lua_setmetatable(L,-2);
//LOG<<"metatable set\n";
//lua::StackDump(L);
GetTable(L,obj);
lua_pushliteral(L,"__obj");
lua_pushvalue(L,-3);
lua_settable(L,-3);
lua_pop(L,1);
//LOG<<"Object referenced\n";
//lua::StackDump(L);
//T *p = new(ud) T(L); // call constructor for T objects
//lua::StackDump(L);
ud->pT = obj; // store pointer to object in userdata
ud->tableref=id;
//luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
//lua_setmetatable(L, tableindex);
//lua::StackDump(L);
//LOG<<"Push done\n";
return 1; // userdata containing pointer to T object
}
Lune() {}; //non constructable...
};
#define method(class, name) {#name, &class::name}
#define DEF_LUNE(class) static const char className[];\
static Lune<class>::RegType methods[];
#define DEF_LUNE_NOGC(class) static const char className[];\
static Lune<class,false>::RegType methods[];
#define IMP_LUNE(class,lua_name) const char class::className[]=#lua_name;
#define LUNE_METHODS_START(class) Lune<class>::RegType class::methods[] = {
#define LUNE_METHODS_START_NOGC(class) Lune<class,false>::RegType class::methods[] = {
#define LUNE_METHODS_END() {0,0}}
#endif // LUNE_H

@ -1,12 +0,0 @@
Dfusion plugin offers four DFhack commands: 'dfusion', 'dfuse' and 'lua', 'runlua'.
lua:
Runs an interactive lua console. For more on lua commands see [http://www.lua.org/manual/5.1/manual.html Lua reference manual] or google "lua". Also this command could be ran with filepath as an argument. Then it runs that file as a lua script file. E.g. ''lua dfusion/temp.lua'' runs a file <your df path>/dfusion/temp.lua.
runlua:
Similar to ''lua <filename>'' but not interactive, to be used with hotkeys
dfusion:
First this command runs all plugins' init.lua part then show a menu. Type number to run specified plugin.
dfuse:
Similar to dfusion but not interactive. To be used with hotkeys (later will have command support).
Also dfuse/dfusion runs an init script located at 'save directory/dfusion/init.lua'. And 'initcustom.lua' if it exists
More info http://dwarffortresswiki.org/index.php/Utility:DFusion

@ -1,102 +0,0 @@
#include "OutFile.h"
#include <stdexcept>
using namespace OutFile;
File::File(std::string path)
{
//mystream.exceptions ( std::fstream::eofbit | std::fstream::failbit | std::fstream::badbit );
mystream.open(path.c_str(),std::fstream::binary|std::ios::in|std::ios::out);
if(mystream)
{
mystream.read((char*)&myhead,sizeof(myhead));
for(unsigned i=0;i<myhead.sectioncount;i++)
{
Section x;
mystream.read((char*)&x,sizeof(Section));
sections[x.name]=x;
}
//std::cout<<"Sizeof:"<<sizeof(Section)<<"\n";
/*myhead.PrintData();
for(auto it=sections.begin();it!=sections.end();it++)
{
it->second.PrintData();
}*/
}
else
{
throw std::runtime_error("Error opening file!");
}
}
Section &File::GetSection(std::string name)
{
return sections[name];
}
void File::GetText(char *ptr)
{
Section &s=GetSection(".text");
mystream.seekg(s.start);
mystream.read(ptr,s.size);
}
size_t File::GetTextSize()
{
Section &s=GetSection(".text");
return s.size;
}
void File::PrintRelocations()
{
for(auto it=sections.begin();it!=sections.end();it++)
{
std::cout<<it->first<<":\n";
for(unsigned i=0;i<it->second.numRel;i++)
{
Relocation r;
mystream.seekg(it->second.ptrRel+10*i);
mystream.read((char*)&r,10);
std::cout<<r.ptr<<" -- "<<r.tblIndex<<":"<</*symbols[r.tblIndex].name<<*/" type:"<<r.type<<"\n";
}
}
}
void File::PrintSymbols()
{
std::cout<<"Sizeof symbol:"<<sizeof(Symbol)<<std::endl;
std::cout<<"Symbol count:"<<myhead.symbolcount<<std::endl;
for(unsigned i=0;i<myhead.symbolcount;i++)
{
mystream.seekg(myhead.symbolptr+i*18);
Symbol s;
std::cout<<i<<"\t";
s.Read(mystream,myhead.symbolptr+18*myhead.symbolcount);
//mystream.read((char*)&s,sizeof(Symbol));
s.PrintData();
symbols.push_back(s);
if(s.auxsymbs>0)
{
i+=s.auxsymbs;
}
}
}
void File::LoadSymbols()
{
symbols.clear();
for(unsigned i=0;i<myhead.symbolcount;i++)
{
mystream.seekg(myhead.symbolptr+i*18);
Symbol s;
s.Read(mystream,myhead.symbolptr+18*myhead.symbolcount);
symbols.push_back(s);
if(s.auxsymbs>0)
{
i+=s.auxsymbs;
}
}
}
File::~File()
{
}

@ -1,116 +0,0 @@
#include "hexsearch.h"
Hexsearch::Hexsearch(const SearchArgType &args,char * startpos,char * endpos):args_(args),pos_(startpos),startpos_(startpos),endpos_(endpos)
{
ReparseArgs();
}
Hexsearch::~Hexsearch()
{
}
inline bool Hexsearch::Compare(int a,int b)
{
if(b==Hexsearch::ANYBYTE)
return true;
if(a==b)
return true;
return false;
}
void Hexsearch::ReparseArgs()
{
union
{
uint32_t val;
uint8_t bytes[4];
}B;
SearchArgType targ;
targ=args_;
args_.clear();
for(size_t i=0;i<targ.size();)
{
if(targ[i]==DWORD_)
{
i++;
B.val=targ[i];
for(int j=0;j<4;j++)
{
args_.push_back(B.bytes[j]);
}
i++;
}
else if (targ[i]==ANYDWORD)
{
i++;
for(int j=0;j<4;j++)
args_.push_back(ANYBYTE);
}
else
{
args_.push_back(targ[i]);
i++;
}
}
}
void * Hexsearch::FindNext() //TODO rewrite using Boyer-Moore algorithm
{
DFHack::Core &inst=DFHack::Core::getInstance();
DFHack::Process *p=inst.p;
uint8_t *buf;
buf=new uint8_t[args_.size()];
while(pos_<endpos_)
{
bool found=true;
p->readByte(pos_,buf[0]);
if(Compare(buf[0],args_[0]))
{
p->read(pos_,args_.size(),buf);
for(size_t i=0;i<args_.size();i++)
{
if(!Compare(buf[i],args_[i]))
{
pos_+=i;
found=false;
break;
}
}
if(found)
{
pos_+=args_.size();
delete [] buf;
return pos_-args_.size();
}
}
pos_ = pos_ + 1;
}
delete [] buf;
return 0;
}
std::vector<void *> Hexsearch::FindAll()
{
std::vector<void *> ret;
void * cpos=pos_;
while(cpos!=0)
{
cpos=FindNext();
if(cpos!=0)
ret.push_back(cpos);
}
return ret;
}
void Hexsearch::PrepareBadCharShift()
{
BadCharShifts.resize(256,-1);
int i=0;
for(SearchArgType::reverse_iterator it=args_.rbegin();it!=args_.rend();it++)
{
BadCharShifts[*it]=i;
i++;
}
}
void Hexsearch::PrepareGoodSuffixTable()
{
GoodSuffixShift.resize(args_.size()+1,0);
}

@ -1,61 +0,0 @@
#include "lua_Hexsearch.h"
int lua::Hexsearch::find(lua_State *L)
{
lua::state st(L);
void * pos=p->FindNext();
st.push(reinterpret_cast<size_t>(pos));
return 1;
}
int lua::Hexsearch::findall(lua_State *L)
{
lua::state st(L);
std::vector<void *> pos=p->FindAll();
st.newtable();
for(unsigned i=0;i<pos.size();i++)
{
st.push(i+1);
st.push(reinterpret_cast<size_t>(pos[i]));
st.settable();
}
return 1;
}
lua::Hexsearch::Hexsearch(lua_State *L,int id):tblid(id)
{
lua::state st(L);
char * start,* end;
::Hexsearch::SearchArgType args;
start= (char *)st.as<uint32_t>(1);
end=(char *)st.as<uint32_t>(2);
for(int i=3;i<=st.gettop();i++)
{
args.push_back(st.as<int>(i));
}
p=new ::Hexsearch(args,start,end);
}
lua::Hexsearch::~Hexsearch()
{
delete p;
}
int lua::Hexsearch::reset(lua_State *L)
{
lua::state st(L);
p->Reset();
return 0;
}
IMP_LUNE(lua::Hexsearch,hexsearch);
LUNE_METHODS_START(lua::Hexsearch)
method(lua::Hexsearch,find),
method(lua::Hexsearch,findall),
method(lua::Hexsearch,reset),
LUNE_METHODS_END();
#define __ADDCONST(name) st.push(::Hexsearch:: name); st.setglobal(#name)
void lua::RegisterHexsearch(lua::state &st)
{
Lune<lua::Hexsearch>::Register(st);
__ADDCONST(ANYBYTE);
__ADDCONST(ANYDWORD);
__ADDCONST(DWORD_);
}
#undef __ADDCONST

@ -1,154 +0,0 @@
#include "lua_Misc.h"
uint32_t lua::PlugManager::AddNewPlug(std::string name,uint32_t size,uint32_t loc)
{
void *p;
if(size!=0)
p=new unsigned char[size];
else
p=(void*)loc;
plugs[name]=p;
return (uint32_t)p;
}
uint32_t lua::PlugManager::FindPlugin(std::string name)
{
mapPlugs::iterator it=plugs.find(name);
if(it!=plugs.end())
return (uint32_t)it->second;
else
return 0;
}
static int LoadMod(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
std::string modname=st.as<std::string>(2);
uint32_t size_add=st.as<uint32_t>(0,3);
OutFile::File f(modfile);
uint32_t size=f.GetTextSize();
uint32_t pos=lua::PlugManager::GetInst().AddNewPlug(modname,size+size_add);
char *buf;
buf=new char[size];
f.GetText(buf);
//std::cout<<"poking @:"<<std::hex<<pos<<"size :"<<size<<std::endl;
DFHack::Core::getInstance().p->write((void *) pos,size,(uint8_t*)buf);
delete [] buf;
st.push(pos);
st.push(size);
return 2;
}
static int LoadObj(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
OutFile::File f(modfile);
size_t s=f.GetTextSize();
void *p=st.newuserdata(s); //TODO does it leak memory??
f.GetText((char*)p);
st.push(s);
return 2;
}
static int FindMarker(lua_State *L) // marker, void ptr, size, start
{
lua::state st(L);
union
{
unsigned char bytes[4];
size_t mark;
}M;
M.mark=st.as<size_t>(1);
unsigned char *p=(unsigned char *)lua_touserdata(L, 2);//st.as<lua::userdata>(2);
size_t size=st.as<size_t>(3);
size_t start=st.as<size_t>(4);
for(size_t i=start;i<size;i++)
{
bool ok;
ok=true;
if(p[i]==M.bytes[0])
{
for(size_t j=0;j<4;j++)
{
if(p[i+j]!=M.bytes[j])
{
ok=false;
break;
}
}
if(ok)
{
st.push(i);
return 1;
}
}
}
return 0;
}
static int LoadObjSymbols(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
OutFile::File f(modfile);
OutFile::vSymbol vec=f.GetSymbols();
OutFile::Symbol S;
st.newtable();
for(size_t i=0;i<vec.size();i++)
{
st.push(i);
S=vec[i];
st.newtable();
st.push(S.name);
st.setfield("name");
st.push(S.pos);
st.setfield("pos");
st.settable();
}
return 1;
}
static int NewMod(lua_State *L)
{
lua::state st(L);
std::string modname=st.as<std::string>(1);
size_t size=st.as<size_t>(2);
size_t loc=st.as<size_t>(3,0);
uint32_t pos=lua::PlugManager::GetInst().AddNewPlug(modname,size,loc);
st.push(pos);
return 1;
}
static int GetMod(lua_State *L)
{
lua::state st(L);
std::string modname=st.as<std::string>(1);
uint32_t pos=lua::PlugManager::GetInst().FindPlugin(modname);
if(pos==0)
st.push();
else
st.push(pos);
return 1;
}
const luaL_Reg lua_misc_func[]=
{
{"loadmod",LoadMod},
{"getmod",GetMod},
{"loadobj",LoadObj},
{"loadobjsymbols",LoadObjSymbols},
{"findmarker",FindMarker},
{"newmod",NewMod},
{NULL,NULL}
};
void lua::RegisterMisc(lua::state &st)
{
st.getglobal("engine");
if(st.is<lua::nil>())
{
st.pop();
st.newtable();
}
lua::RegFunctionsLocal(st, lua_misc_func);
st.setglobal("engine");
}

@ -1,269 +0,0 @@
#include "lua_Process.h"
static DFHack::Process* GetProcessPtr(lua::state &st)
{
return DFHack::Core::getInstance().p;
}
static int lua_Process_readDWord(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
uint32_t ret=c->readDWord( (void *) st.as<uint32_t>(1));
st.push(ret);
return 1;
}
static int lua_Process_writeDWord(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c->writeDWord((void *) st.as<uint32_t>(1),st.as<uint32_t>(2));
return 0;
}
static int lua_Process_readFloat(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
float ret=c->readFloat((void *) st.as<uint32_t>(1));
st.push(ret);
return 1;
}
static int lua_Process_readWord(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
uint16_t ret=c->readWord((void *) st.as<uint32_t>(1));
st.push(ret);
return 1;
}
static int lua_Process_writeWord(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c->writeWord((void *) st.as<uint32_t>(1),st.as<uint16_t>(2));
return 0;
}
static int lua_Process_readByte(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
uint8_t ret=c->readByte((void *) st.as<uint32_t>(1));
st.push(ret);
return 1;
}
static int lua_Process_writeByte(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c->writeByte((void *) st.as<uint32_t>(1),st.as<uint8_t>(2));
return 0;
}
static int lua_Process_read(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
size_t len=st.as<uint32_t>(2);
uint8_t* buf;
if(!st.is<lua::nil>(3))
buf=(uint8_t*)lua_touserdata(st,3);
else
buf=new uint8_t[len];
c->read((void *) st.as<uint32_t>(1),len,buf);
st.pushlightuserdata(buf);
return 1;
}
static int lua_Process_write(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c-> write((void *) st.as<uint32_t>(1),st.as<uint32_t>(2),static_cast<uint8_t*>(lua_touserdata(st,3)));
return 0;
}
static int lua_Process_readSTLString (lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::string r=c->readSTLString((void *) st.as<uint32_t>(1));
st.push(r);
return 1;
}
static int lua_Process_writeSTLString(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c->writeSTLString((void *) st.as<uint32_t>(1),st.as<std::string>(2));
return 0;
}
static int lua_Process_copySTLString(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
c->copySTLString((void *) st.as<uint32_t>(1),st.as<uint32_t>(2));
return 0;
}
static int lua_Process_doReadClassName(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::string r=c->doReadClassName((void*)st.as<size_t>(1));
st.push(r);
return 1;
}
static int lua_Process_readClassName(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::string r=c->readClassName((void*)st.as<size_t>(1));
st.push(r);
return 1;
}
static int lua_Process_readCString (lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::string r=c->readCString((void *) st.as<uint32_t>(1));
st.push(r);
return 1;
}
static int lua_Process_isSuspended(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
st.push(c->isSuspended());
return 1;
}
static int lua_Process_isIdentified(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
st.push(c->isIdentified());
return 1;
}
static int lua_Process_getMemRanges(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::vector<DFHack::t_memrange> ranges;
c->getMemRanges(ranges);
st.newtable();
for(size_t i=0;i<ranges.size();i++)
{
st.push(i);
st.newtable();
st.push((uint32_t)ranges[i].start); // WARNING!! lua has only 32bit numbers, possible loss of data!!
st.setfield("start");
st.push((uint32_t)ranges[i].end);
st.setfield("end");
st.push(std::string(ranges[i].name));
st.setfield("name");
st.push(ranges[i].read);
st.setfield("read");
st.push(ranges[i].write);
st.setfield("write");
st.push(ranges[i].execute);
st.setfield("execute");
st.push(ranges[i].shared);
st.setfield("shared");
st.push(ranges[i].valid);
st.setfield("valid");
st.settable();
}
return 1;
}
static int lua_Process_getBase(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
uint32_t base=c->getBase();
st.push(base);
return 1;
}
/*static int lua_Process_getPID(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
int ret=c->getPID();
st.push(ret);
return 1;
}*/
static int lua_Process_getPath(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::string ret=c->getPath();
st.push(ret);
return 1;
}
static int lua_Process_setPermisions(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
DFHack::t_memrange range,trange;
st.getfield("start",1);
range.start= (void *)st.as<uint64_t>();
st.pop();
st.getfield("end",1);
range.end= (void *)st.as<uint64_t>();
st.pop();
st.getfield("read",2);
trange.read=st.as<bool>();
st.pop();
st.getfield("write",2);
trange.write=st.as<bool>();
st.pop();
st.getfield("execute",2);
trange.execute=st.as<bool>();
st.pop();
c->setPermisions(range,trange);
return 0;
}
#define PROC_FUNC(name) {#name,lua_Process_ ## name}
const luaL_Reg lua_process_func[]=
{
PROC_FUNC(readDWord),
PROC_FUNC(writeDWord),
PROC_FUNC(readFloat),
PROC_FUNC(readWord),
PROC_FUNC(writeWord),
PROC_FUNC(readByte),
PROC_FUNC(writeByte),
PROC_FUNC(read),
PROC_FUNC(write),
PROC_FUNC(readSTLString),
PROC_FUNC(writeSTLString),
PROC_FUNC(copySTLString),
PROC_FUNC(doReadClassName),
PROC_FUNC(readClassName),
PROC_FUNC(readCString ),
PROC_FUNC(isSuspended),
PROC_FUNC(isIdentified),
PROC_FUNC(getMemRanges),
PROC_FUNC(getBase),
//PROC_FUNC(getPID), //not implemented
PROC_FUNC(getPath),
PROC_FUNC(setPermisions),
{NULL,NULL}
};
#undef PROC_FUNC
void lua::RegisterProcess(lua::state &st)
{
st.getglobal("Process");
if(st.is<lua::nil>())
{
st.pop();
st.newtable();
}
lua::RegFunctionsLocal(st, lua_process_func);
st.setglobal("Process");
}

@ -1,69 +0,0 @@
#include "luamain.h"
#include <vector>
lua::glua* lua::glua::ptr=0;
lua::glua::glua()
{
RegBasics(mystate);
}
lua::state &lua::glua::Get()
{
if(!glua::ptr)
glua::ptr=new glua();
return glua::ptr->mystate;
}
int lua_Ver_Lua(lua_State *L)
{
lua::state st(L);
st.push(LUA_RELEASE);
return 1;
}
static const struct luaL_Reg lua_basic_lib [] =
{
{"getluaver", lua_Ver_Lua},
{NULL, NULL} /* sentinel */
};
void lua::RegBasics(lua::state &L)
{
luaL_openlibs(L);
RegFunctions(L,lua_basic_lib);
}
void lua::RegFunctions(lua::state &L,luaL_Reg const*arr)
{
luaL_Reg const *cur=arr;
while(cur->name!=NULL)
{
lua_pushcfunction(L, cur->func);
lua_setglobal(L, cur->name);
cur++;
}
}
void lua::RegFunctionsLocal(lua::state &L,luaL_Reg const*arr)
{
luaL_Reg const *cur=arr;
while(cur->name!=NULL)
{
lua_pushcfunction(L, cur->func);
//lua_setglobal(L, cur->name);
L.setfield(cur->name);
cur++;
}
}
string lua::DebugDump(lua::state &L)
{
L.getglobal("debug");
L.getfield("traceback");
L.call(0,1);
string ret=L.as<string>();
//cout<<"StackTrace:"<<ret<<endl;
L.settop(0);
return ret;
}

@ -1,836 +0,0 @@
/* vim: set et sw=3 tw=0 fo=croqlaw cino=t0:
*
* Luaxx, the C++ Lua wrapper library.
* Copyright (c) 2006-2008 Matthew Nicholson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <luaxx.hpp>
#include <iostream>
#define LOG std::cout
/** @file
* Luaxx implementation file.
*/
namespace lua
{
void StackDump(lua_State *L)
{
int i;
int top = lua_gettop(L);
for (i = 1; i <= top; i++) /* repeat for each level */
{
int t = lua_type(L, i);
LOG<<i<<":";
switch (t)
{
case LUA_TSTRING: /* strings */
LOG<<"str ="<<lua_tostring(L, i)<<"\n";
break;
case LUA_TBOOLEAN: /* booleans */
LOG<<"bool="<<(lua_toboolean(L, i) ? "true" : "false")<<"\n";
break;
case LUA_TNUMBER: /* numbers */
LOG<<"num ="<<lua_tonumber(L, i)<<"\n";
break;
case LUA_TTABLE:
LOG<<lua_typename(L, t);
{
//LOG<<"PRE TOP:"<< lua_gettop(L)<<"\n";
lua_getglobal(L,"PrintTable");
//lua_insert(L,-2);
lua_pushvalue(L,i);
lua_pcall(L,1,0,0);
//LOG<<"POST TOP:"<< lua_gettop(L)<<"\n";
}
break;
default: /* other values */
LOG<<lua_typename(L, t);
{
//LOG<<"PRE TOP:"<< lua_gettop(L)<<"\n";
lua_getglobal(L,"tostring");
//lua_insert(L,-2);
lua_pushvalue(L,i);
lua_pcall(L,1,1,0);
LOG<<"=";
LOG<<lua_tostring(L,-1)<<"\n";
lua_pop(L,1);
//LOG<<"POST TOP:"<< lua_gettop(L)<<"\n";
}
break;
}
}
LOG<<"\n"; /* end the listing */
LOG<<"==============================\n";
}
#undef LOG
/// Construct our lua environment.
state::state() : L(luaL_newstate()), managed(true) {
if (L == NULL)
throw bad_alloc("Error creating lua state");
}
/** Construct our lua environment from an existing lua_State.
* @param L the existing state to use.
*
* This function differs from the normal constructor as it sets a flag
* that prevents lua_close() from being called when this class is
* destroyed.
*/
state::state(lua_State* L) :
L(L), managed(false) {
}
state& state::operator = (const state& t)
{
if(managed)
lua_close(L);
managed=false;
L=t.L;
return *this;
}
/// Destroy our lua environment.
state::~state() {
if (managed)
lua_close(L);
}
/** Push a nil onto the stack.
* @returns a reference to this lua::state
*/
state& state::push() {
lua_pushnil(L);
return *this;
}
/** Push a nil onto the stack.
* @returns a reference to this lua::state
*/
state& state::push(nil) {
lua_pushnil(L);
return *this;
}
/** Push a boolean onto the stack.
* @param boolean the value to push
* @returns a reference to this lua::state
*/
state& state::push(bool boolean) {
lua_pushboolean(L, boolean);
return *this;
}
/** Push a C-style string onto the stack.
* @param s the string to push
* @param length the length of the string
* @returns a reference to this lua::state
*/
state& state::push(const char* s, size_t length) {
lua_pushlstring(L, s, length);
return *this;
}
/** Push a C-style string onto the stack.
* @param s the string to push
* @note This must be a '0' terminated string.
* @returns a reference to this lua::state
*/
state& state::push(const char* s) {
lua_pushstring(L, s);
return *this;
}
/** Push an std::string onto the stack.
* @param s the string to push
* @returns a reference to this lua::state
*/
state& state::push(const std::string& s) {
lua_pushlstring(L, s.c_str(), s.size());
return *this;
}
/** Push an C function onto the stack.
* @param f the function to push
* @returns a reference to this lua::state
*/
state& state::push(cfunction f) {
lua_pushcfunction(L, f);
return *this;
}
/** Create a new table on to the stack.
* @see state::newtable()
* @returns a reference to this lua::state
*/
state& state::push(table) {
lua_newtable(L);
return *this;
}
/** Push a light userdatum on to the stack.
* @param p the pointer to push
* @returns a reference to this lua::state
*/
state& state::push(void* p) {
lua_pushlightuserdata(L, p);
return *this;
}
/** Get the value at index as a string.
* @param default_value this value is returned if the conversion fails
* @param index the index to get
* @note This function does \em not pop the value from the stack.
*
* @note lua::state::as(std::string()) will convert the value at the
* indicated index to a string <em>on the stack</em>. This can
* confuse lua::state::next();
*
* @returns the indicated value from the stack or the default value if the
* conversion fails
*/
template<>
std::string state::as(std::string default_value, int index) {
if (lua_isstring(L, index))
{
size_t len;
const char *str = lua_tolstring(L, index, &len);
return std::string(str, len);
}
else
return default_value;
}
/** Check an argument of the current function.
* @param narg the argument number to check
*
* This function will throw a lua error if there is no argument at the
* given position.
*
* @note This function is meant to be called from with in a lua::cfunction.
* The error throw is internal to the lua interpreter. When compiled as
* C++, a C++ exception is thrown, so the stack is properly unwound. This
* exception is not meant to be caught.
*/
state& state::check(int narg) {
luaL_checkany(L, narg);
return *this;
}
#ifndef lua_Integer_int
/** Check an argument of the current function.
* @param i the int to hold the returned value
* @param narg the argument number to check
*
* This function checks if the given argument number is an int.
*
* @note This function is meant to be called from with in a
* lua::cfunction. The error throw is internal to the lua intrepeter.
* When compiled as C++, a C++ exception is thrown, so the stack is
* properly unwound. This exception is not meant to be caught.
*/
state& state::check(int& i, int narg) {
i = luaL_checkint(L, narg);
return *this;
}
#endif
/** Check an argument of the current function.
* @param i the lua::integer (lua_Integer) to hold the returned value
* @param narg the argument number to check
*
* This function checks if the given argument number is an integer.
*
* @note This is different from lua::check(int(), ...). It returns a
* lua::integer (lua_Integer), which may not be an int.
*
* @note This function is meant to be called from with in a
* lua::cfunction. The error throw is internal to the lua intrepeter.
* When compiled as C++, a C++ exception is thrown, so the stack is
* properly unwound. This exception is not meant to be caught.
*/
state& state::check(integer& i, int narg) {
i = luaL_checkinteger(L, narg);
return *this;
}
#ifndef lua_Integer_long
/** Check an argument of the current function.
* @param l the long to hold the returned value
* @param narg the argument number to check
*
* This function checks if the given argument number is a long.
*
* @note This function is meant to be called from with in a
* lua::cfunction. The error throw is internal to the lua intrepeter.
* When compiled as C++, a C++ exception is thrown, so the stack is
* properly unwound. This exception is not meant to be caught.
*/
state& state::check(long& l, int narg) {
l = luaL_checklong(L, narg);
return *this;
}
#endif
/** Check an argument of the current function.
* @param s the string to hold the returned value
* @param narg the argument number to check
*
* This function checks if the given argument number is a string.
*
* @note This function is meant to be called from with in a
* lua::cfunction. The error throw is internal to the lua intrepeter.
* When compiled as C++, a C++ exception is thrown, so the stack is
* properly unwound. This exception is not meant to be caught.
*/
state& state::check(std::string& s, int narg) {
const char* c;
size_t l;
c = luaL_checklstring(L, narg, &l);
s.assign(c, l);
return *this;
}
/** Check an argument of the current function.
* @param n the lua::number (lua_Number) to hold the returned value
* @param narg the argument number to check
*
* This function checks if the given argument number is a lua::number
* (lua_Number, a double by default).
*
* @note This function is meant to be called from with in a lua::cfunction.
* The error throw is internal to the lua interpreter. When compiled as
* C++, a C++ exception is thrown, so the stack is properly unwound. This
* exception is not meant to be caught.
*/
state& state::check(number& n, int narg) {
n = luaL_checknumber(L, narg);
return *this;
}
#if 0
/** [specialization] Generate a Lua error (T = std::string).
*/
template<>
void state::error(const std::string& message) {
push(message);
lua_error(L);
}
#endif
/** Call a lua function.
* @param nargs the number of args to pass to the function
* @param nresults the number of values to return from the function
* @param on_error A stack index where the error handling function is
* stored.
* @note The error handling function must be pushed in the stack
* before the function to be called and its arguments.
* @returns a reference to this lua::state
*/
state& state::pcall(int nargs, int nresults, int on_error) {
throw_error(lua_pcall(L, nargs, nresults, on_error));
return *this;
}
/** Call a lua function in unprotected mode.
* @param nargs the number of args to pass to the function
* @param nresults the number of values to return from the function
* stored.
* @note If there is an error in the call the program will terminate.
* @returns a reference to this lua::state
*/
state& state::call(int nargs, int nresults) {
lua_call(L, nargs, nresults);
return *this;
}
/** Ensure the stack is at least the given size.
* @param size the size to use
*
* If the stack is smaller than the given size, it will grow to the
* specified size.
*
* @exception lua::exception Thrown if the operation fails.
* @returns a reference to this lua::state
*/
state& state::checkstack(int size) {
if (!lua_checkstack(L, size))
throw lua::exception("Error growing the stack");
return *this;
}
/** Set a new index as the top of the stack.
* @param index the index to use as the new top
* @note If the previous top was higher than the new one, top values
* are discarded. Otherwise this function pushs nils on to the stack
* to get the proper size.
* @returns a reference to this lua::state
*/
state& state::settop(int index) {
lua_settop(L, index);
return *this;
}
/** Get the number of elements in the stack.
* @note This value is also the index of the top element.
* @returns the number of elements in the stack
*/
int state::gettop() {
return lua_gettop(L);
}
/** Get the number of elements in the stack.
* @note This value is also the index of the top element.
* @returns the number of elements in the stack
*/
int state::size() {
return lua_gettop(L);
}
/** Check if the stack is empty.
* @returns true if the stack is empty, false otherwise
*/
bool state::empty() {
return !lua_gettop(L);
}
/** Move the top element to the given index.
* @param index the index to insert at
* @note All elements on top of the given index are shifted up to open
* space for this element.
* @returns a reference to this lua::state
*/
state& state::insert(int index) {
lua_insert(L, index);
return *this;
}
/** Replace the given index with the top element.
* @param index the index to replae
* @returns a reference to this lua::state
*/
state& state::replace(int index) {
lua_replace(L, index);
return *this;
}
/** Remove the given index from the stack.
* @param index the index to remove
* @note Elements are shifted down to fill in the empty spot.
* @returns a reference to this lua::state
*/
state& state::remove(int index) {
lua_remove(L, index);
return *this;
}
/** Remove the given number of elemens from the stack.
* @param elements the number of elements to remove
* @returns a reference to this lua::state
*/
state& state::pop(int elements) {
lua_pop(L, elements);
return *this;
}
/** Push a copy of the element at the given index to the top of the
* stack.
* @param index the index of the element to copy
* @returns a reference to this lua::state
*/
state& state::pushvalue(int index) {
lua_pushvalue(L, index);
return *this;
}
/** Create a new table on the stack.
* @returns a reference to this lua::state
*/
state& state::newtable() {
lua_newtable(L);
return *this;
}
/** Create a new metatable and add it to the registry.
* @param tname the name to use for the new metatable in the registry
*
* This function creates a new metatable and adds it to the registry
* with the given key. This function also pushes the new metatable
* onto the stack.
*
* @note Regardless of the return value, the new metatable is always pushed
* on to the stack.
*
* @return true if a new metatable was created, false if the registry
* alread has a key with the given name.
*/
bool state::newmetatable(const std::string& tname) {
return luaL_newmetatable(L, tname.c_str());
}
/** Create a new userdatum on the stack.
* @param nbytes the size of the new userdatum
* @return a pointer to the new userdatum
*/
void* state::newuserdata(size_t nbytes) {
return lua_newuserdata(L, nbytes);
}
/** Get a value from a table on the stack.
* @param index the index the table is stored at
*
* This function gets a value from the table at the given index and
* pushes it onto the stack.
*
* @note You should have already pushed the key used to reference this
* value onto the stack before calling this function.
*
* @returns a reference to this lua::state
*/
state& state::gettable(int index) {
lua_gettable(L, index);
return *this;
}
/** Get a value from a table on the stack.
* @param k the key
* @param index the index the table is stored at
*
* This function gets a value from the table at the given index and
* pushes it onto the stack.
*
* @returns a reference to this lua::state
*/
state& state::getfield(const std::string& k, int index) {
lua_getfield(L, index, k.c_str());
return *this;
}
/** Set a value in a table.
* @param index the index the table is stored at
*
* This function sets a value in a table stored at the given index.
*
* @note The key and value to be used should have already been pushed
* on the stack in that order.
*
* @returns a reference to this lua::state
*/
state& state::settable(int index) {
lua_settable(L, index);
return *this;
}
/** Set a field in a table.
* @param k the key
* @param index the index the table is stored at
*
* This function sets a value in a table stored at the given index.
*
* @note The value to be used should be on the top of the stack.
*
* @returns a reference to this lua::state
*/
state& state::setfield(const std::string& k, int index) {
lua_setfield(L, index, k.c_str());
return *this;
}
/** Get the metatable associated with the given registry entry.
* @param tname the name in the registry
*
* This function gets the metatable associated with the given key in
* the registry. The resulting metatable is pushed onto the stack.
*
* @note This function uses luaL_getmetatable() internally.
*
* @returns a reference to this lua::state
*/
state& state::getmetatable(const std::string& tname) {
luaL_getmetatable(L, tname.c_str());
return *this;
}
/** Get the metatable of the value at the given index.
* @param index the index the value is stored at
*
* This function pushes on to the stack the metatabe of the value at
* the given index.
*
* @note This function uses lua_getmetatable() internally.
*
* @returns false if the value at the given index does not have a
* metatable or if the index is not valid
*/
bool state::getmetatable(int index) {
return lua_getmetatable(L, index);
}
/** Get the next key value pair from a table on the stack.
* @param index the stack index the table is at
*
* This function pops a key from the stack and pushes the next key
* value pair to the stack. The key will be stored at index -2 and
* the value will be at index -1. The key is expected to be on the
* top of the stack.
*
* @note While traversing a table, do not call
* lua::state::to(std::string()) directly on a key, unless you know
* that the key is actually a string. lua::state::to(std::string())
* changes the value at the given index; this confuses the next call
* to lua::state::next().
*
* <strong>While Loop Example:</strong>
* @code
* while(L.next() != 0) {
* // do stuff
* L.pop();
* }
* @endcode
*
* <strong>For Loop Example:</strong>
* @code
* for(L.push(lua::nil()); L.next(); L.pop()) {
* // do stuff
* }
* @endcode
*
* @returns true as long as there are remaining items in the table
*/
bool state::next(int index) {
return lua_next(L, index);
}
/** Load a global symbol onto the stack.
* @param name the name of the global to load
*
* This function loads a global symbol onto the stack from the lua
* state.
*
* @returns a reference to this lua::state
*/
state& state::getglobal(const std::string& name) {
lua_getglobal(L, name.c_str());
return *this;
}
/** Set a global symbol.
* @param name the name of the global to set
*
* This function sets/creates a global symbol from the value above it
* on the stack.
*
* @note You should have pushed the value of the symbol onto the stack
* before calling this function.
*
* @returns a reference to this lua::state
*/
state& state::setglobal(const std::string& name) {
lua_setglobal(L, name.c_str());
return *this;
}
/** Load a file as a Lua chunk.
* @param filename the name of the file to load
* @returns a reference to this lua::state
*/
state& state::loadfile(const std::string& filename) {
throw_error(luaL_loadfile(L, filename.c_str()));
return *this;
}
/** Load a string as a Lua chunk.
* @param s the string to load
* @returns a reference to this lua::state
*/
state& state::loadstring(const std::string& s) {
throw_error(luaL_loadstring(L, s.c_str()));
return *this;
}
/** Get the length of a value on the stack.
* @param index the index the value is stored at
* @returns the length of the indicated value
*/
size_t state::objlen(int index) {
return lua_rawlen(L, index);
}
/** Get the value at index as a bool.
* @param boolean where to store the value
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @todo Instead of throwing an exception here, we may just return an
* error code.
* @throws lua::bad_conversion if the value on the stack could not be
* converted to the indicated type
* @returns a reference to this lua::state
*/
template<>
state& state::to(bool& boolean, int index) {
if (lua_isboolean(L, index))
boolean = lua_toboolean(L, index);
else
throw bad_conversion("Cannot convert non 'boolean' value to bool");
return *this;
}
/** Get the value at index as a string.
* @param string where to store the value
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @todo Instead of throwing an exception here, we may just return an
* error code.
*
* @note lua::state::to(std::string()) will convert the value at the
* indicated index to a string <em>on the stack</em>. This can
* confuse lua::state::next();
*
* @throws lua::bad_conversion if the value on the stack could not be
* converted to the indicated type
* @returns a reference to this lua::state
*/
template<>
state& state::to(std::string& string, int index) {
if (lua_isstring(L, index))
{
size_t len;
const char *str = lua_tolstring(L, index, &len);
string.replace(0, std::string::npos, str, len);
}
else
throw bad_conversion("Cannot convert value to string");
return *this;
}
/** Get the value at index as a bool.
* @param default_value this value is returned if the conversion fails
* @param index the index to get
* @note This function does \em not pop the value from the stack.
* @returns the indicated value from the stack or the default value if the
* conversion fails
*/
template<>
bool state::as(bool default_value, int index) {
if (lua_isboolean(L, index))
return lua_toboolean(L, index);
else
return default_value;
}
/** [specialization] Get the value at index as a bool (T = bool).
*/
template<>
bool state::as(int index) {
if (lua_isboolean(L, index))
return lua_toboolean(L, index);
else
throw bad_conversion("Cannot convert non 'boolean' value to bool");
}
/** [specialization] Get the value at index as a string (T = std::string).
* @note lua::state::as(std::string()) will convert the value at the
* indicated index to a string <em>on the stack</em>. This can confuse
* lua::state::next();
*/
template<>
std::string state::as(int index) {
if (lua_isstring(L, index))
{
size_t len;
const char *str = lua_tolstring(L, index, &len);
return std::string(str, len);
}
else
throw bad_conversion("Cannot convert value to string");
}
/** [specialization] Check if the given index is a nil (T = lua::nil).
*/
template<>
bool state::is<nil>(int index) {
return lua_isnil(L, index);
}
/** [specialization] Check if the given index is a boolean (T = bool).
*/
template<>
bool state::is<bool>(int index) {
return lua_isboolean(L, index);
}
/** [specialization] Check if the given index is a string (T = std::string).
*/
template<>
bool state::is<std::string>(int index) {
return lua_isstring(L, index);
}
/** [specialization] Check if the given index is a table (T = lua::table).
*/
template<>
bool state::is<table>(int index) {
return lua_istable(L, index);
}
/** [specialization] Check if the given index is a C function (T =
* lua::cfunction).
*/
template<>
bool state::is<cfunction>(int index) {
return lua_iscfunction(L, index);
}
/** [specialization] Check if the given index is a function (T =
* lua::function).
*/
template<>
bool state::is<function>(int index) {
return lua_isfunction(L, index);
}
/** [specialization] Check if the given index is userdata (T =
* lua::userdata).
*/
template<>
bool state::is<userdata>(int index) {
return lua_isuserdata(L, index);
}
/** [specialization] Check if the given index is light userdata (T =
* lua::lightuserdata).
*/
template<>
bool state::is<lightuserdata>(int index) {
return lua_islightuserdata(L, index);
}
}

@ -1,2 +0,0 @@
#include "lune.h"
lua::object *lua::local_object::mytbl=0;

@ -44,6 +44,7 @@ REQUIRE_GLOBAL(ui);
static bool autochop_enabled = false;
static int min_logs, max_logs;
static const int LOG_CAP_MAX = 99999;
static bool wait_for_threshold;
static PersistentDataItem config_autochop;
@ -157,6 +158,18 @@ private:
static WatchedBurrows watchedBurrows;
static int string_to_int(string s, int default_ = 0)
{
try
{
return std::stoi(s);
}
catch (std::exception&)
{
return default_;
}
}
static void save_config()
{
config_autochop.val() = watchedBurrows.getSerialisedIds();
@ -359,6 +372,7 @@ class ViewscreenAutochop : public dfhack_viewscreen
public:
ViewscreenAutochop()
{
edit_mode = EDIT_NONE;
burrows_column.multiselect = true;
burrows_column.setTitle("Burrows");
burrows_column.bottom_margin = 3;
@ -415,6 +429,46 @@ public:
void feed(set<df::interface_key> *input)
{
if (edit_mode != EDIT_NONE)
{
string entry = int_to_string(edit_mode == EDIT_MIN ? min_logs : max_logs);
if (input->count(interface_key::LEAVESCREEN) || input->count(interface_key::SELECT))
{
if (edit_mode == EDIT_MIN)
max_logs = std::max(min_logs, max_logs);
else if (edit_mode == EDIT_MAX)
min_logs = std::min(min_logs, max_logs);
edit_mode = EDIT_NONE;
}
else if (input->count(interface_key::STRING_A000))
{
if (!entry.empty())
entry.erase(entry.size() - 1);
}
else if (entry.size() < 5)
{
for (auto k = input->begin(); k != input->end(); ++k)
{
char ch = char(Screen::keyToChar(*k));
if (ch >= '0' && ch <= '9')
entry += ch;
}
}
switch (edit_mode)
{
case EDIT_MIN:
min_logs = string_to_int(entry);
break;
case EDIT_MAX:
max_logs = string_to_int(entry);
break;
default: break;
}
return;
}
bool key_processed = false;
message.clear();
switch (selected_column)
@ -456,6 +510,19 @@ public:
message = "Trees unmarked: " + int_to_string(count);
marked_tree_count = do_chop_designation(false, true);
}
else if (input->count(interface_key::CUSTOM_N))
{
edit_mode = EDIT_MIN;
}
else if (input->count(interface_key::CUSTOM_M))
{
edit_mode = EDIT_MAX;
}
else if (input->count(interface_key::CUSTOM_SHIFT_N))
{
min_logs = LOG_CAP_MAX + 1;
max_logs = LOG_CAP_MAX + 1;
}
else if (input->count(interface_key::CUSTOM_H))
{
change_min_logs(-1);
@ -537,8 +604,38 @@ public:
OutputHotkeyString(x, y, "Toggle Burrow", "Enter", true, left_margin);
if (autochop_enabled)
{
OutputLabelString(x, y, "Min Logs", "hjHJ", int_to_string(min_logs), true, left_margin);
OutputLabelString(x, y, "Max Logs", "klKL", int_to_string(max_logs), true, left_margin);
using namespace df::enums::interface_key;
const struct {
const char *caption;
int count;
bool in_edit;
df::interface_key key;
df::interface_key skeys[4];
} rows[] = {
{"Min Logs: ", min_logs, edit_mode == EDIT_MIN, CUSTOM_N, {CUSTOM_H, CUSTOM_J, CUSTOM_SHIFT_H, CUSTOM_SHIFT_J}},
{"Max Logs: ", max_logs, edit_mode == EDIT_MAX, CUSTOM_M, {CUSTOM_K, CUSTOM_L, CUSTOM_SHIFT_K, CUSTOM_SHIFT_L}}
};
for (size_t i = 0; i < sizeof(rows)/sizeof(rows[0]); ++i)
{
auto row = rows[i];
OutputHotkeyString(x, y, row.caption, row.key);
auto prev_x = x;
if (row.in_edit)
OutputString(COLOR_LIGHTCYAN, x, y, int_to_string(row.count) + "_");
else if (row.count <= LOG_CAP_MAX)
OutputString(COLOR_LIGHTGREEN, x, y, int_to_string(row.count));
else
OutputString(COLOR_LIGHTBLUE, x, y, "Unlimited");
if (edit_mode == EDIT_NONE)
{
x = std::max(x, prev_x + 10);
for (size_t j = 0; j < sizeof(row.skeys)/sizeof(row.skeys[0]); ++j)
OutputString(COLOR_LIGHTGREEN, x, y, DFHack::Screen::getKeyDisplay(row.skeys[j]));
OutputString(COLOR_WHITE, x, y, ": Step");
}
OutputString(COLOR_WHITE, x, y, "", true, left_margin);
}
OutputHotkeyString(x, y, "No limit", CUSTOM_SHIFT_N, true, left_margin);
}
++y;
@ -565,6 +662,7 @@ private:
int marked_tree_count;
MapExtras::MapCache mcache;
string message;
enum { EDIT_NONE, EDIT_MIN, EDIT_MAX } edit_mode;
void validateColumn()
{

@ -107,6 +107,23 @@ void add_tasks(gem_map &gem_types, df::building_workshopst *workshop) {
}
}
bool valid_gem(df::item* item) {
if (item->getType() != item_type::ROUGH) return false;
if (item->getMaterial() != builtin_mats::INORGANIC) return false;
if (item->flags.bits.in_job) return false;
if (item->flags.bits.forbid) return false;
if (item->flags.bits.dump) return false;
if (item->flags.bits.owned) return false;
if (item->flags.bits.trader) return false;
if (item->flags.bits.hostile) return false;
if (item->flags.bits.removed) return false;
if (item->flags.bits.encased) return false;
if (item->flags.bits.construction) return false;
if (item->flags.bits.garbage_collect) return false;
if (item->flags.bits.in_building) return false;
return true;
}
void create_jobs() {
// Creates jobs in Jeweler's Workshops as necessary.
// Todo: Consider path availability?
@ -137,7 +154,7 @@ void create_jobs() {
Buildings::StockpileIterator stored;
for (stored.begin(stockpile); !stored.done(); ++stored) {
auto item = *stored;
if (item->getType() == item_type::ROUGH && item->getMaterial() == builtin_mats::INORGANIC) {
if (valid_gem(item)) {
stockpiled.insert(item->id);
piled[item->getMaterialIndex()] += 1;
}
@ -182,8 +199,7 @@ void create_jobs() {
auto gems = world->items.other[items_other_id::ROUGH];
for (auto g = gems.begin(); g != gems.end(); ++g) {
auto item = *g;
// ROUGH also includes raw glass; the INORGANIC check filters that out.
if (item->getMaterial() == builtin_mats::INORGANIC && !stockpiled.count(item->id)) {
if (valid_gem(item) && !stockpiled.count(item->id)) {
available[item->getMaterialIndex()] += 1;
}
}
@ -268,7 +284,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
if (enabled && World::isFortressMode()) {
// Determine whether auto gem cutting has been disabled for this fort.
auto config = World::GetPersistentData(CONFIG_KEY);
running = !(config.isValid() && config.ival(0));
running = config.isValid() && !config.ival(0);
last_frame_count = world->frame_counter;
}
} else if (event == DFHack::SC_MAP_UNLOADED) {
@ -286,9 +302,9 @@ DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
}
enabled = enable;
running = enabled && World::isFortressMode();
}
running = enabled && World::isFortressMode();
return CR_OK;
}

@ -365,7 +365,15 @@ static const dwarf_state dwarf_states[] = {
BUSY /* PushTrackVehicle */,
BUSY /* PlaceTrackVehicle */,
BUSY /* StoreItemInVehicle */,
BUSY /* GeldAnimal */
BUSY /* GeldAnimal */,
BUSY /* MakeFigurine */,
BUSY /* MakeAmulet */,
BUSY /* MakeScepter */,
BUSY /* MakeCrown */,
BUSY /* MakeRing */,
BUSY /* MakeEarring */,
BUSY /* MakeBracelet */,
BUSY /* MakeGem */
};
struct labor_info

@ -1,153 +0,0 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include <vector>
#include <string>
#include "modules/Vermin.h"
#include "modules/Materials.h"
using std::vector;
using std::string;
using namespace DFHack;
command_result colonies (color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("colonies");
REQUIRE_GLOBAL(world); // used by Materials
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"colonies", "List or change wild colonies (ants hills and such)",
colonies, false,
" Without any options, this command lists all the vermin colonies present.\n"
"Options:\n"
//" kill - destroy colonies\n" // unlisted because it's likely broken anyway
" bees - turn colonies into honey bee hives\n"
));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
void destroyColonies();
void convertColonies(Materials *Materials);
void showColonies(color_ostream &out, Materials *Materials);
command_result colonies (color_ostream &out, vector <string> & parameters)
{
bool destroy = false;
bool convert = false;
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "kill")
destroy = true;
else if(parameters[i] == "bees")
convert = true;
else
return CR_WRONG_USAGE;
}
if (destroy && convert)
{
out.printerr("Kill or make bees? DECIDE!\n");
return CR_FAILURE;
}
CoreSuspender suspend;
Materials * materials = Core::getInstance().getMaterials();
materials->ReadCreatureTypesEx();
if (destroy)
destroyColonies();
else if (convert)
convertColonies(materials);
else
showColonies(out, materials);
materials->Finish();
return CR_OK;
}
//FIXME: this is probably bullshit
void destroyColonies()
{
uint32_t numSpawnPoints = Vermin::getNumVermin();
for (uint32_t i = 0; i < numSpawnPoints; i++)
{
Vermin::t_vermin sp;
Vermin::Read(i, sp);
if (sp.visible && sp.is_colony)
{
sp.visible = false;
Vermin::Write(i, sp);
}
}
}
// Convert all colonies to honey bees.
void convertColonies(Materials *Materials)
{
int bee_idx = -1;
for (size_t i = 0; i < Materials->raceEx.size(); i++)
{
if (Materials->raceEx[i].id == "HONEY_BEE")
{
bee_idx = i;
break;
}
}
if (bee_idx == -1)
{
std::cerr << "Honey bees not present in game." << std::endl;
return;
}
uint32_t numSpawnPoints = Vermin::getNumVermin();
for (uint32_t i = 0; i < numSpawnPoints; i++)
{
Vermin::t_vermin sp;
Vermin::Read(i, sp);
if (sp.visible && sp.is_colony)
{
sp.race = bee_idx;
Vermin::Write(i, sp);
}
}
}
void showColonies(color_ostream &out, Materials *Materials)
{
uint32_t numSpawnPoints = Vermin::getNumVermin();
int numColonies = 0;
for (uint32_t i = 0; i < numSpawnPoints; i++)
{
Vermin::t_vermin sp;
Vermin::Read(i, sp);
if (sp.visible && sp.is_colony)
{
numColonies++;
string race="(no race)";
if(sp.race != -1)
race = Materials->raceEx[sp.race].id;
out.print("Colony %u: %s at %d:%d:%d\n", i,
race.c_str(), sp.x, sp.y, sp.z);
}
}
if (numColonies == 0)
out << "No colonies present." << std::endl;
}

@ -33,6 +33,7 @@ using namespace DFHack;
using namespace df::enums;
DFHACK_PLUGIN("createitem");
REQUIRE_GLOBAL(cursor);
REQUIRE_GLOBAL(world);
REQUIRE_GLOBAL(ui);
REQUIRE_GLOBAL(gametype);
@ -68,7 +69,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_item = false)
bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_item = false, bool move_to_cursor = false)
{
vector<df::reaction_product*> out_products;
vector<df::item *> out_items;
@ -115,6 +116,8 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
}
if (on_ground)
out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z);
if (move_to_cursor)
out_items[i]->moveToGround(cursor->x, cursor->y, cursor->z);
if (is_gloves)
{
// if the reaction creates gloves without handedness, then create 2 sets (left and right)
@ -138,6 +141,7 @@ command_result df_createitem (color_ostream &out, vector <string> & parameters)
int16_t mat_type = -1;
int32_t mat_index = -1;
int count = 1;
bool move_to_cursor = false;
if (parameters.size() == 1)
{
@ -355,6 +359,12 @@ command_result df_createitem (color_ostream &out, vector <string> & parameters)
// Use the adventurer unit
unit = world->units.active[0];
}
else if (!world->units.active.empty() && cursor->x >= 0)
{
// Use the first possible unit and the cursor position
unit = world->units.active[0];
move_to_cursor = true;
}
else
{
out.printerr("No unit selected!\n");
@ -410,7 +420,7 @@ command_result df_createitem (color_ostream &out, vector <string> & parameters)
out.printerr("Previously selected building no longer exists - item will be placed on the floor.\n");
}
bool result = makeItem(prod, unit);
bool result = makeItem(prod, unit, false, move_to_cursor);
delete prod;
if (!result)
{

@ -360,7 +360,16 @@ static const dwarf_state dwarf_states[] = {
BUSY /* CarveTrack */,
BUSY /* PushTrackVehicle */,
BUSY /* PlaceTrackVehicle */,
BUSY /* StoreItemInVehicle */
BUSY /* StoreItemInVehicle */,
BUSY /* GeldAnimal */,
BUSY /* MakeFigurine */,
BUSY /* MakeAmulet */,
BUSY /* MakeScepter */,
BUSY /* MakeCrown */,
BUSY /* MakeRing */,
BUSY /* MakeEarring */,
BUSY /* MakeBracelet */,
BUSY /* MakeGem */
};
struct labor_info
@ -1328,6 +1337,15 @@ public:
job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE);
job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE);
job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE);
job_to_labor_table[df::job_type::GeldAnimal] = jlf_const(df::unit_labor::GELD);
job_to_labor_table[df::job_type::MakeFigurine] = jlf_make_object;
job_to_labor_table[df::job_type::MakeAmulet] = jlf_make_object;
job_to_labor_table[df::job_type::MakeScepter] = jlf_make_object;
job_to_labor_table[df::job_type::MakeCrown] = jlf_make_object;
job_to_labor_table[df::job_type::MakeRing] = jlf_make_object;
job_to_labor_table[df::job_type::MakeEarring] = jlf_make_object;
job_to_labor_table[df::job_type::MakeBracelet] = jlf_make_object;
job_to_labor_table[df::job_type::MakeGem] = jlf_make_object;
};
df::unit_labor find_job_labor(df::job* j)

@ -1,18 +1,20 @@
// Lists embeded STL vectors and pointers to STL vectors found in the given
// memory range.
//
// Linux only, enabled with BUILD_VECTORS cmake option.
// Linux and OS X only
#include "Core.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <MemAccess.h>
#include <vector>
#include <string>
#include <stdio.h>
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "MemAccess.h"
#include "VersionInfo.h"
using std::vector;
using std::string;
using namespace DFHack;
@ -58,7 +60,7 @@ static bool hexOrDec(string &str, uint32_t &value)
return false;
}
static bool mightBeVec(vector<t_memrange> &heap_ranges,
static bool mightBeVec(vector<t_memrange> &ranges,
t_vecTriplet *vec)
{
if ((vec->start > vec->end) || (vec->end > vec->alloc_end))
@ -70,9 +72,9 @@ static bool mightBeVec(vector<t_memrange> &heap_ranges,
if (((int)vec->start % 4 != 0) || ((int)vec->alloc_end % 4 != 0))
return false;
for (size_t i = 0; i < heap_ranges.size(); i++)
for (size_t i = 0; i < ranges.size(); i++)
{
t_memrange &range = heap_ranges[i];
t_memrange &range = ranges[i];
if (range.isInRange(vec->start) && range.isInRange(vec->alloc_end))
return true;
@ -92,7 +94,7 @@ static bool inAnyRange(vector<t_memrange> &ranges, void * ptr)
return false;
}
static bool getHeapRanges(color_ostream &out, std::vector<t_memrange> &heap_ranges)
static bool getRanges(color_ostream &out, std::vector<t_memrange> &out_ranges)
{
std::vector<t_memrange> ranges;
@ -102,19 +104,13 @@ static bool getHeapRanges(color_ostream &out, std::vector<t_memrange> &heap_rang
{
t_memrange &range = ranges[i];
// Some kernels don't report [heap], and the heap can consist of
// more segments than just the one labeled with [heap], so include
// all segments which *might* be part of the heap.
if (range.read && range.write && !range.shared)
{
if (strlen(range.name) == 0 || strcmp(range.name, "[heap]") == 0)
heap_ranges.push_back(range);
}
out_ranges.push_back(range);
}
if (heap_ranges.empty())
if (out_ranges.empty())
{
out << "No possible heap segments." << std::endl;
out << "No readable segments." << std::endl;
return false;
}
@ -132,13 +128,26 @@ static void vectorsUsage(color_ostream &con)
}
static void printVec(color_ostream &con, const char* msg, t_vecTriplet *vec,
uint32_t start, uint32_t pos)
uint32_t start, uint32_t pos, std::vector<t_memrange> &ranges)
{
uint32_t length = (int)vec->end - (int)vec->start;
uint32_t offset = pos - start;
con.print("%8s offset %06p, addr %010p, start %010p, length %u\n",
con.print("%8s offset %06p, addr %010p, start %010p, length %u",
msg, offset, pos, vec->start, length);
if (length >= 4 && length % 4 == 0)
{
void *ptr = vec->start;
for (int level = 0; level < 2; level++)
{
if (inAnyRange(ranges, ptr))
ptr = *(void**)ptr;
}
std::string classname;
if (Core::getInstance().vinfo->getVTableName(ptr, classname))
con.print(", 1st item: %s", classname.c_str());
}
con.print("\n");
}
command_result df_vectors (color_ostream &con, vector <string> & parameters)
@ -171,17 +180,17 @@ command_result df_vectors (color_ostream &con, vector <string> & parameters)
CoreSuspender suspend;
std::vector<t_memrange> heap_ranges;
std::vector<t_memrange> ranges;
if (!getHeapRanges(con, heap_ranges))
if (!getRanges(con, ranges))
{
return CR_FAILURE;
}
bool startInRange = false;
for (size_t i = 0; i < heap_ranges.size(); i++)
for (size_t i = 0; i < ranges.size(); i++)
{
t_memrange &range = heap_ranges[i];
t_memrange &range = ranges[i];
if (!range.isInRange((void *)start))
continue;
@ -217,9 +226,9 @@ command_result df_vectors (color_ostream &con, vector <string> & parameters)
{
t_vecTriplet* vec = (t_vecTriplet*) pos;
if (mightBeVec(heap_ranges, vec))
if (mightBeVec(ranges, vec))
{
printVec(con, "VEC:", vec, start, pos);
printVec(con, "VEC:", vec, start, pos, ranges);
// Skip over rest of vector.
pos += sizeof(t_vecTriplet) - ptr_size;
continue;
@ -231,13 +240,13 @@ command_result df_vectors (color_ostream &con, vector <string> & parameters)
{
uint32_t ptr = * ( (uint32_t*) pos);
if (inAnyRange(heap_ranges, (void *) ptr))
if (inAnyRange(ranges, (void *) ptr))
{
t_vecTriplet* vec = (t_vecTriplet*) ptr;
if (mightBeVec(heap_ranges, vec))
if (mightBeVec(ranges, vec))
{
printVec(con, "VEC PTR:", vec, start, pos);
printVec(con, "VEC PTR:", vec, start, pos, ranges);
continue;
}
}
@ -282,9 +291,9 @@ command_result df_clearvec (color_ostream &con, vector <string> & parameters)
CoreSuspender suspend;
std::vector<t_memrange> heap_ranges;
std::vector<t_memrange> ranges;
if (!getHeapRanges(con, heap_ranges))
if (!getRanges(con, ranges))
{
return CR_FAILURE;
}
@ -305,7 +314,7 @@ command_result df_clearvec (color_ostream &con, vector <string> & parameters)
continue;
}
if (!inAnyRange(heap_ranges, (void *) addr))
if (!inAnyRange(ranges, (void *) addr))
{
con << addr_str << " not in any valid address range." << std::endl;
continue;
@ -315,7 +324,7 @@ command_result df_clearvec (color_ostream &con, vector <string> & parameters)
bool ptr = false;
t_vecTriplet* vec = (t_vecTriplet*) addr;
if (mightBeVec(heap_ranges, vec))
if (mightBeVec(ranges, vec))
valid = true;
else
{
@ -323,7 +332,7 @@ command_result df_clearvec (color_ostream &con, vector <string> & parameters)
addr = * ( (uint32_t*) addr);
vec = (t_vecTriplet*) addr;
if (inAnyRange(heap_ranges, (void *) addr) && mightBeVec(heap_ranges, vec))
if (inAnyRange(ranges, (void *) addr) && mightBeVec(ranges, vec))
{
valid = true;
ptr = true;

@ -1 +0,0 @@
Subproject commit 5b167d2ba89b877d80e0609feae8771aeaef356d

@ -1,31 +0,0 @@
PROJECT (export)
# A list of source files
SET(PROJECT_SRCS
dwarfexport.cpp
)
# A list of headers
SET(PROJECT_HDRS
dwarfexport.h
)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
#linux
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
)
# windows
ELSE(UNIX)
SET(PROJECT_LIBS
# add any extra linux libs here
${PROJECT_LIBS}
$(NOINHERIT)
)
ENDIF(UNIX)
# this makes sure all the stuff is put in proper places and linked to dfhack
DFHACK_PLUGIN(dwarfexport ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})

@ -1,244 +0,0 @@
// some headers required for a plugin. Nothing special, just the basics.
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
#define DFHACK_WANT_MISCUTILS
#include <Core.h>
#include <VersionInfo.h>
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <modules/Units.h>
#include <modules/Translation.h>
#include <df/ui.h>
#include <df/world.h>
#include <df/unit.h>
#include <df/unit_soul.h>
#include <df/unit_labor.h>
#include <df/unit_skill.h>
/*
dwarfexport
===========
Export dwarves to RuneSmith-compatible XML; also unused by modern tools.
*/
using namespace DFHack;
using df::global::ui;
using df::global::world;
// our own, empty header.
#include "dwarfexport.h"
#include <df/personality_facet_type.h>
// Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom
command_result export_dwarves (color_ostream &con, std::vector <std::string> & parameters);
DFHACK_PLUGIN("dwarfexport");
// Mandatory init function. If you have some global state, create it here.
DFhackCExport command_result plugin_init (color_ostream &con, std::vector <PluginCommand> &commands)
{
// Fill the command list with your commands.
commands.push_back(PluginCommand("dwarfexport",
"Export dwarves to RuneSmith-compatible XML.",
export_dwarves /*,
true or false - true means that the command can't be used from non-interactive user interface'*/));
return CR_OK;
}
// This is called right before the plugin library is removed from memory.
DFhackCExport command_result plugin_shutdown (color_ostream &con)
{
return CR_OK;
}
static const char* physicals[] = {
"Strength",
"Agility",
"Toughness",
"Endurance",
"Recuperation",
"DiseaseResistance",
};
static const char* mentals[] = {
"AnalyticalAbility",
"Focus",
"Willpower",
"Creatvity", //Speeling deliberate
"Intuition",
"Patience",
"Memory",
"LinguisticAbility",
"SpatialSense",
"Musicality",
"KinaestheticSense",
"Empathy",
"SocialAwareness",
};
static void element(const char* name, const char* content, ostream& out, const char* extra_indent="") {
out << extra_indent << " <" << name << ">" << content << "</" << name << ">" << endl;
}
static void element(const char* name, const uint32_t content, ostream& out, const char* extra_indent="") {
out << extra_indent << " <" << name << ">" << content << "</" << name << ">" << endl;
}
static void printAttributes(color_ostream &con, df::unit* cre, ostream& out) {
out << " <Attributes>" << endl;
for (int i = 0; i < NUM_CREATURE_PHYSICAL_ATTRIBUTES; i++) {
element(physicals[i], cre->body.physical_attrs[i].value, out, " ");
}
df::unit_soul * s = cre->status.current_soul;
if (s) {
for (int i = 0; i < NUM_CREATURE_MENTAL_ATTRIBUTES; i++) {
element(mentals[i], s->mental_attrs[i].value, out, " ");
}
}
out << " </Attributes>" << endl;
}
static void printTraits(color_ostream &con, df::unit* cre, ostream& out)
{
out << " <Traits>" << endl;
df::unit_soul * s = cre->status.current_soul;
if (s)
{
FOR_ENUM_ITEMS(personality_facet_type,index)
{
out << " <Trait name='" << ENUM_KEY_STR(personality_facet_type, index) <<
"' value='" << s->traits[index] << "'>";
//FIXME: needs reimplementing trait string generation
/*
string trait = con->vinfo->getTrait(i, s->traits[i]);
if (!trait.empty()) {
out << trait.c_str();
}
*/
out << "</Trait>" << endl;
}
}
out << " </Traits>" << endl;
}
static int32_t getCreatureAge(df::unit* cre)
{
int32_t yearDifference = *df::global::cur_year - cre->relations.birth_year;
// If the birthday this year has not yet passed, subtract one year.
// ASSUMPTION: birth_time is on the same scale as cur_year_tick
if (cre->relations.birth_time >= *df::global::cur_year_tick) {
yearDifference--;
}
return yearDifference;
}
static void printLabors(color_ostream &con, df::unit* cre, ostream& out)
{
// Using British spelling here, consistent with Runesmith
out << " <Labours>" << endl;
FOR_ENUM_ITEMS(unit_labor, iCount)
{
if (cre->status.labors[iCount]) {
// Get the caption for the labor index.
element("Labour", ENUM_ATTR_STR(unit_labor, caption, iCount), out);
}
}
out << " </Labours>" << endl;
}
static void printSkill(color_ostream &con, df::unit_skill* skill, ostream& out)
{
out << " <Skill>" << endl;
element("Name", ENUM_ATTR_STR(job_skill, caption, skill->id), out);
element("Level", skill->rating, out);
out << " </Skill>" << endl;
}
static void printSkills(color_ostream &con, df::unit* cre, ostream& out)
{
std::vector<df::unit_skill* > vSkills = cre->status.current_soul->skills;
out << " <Skills>" << endl;
for (int iCount = 0; iCount < vSkills.size(); iCount++)
{
printSkill(con, vSkills.at(iCount), out);
}
out << " </Skills>" << endl;
}
// GDC needs:
// Name
// Nickname
// Sex
// Attributes
// Traits
static void export_dwarf(color_ostream &con, df::unit* cre, ostream& out) {
string info = cre->name.first_name;
info += " ";
info += Translation::TranslateName(&cre->name, false);
info[0] = toupper(info[0]);
con.print("Exporting %s\n", info.c_str());
out << " <Creature>" << endl;
element("Name", info.c_str(), out);
element("Nickname", cre->name.nickname.c_str(), out);
element("Sex", cre->sex == 0 ? "Female" : "Male", out);
element("Age", getCreatureAge(cre), out); // Added age, active labors, and skills March 9, 2012
printAttributes(con, cre, out);
printTraits(con, cre, out);
printLabors(con, cre, out);
printSkills(con, cre, out);
out << " </Creature>" << endl;
}
command_result export_dwarves (color_ostream &con, std::vector <std::string> & parameters)
{
string filename;
if (parameters.size() == 1) {
filename = parameters[0];
} else {
con.print("export <filename>\n");
return CR_OK;
}
ofstream outf(filename.c_str());
if (!outf) {
con.printerr("Failed to open file %s\n", filename.c_str());
return CR_FAILURE;
}
CoreSuspender suspend;
uint32_t race = ui->race_id;
uint32_t civ = ui->civ_id;
outf << "<?xml version='1.0' encoding='ibm850'?>" << endl << "<Creatures>" << endl;
for (int i = 0; i < world->units.all.size(); ++i)
{
df::unit* cre = world->units.all[i];
if (cre->race == race && cre->civ_id == civ) {
export_dwarf(con, cre, outf);
}
}
outf << "</Creatures>" << endl;
return CR_OK;
}

@ -92,54 +92,37 @@ static bool is_lua_hook(const std::string &name)
/*
* Hooks
*/
static void handle_fillsidebar(color_ostream &out,df::building_actual*,bool *call_native){};
static void handle_postfillsidebar(color_ostream &out,df::building_actual*){};
static void handle_reaction_done(color_ostream &out,df::reaction*, df::reaction_product_itemst*, df::unit *unit, std::vector<df::item*> *in_items,std::vector<df::reaction_reagent*> *in_reag
, std::vector<df::item*> *out_items,bool *call_native){};
static void handle_contaminate_wound(color_ostream &out,df::item_actual*,df::unit* unit, df::unit_wound* wound, uint8_t a1, int16_t a2){};
static void handle_projitem_ci(color_ostream &out,df::proj_itemst*,bool){};
static void handle_projitem_cm(color_ostream &out,df::proj_itemst*){};
static void handle_projunit_ci(color_ostream &out,df::proj_unitst*,bool){};
static void handle_projunit_cm(color_ostream &out,df::proj_unitst*){};
DEFINE_LUA_EVENT_2(onWorkshopFillSidebarMenu, handle_fillsidebar, df::building_actual*,bool* );
DEFINE_LUA_EVENT_1(postWorkshopFillSidebarMenu, handle_postfillsidebar, df::building_actual*);
DEFINE_LUA_EVENT_7(onReactionComplete, handle_reaction_done,df::reaction*, df::reaction_product_itemst*, df::unit *, std::vector<df::item*> *,std::vector<df::reaction_reagent*> *,std::vector<df::item*> *,bool *);
DEFINE_LUA_EVENT_5(onItemContaminateWound, handle_contaminate_wound, df::item_actual*,df::unit* , df::unit_wound* , uint8_t , int16_t );
DEFINE_LUA_EVENT_NH_2(onWorkshopFillSidebarMenu, df::building_actual*, bool*);
DEFINE_LUA_EVENT_NH_1(postWorkshopFillSidebarMenu, df::building_actual*);
DEFINE_LUA_EVENT_NH_7(onReactionCompleting, df::reaction*, df::reaction_product_itemst*, df::unit *, std::vector<df::item*> *, std::vector<df::reaction_reagent*> *, std::vector<df::item*> *, bool *);
DEFINE_LUA_EVENT_NH_6(onReactionComplete, df::reaction*, df::reaction_product_itemst*, df::unit *, std::vector<df::item*> *, std::vector<df::reaction_reagent*> *, std::vector<df::item*> *);
DEFINE_LUA_EVENT_NH_5(onItemContaminateWound, df::item_actual*, df::unit*, df::unit_wound*, uint8_t, int16_t);
//projectiles
DEFINE_LUA_EVENT_2(onProjItemCheckImpact, handle_projitem_ci, df::proj_itemst*,bool );
DEFINE_LUA_EVENT_1(onProjItemCheckMovement, handle_projitem_cm, df::proj_itemst*);
DEFINE_LUA_EVENT_2(onProjUnitCheckImpact, handle_projunit_ci, df::proj_unitst*,bool );
DEFINE_LUA_EVENT_1(onProjUnitCheckMovement, handle_projunit_cm, df::proj_unitst* );
DEFINE_LUA_EVENT_NH_2(onProjItemCheckImpact, df::proj_itemst*, bool);
DEFINE_LUA_EVENT_NH_1(onProjItemCheckMovement, df::proj_itemst*);
DEFINE_LUA_EVENT_NH_2(onProjUnitCheckImpact, df::proj_unitst*, bool);
DEFINE_LUA_EVENT_NH_1(onProjUnitCheckMovement, df::proj_unitst*);
//event manager
static void handle_int32t(color_ostream &out,int32_t){}; //we don't use this so why not use it everywhere
static void handle_job_init(color_ostream &out,df::job*){};
static void handle_job_complete(color_ostream &out,df::job*){};
static void handle_constructions(color_ostream &out,df::construction*){};
static void handle_syndrome(color_ostream &out,int32_t,int32_t){};
static void handle_inventory_change(color_ostream& out,int32_t,int32_t,df::unit_inventory_item*,df::unit_inventory_item*){};
static void handle_report(color_ostream& out,int32_t){};
static void handle_unitAttack(color_ostream& out,int32_t,int32_t,int32_t){};
static void handle_unload(color_ostream& out){};
static void handle_interaction(color_ostream& out, std::string, std::string, int32_t, int32_t, int32_t, int32_t){};
DEFINE_LUA_EVENT_1(onBuildingCreatedDestroyed, handle_int32t, int32_t);
DEFINE_LUA_EVENT_1(onJobInitiated,handle_job_init,df::job*);
DEFINE_LUA_EVENT_1(onJobCompleted,handle_job_complete,df::job*);
DEFINE_LUA_EVENT_1(onUnitDeath,handle_int32t,int32_t);
DEFINE_LUA_EVENT_1(onItemCreated,handle_int32t,int32_t);
DEFINE_LUA_EVENT_1(onConstructionCreatedDestroyed, handle_constructions, df::construction*);
DEFINE_LUA_EVENT_2(onSyndrome, handle_syndrome, int32_t,int32_t);
DEFINE_LUA_EVENT_1(onInvasion,handle_int32t,int32_t);
DEFINE_LUA_EVENT_4(onInventoryChange,handle_inventory_change,int32_t,int32_t,df::unit_inventory_item*,df::unit_inventory_item*);
DEFINE_LUA_EVENT_1(onReport,handle_report,int32_t);
DEFINE_LUA_EVENT_3(onUnitAttack,handle_unitAttack,int32_t,int32_t,int32_t);
DEFINE_LUA_EVENT_0(onUnload,handle_unload);
DEFINE_LUA_EVENT_6(onInteraction,handle_interaction, std::string, std::string, int32_t, int32_t, int32_t, int32_t);
DEFINE_LUA_EVENT_NH_1(onBuildingCreatedDestroyed, int32_t);
DEFINE_LUA_EVENT_NH_1(onJobInitiated, df::job*);
DEFINE_LUA_EVENT_NH_1(onJobCompleted, df::job*);
DEFINE_LUA_EVENT_NH_1(onUnitDeath, int32_t);
DEFINE_LUA_EVENT_NH_1(onItemCreated, int32_t);
DEFINE_LUA_EVENT_NH_1(onConstructionCreatedDestroyed, df::construction*);
DEFINE_LUA_EVENT_NH_2(onSyndrome, int32_t, int32_t);
DEFINE_LUA_EVENT_NH_1(onInvasion, int32_t);
DEFINE_LUA_EVENT_NH_4(onInventoryChange, int32_t, int32_t, df::unit_inventory_item*, df::unit_inventory_item*);
DEFINE_LUA_EVENT_NH_1(onReport, int32_t);
DEFINE_LUA_EVENT_NH_3(onUnitAttack, int32_t, int32_t, int32_t);
DEFINE_LUA_EVENT_NH_0(onUnload);
DEFINE_LUA_EVENT_NH_6(onInteraction, std::string, std::string, int32_t, int32_t, int32_t, int32_t);
DFHACK_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu),
DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu),
DFHACK_LUA_EVENT(onReactionCompleting),
DFHACK_LUA_EVENT(onReactionComplete),
DFHACK_LUA_EVENT(onItemContaminateWound),
DFHACK_LUA_EVENT(onProjItemCheckImpact),
@ -306,7 +289,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(furnace_hook, fillSidebarMenu);
struct product_hook : item_product {
typedef item_product interpose_base;
DEFINE_VMETHOD_INTERPOSE(
void, produce,
(df::unit *unit,
@ -326,7 +309,7 @@ struct product_hook : item_product {
df::reaction* this_reaction=product->react;
CoreSuspendClaimer suspend;
bool call_native=true;
onReactionComplete(out,this_reaction,(df::reaction_product_itemst*)this,unit,in_items,in_reag,out_items,&call_native);
onReactionCompleting(out,this_reaction,(df::reaction_product_itemst*)this,unit,in_items,in_reag,out_items,&call_native);
if(!call_native)
return;
@ -335,7 +318,7 @@ struct product_hook : item_product {
if ( out_items->size() == out_item_count )
return;
//if it produced something, call the scripts
onReactionComplete(out,this_reaction,(df::reaction_product_itemst*)this,unit,in_items,in_reag,out_items,NULL);
onReactionComplete(out,this_reaction,(df::reaction_product_itemst*)this,unit,in_items,in_reag,out_items);
}
};
@ -417,8 +400,6 @@ static bool find_reactions(color_ostream &out)
for (size_t i = 0; i < rlist.size(); i++)
{
//if (!is_lua_hook(rlist[i]->code))
// continue;
reactions[rlist[i]->code].react = rlist[i];
}
@ -460,7 +441,6 @@ static void world_specific_hooks(color_ostream &out,bool enable)
{
if(enable && find_reactions(out))
{
//out.print("Detected reaction hooks - enabling plugin.\n");
INTERPOSE_HOOK(product_hook, produce).apply(true);
}
else

@ -10,6 +10,7 @@
#include "df/unit.h"
#include "df/unit_action.h"
#include "df/map_block.h"
#include "df/units_other_id.h"
using std::string;
using std::vector;
@ -97,6 +98,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// move unit to destination
unit->pos = unit->path.dest;
unit->path.path.clear();
//move unit's riders(including babies) to destination
if (unit->flags1.bits.ridden)
{
for (size_t j = 0; j < world->units.other[units_other_id::ANY_RIDER].size(); j++)
{
df::unit *rider = world->units.other[units_other_id::ANY_RIDER][j];
if (rider->relations.rider_mount_id == unit->id)
rider->pos = unit->pos;
}
}
} while (0);
if (enable_fastdwarf)

@ -11,8 +11,11 @@ local _ENV = mkmodule('plugins.building-hacks')
fix_impassible -- make impassible tiles impassible to liquids too
consume -- how much machine power is needed to work
produce -- how much machine power is produced
gears -- a table or {x=?,y=?} of connection points for machines
needs_power -- needs power to be able to add jobs
action -- a table of number (how much ticks to skip) and a function which gets called on shop update
canBeRoomSubset -- room is considered in to be part of the building defined by chairs etc...
auto_gears -- find the gears automatically and animate them
gears -- a table or {x=?,y=?} of connection points for machines
animate -- a table of
frames -- a table of
tables of 4 numbers (tile,fore,back,bright) OR
@ -76,37 +79,101 @@ local function processFrames(shop_def,frames)
end
return frames
end
local function findGears( shop_def ) --finds positions of all gears and inverted gears
local w,h=shop_def.dim_x,shop_def.dim_y
local stage=shop_def.build_stages
local ret={}
for x=0,w-1 do
for y=0,h-1 do
local tile=shop_def.tile[stage][x][y]
if tile==42 then --gear icon
table.insert(ret,{x=x,y=y,invert=false})
elseif tile==15 then --inverted gear icon
table.insert(ret,{x=x,y=y,invert=true})
end
end
end
return ret
end
local function lookup_color( shop_def,x,y,stage )
return shop_def.tile_color[0][stage][x][y],shop_def.tile_color[1][stage][x][y],shop_def.tile_color[2][stage][x][y]
end
local function processFramesAuto( shop_def ,gears) --adds frames for all gear icons and inverted gear icons
local w,h=shop_def.dim_x,shop_def.dim_y
local frames={{},{}} --two frames only
local stage=shop_def.build_stages
for i,v in ipairs(gears) do
local tile,tile_inv
if v.inverted then
tile=42
tile_inv=15
else
tile=15
tile_inv=42
end
table.insert(frames[1],{x=v.x,y=v.y,tile,lookup_color(shop_def,v.x,v.y,stage)})
table.insert(frames[2],{x=v.x,y=v.y,tile_inv,lookup_color(shop_def,v.x,v.y,stage)})
end
for frame_id,frame in ipairs(frames) do
frames[frame_id]=generateFrame(frame,w,h)
end
return frames
end
function registerBuilding(args)
local shop_def=findCustomWorkshop(args.name)
local shop_id=shop_def.id
--misc
local fix_impassible
if args.fix_impassible then
fix_impassible=1
else
fix_impassible=0
end
local roomSubset=args.canBeRoomSubset or -1
--power
local consume=args.consume or 0
local produce=args.produce or 0
local needs_power=args.needs_power or 1
local gears=args.gears or {}
local action=args.action --could be nil
local auto_gears=args.auto_gears or false
local updateSkip=0
local action=args.action --could be nil
if action~=nil then
updateSkip=action[1]
registerUpdateAction(shop_id,action[2])
end
--animations and connections next:
local gears
local frameLength
local animate=args.animate
local frameLength=1
local frames
if animate~=nil then
frameLength=animate.frameLength
if animate.isMechanical then
frameLength=-1
if not auto_gears then
gears=args.gears or {}
frameLength=1
local frames
if animate~=nil then
frameLength=animate.frameLength
if animate.isMechanical then
frameLength=-1
end
frames=processFrames(shop_def,animate.frames)
end
frames=processFrames(shop_def,animate.frames)
else
frameLength=-1
if animate~=nil then
frameLength=animate.frameLength or frameLength
if animate.isMechanical then
frameLength=-1
end
end
gears=findGears(shop_def)
frames=processFramesAuto(shop_def,gears)
end
local roomSubset=args.canBeRoomSubset or -1
addBuilding(shop_id,fix_impassible,consume,produce,needs_power,gears,updateSkip,frames,frameLength,roomSubset)
end
return _ENV
return _ENV

@ -178,6 +178,7 @@ note_delete = defconf('note-delete')
function note_delete.intercept_key(key)
return key == keys.D_NOTE_DELETE and
ui.main.mode == df.ui_sidebar_mode.NotesPoints and
not ui.waypoints.in_edit_name_mode and
not ui.waypoints.in_edit_text_mode
end
note_delete.title = "Delete note"

@ -1,241 +0,0 @@
-- Stuff used by dfusion
local _ENV = mkmodule('plugins.dfusion')
local ms=require("memscan")
local marker={0xDE,0xAD,0xBE,0xEF}
--utility functions
function dwordToTable(dword)
local b={bit32.extract(dword,0,8),bit32.extract(dword,8,8),bit32.extract(dword,16,8),bit32.extract(dword,24,8)}
return b
end
function concatTables(t1,t2)
for k,v in pairs(t2) do
table.insert(t1,v)
end
end
function makeCall(from,to)
local ret={0xe8}
concatTables(ret,dwordToTable(to-from-5))
return ret
end
-- A reversable binary patch
patches={}
BinaryPatch=defclass(BinaryPatch)
BinaryPatch.ATTRS {pre_data=DEFAULT_NIL,data=DEFAULT_NIL,address=DEFAULT_NIL,name=DEFAULT_NIL}
function BinaryPatch:init(args)
self.is_applied=false
if args.pre_data==nil or args.data==nil or args.address==nil or args.name==nil then
error("Invalid parameters to binary patch")
end
if patches[self.name]~=nil then
error("Patch already exist")
end
for k,v in ipairs(args.data) do
if args.pre_data[k]==nil then
error("can't edit without revert data")
end
end
end
function BinaryPatch:postinit(args)
patches[args.name]=self
end
function BinaryPatch:test()
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+#self.pre_data)
for k,v in ipairs(self.pre_data) do
if arr[k-1]~=v then
return false
end
end
return true
end
function BinaryPatch:apply()
if not self:test() then
error(string.format("pre-data for binary patch does not match expected"))
end
local post_buf=df.new('uint8_t',#self.pre_data)
for k,v in ipairs(self.pre_data) do
if self.data[k]==nil then
post_buf[k-1]=v
else
post_buf[k-1]=self.data[k]
end
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,#self.pre_data)
if not ret then
error("Patch application failed!")
end
self.is_applied=true
end
function BinaryPatch:repatch(newdata)
if newdata==nil then newdata=self.data end
self:remove()
self.data=newdata
self:apply()
end
function BinaryPatch:remove(delete)
if delete==nil then
delete=true
end
if not self.is_applied then
error("can't remove BinaryPatch, not applied.")
end
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+#self.pre_data)
local post_buf=df.new('uint8_t',#self.pre_data)
for k,v in pairs(self.pre_data) do
post_buf[k-1]=v
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,#self.pre_data)
if not ret then
error("Patch remove failed!")
end
self.is_applied=false
if delete then
patches[self.name]=nil
end
end
function BinaryPatch:__gc()
if self.is_applied then
self:remove()
end
end
-- A binary hack (obj file) loader/manager
-- has to have: a way to get offsets for marked areas (for post load modification) or some way to do that pre-load
-- page managing (including excecute/write flags for DEP and the like)
-- TODO plugin state enum, a way to modify post install (could include repatching code...)
plugins=plugins or {}
BinaryPlugin=defclass(BinaryPlugin)
BinaryPlugin.ATTRS {filename=DEFAULT_NIL,reloc_table={},name=DEFAULT_NIL}
function BinaryPlugin:init(args)
end
function BinaryPlugin:postinit(args)
if self.name==nil then error("Not a valid plugin name!") end
if plugins[args.name]==nil then
plugins[self.name]=self
else
error("Trying to create a same plugin")
end
self.allocated_object={}
self:load()
end
function BinaryPlugin:get_or_alloc(name,typename,arrsize)
if self.allocated_object[name]~=nil then
return self.allocated_object[name]
else
return self:allocate(name,typename,arrsize)
end
end
function BinaryPlugin:allocate(name,typename,arrsize)
local trg
if df[typename]==nil then
trg=df.new(typename,arrsize)
self.allocated_object[name]=trg
else
trg=df[typename]:new(arrsize)
self.allocated_object[name]=trg
end
return trg
end
function BinaryPlugin:load()
local obj=loadObjectFile(self.filename)
self.data=df.reinterpret_cast("uint8_t",obj.data)
self.size=obj.data_size
for _,v in pairs(obj.symbols) do
if string.sub(v.name,1,5)=="mark_" then
local new_pos=self:find_marker(v.pos)
self.reloc_table[string.sub(v.name,6)]=new_pos
end
end
end
function BinaryPlugin:find_marker(start_pos)
local matched=0
for i=start_pos,self.size do
if self.data[i]==marker[4-matched] then
matched=matched+1
if matched == 4 then
return i-4
end
end
end
end
function BinaryPlugin:set_marker_dword(marker,dword) -- i hope Toady does not make a 64bit version...
if self.reloc_table[marker]==nil then
error("marker ".. marker.. " not found")
end
local b=dwordToTable(dword)
local off=self.reloc_table[marker]
for k,v in ipairs(b) do
self.data[off+k]=b[k]
end
end
function BinaryPlugin:move_to_df()
local _,addr=df.sizeof(self.data)
markAsExecutable(addr)
return addr
end
function BinaryPlugin:print_data()
local out=""
for i=0,self.size do
out=out..string.format(" %02x",self.data[i])
if math.modf(i,16)==15 then
print(out)
out=""
end
end
print(out)
end
function BinaryPlugin:status()
return "invalid, base class only!"
end
function BinaryPlugin:__gc()
for k,v in pairs(self.allocated_object) do
df.delete(v)
end
if self.unload then
self:unload()
end
self.data:delete()
end
-- a Menu for some stuff. Maybe add a posibility of it working as a gui, or a gui adaptor?
-- Todo add hints, and parse them to make a "smart" choice of parameters to pass
SimpleMenu=defclass(SimpleMenu)
SimpleMenu.ATTRS{title=DEFAULT_NIL}
function SimpleMenu:init(args)
self.items={}
end
function SimpleMenu:add(name,entry,hints)
table.insert(self.items,{entry,name,hints})
end
function SimpleMenu:display()
print("Select choice (q exits):")
for p,c in pairs(self.items) do
print(string.format("%3d).%s",p,c[2]))
end
local ans
repeat
local r
r=dfhack.lineedit()
if r==nil then return end
if r=='q' then return end
ans=tonumber(r)
if ans==nil or not(ans<=#self.items and ans>0) then
print("Invalid choice.")
end
until ans~=nil and (ans<=#self.items and ans>0)
if type(self.items[ans][1])=="function" then
self.items[ans][1]()
else
self.items[ans][1]:display()
end
end
return _ENV

@ -1,172 +0,0 @@
local _ENV = mkmodule('plugins.dfusion.adv_tools')
local dfu=require("plugins.dfusion")
local tools=require("plugins.dfusion.tools")
menu=dfu.SimpleMenu()
function Reincarnate(trg_unit,swap_soul) --only for adventurer i guess
if swap_soul==nil then
swap_soul=true
end
local adv=trg_unit or df.global.world.units.active[0]
if adv.flags1.dead==false then
qerror("You are not dead (yet)!")
end
local hist_fig=dfhack.units.getNemesis(adv).figure
if hist_fig==nil then
qerror("No historical figure for adventurer...")
end
local events=df.global.world.history.events
local trg_hist_fig
for i=#events-1,0,-1 do -- reverse search because almost always it will be last entry
if df.history_event_hist_figure_diedst:is_instance(events[i]) then
--print("is instance:"..i)
if events[i].victim_hf==hist_fig.id then
--print("Is same id:"..i)
trg_hist_fig=events[i].slayer_hf
if trg_hist_fig then
trg_hist_fig=df.historical_figure.find(trg_hist_fig)
end
break
end
end
end
if trg_hist_fig ==nil then
qerror("Slayer not found")
end
local trg_unit=trg_hist_fig.unit_id
if trg_unit==nil then
qerror("Unit id not found!")
end
local trg_unit_final=df.unit.find(trg_unit)
change_adv(trg_unit_final)
if swap_soul then --actually add a soul...
t_soul=adv.status.current_soul
adv.status.current_soul=df.NULL
adv.status.souls:resize(0)
trg_unit_final.status.current_soul=t_soul
trg_unit_final.status.souls:insert(#trg_unit_final.status.souls,t_soul)
end
end
menu:add("Reincarnate",Reincarnate,{{df.unit,"optional"}})-- bool, optional
function change_adv(unit,nemesis)
if nemesis==nil then
nemesis=true --default value is nemesis switch too.
end
if unit==nil then
unit=dfhack.gui.getSelectedUnit()--getCreatureAtPointer()
end
if unit==nil then
error("Invalid unit!")
end
local other=df.global.world.units.active
local unit_indx
for k,v in pairs(other) do
if v==unit then
unit_indx=k
break
end
end
if unit_indx==nil then
error("Unit not found in array?!") --should not happen
end
other[unit_indx]=other[0]
other[0]=unit
if nemesis then --basicly copied from advtools plugin...
local nem=dfhack.units.getNemesis(unit)
local other_nem=dfhack.units.getNemesis(other[unit_indx])
if other_nem then
other_nem.flags[0]=false
other_nem.flags[1]=true
end
if nem then
nem.flags[0]=true
nem.flags[2]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nem.id then
df.global.ui_advmode.player_id=k
end
end
else
qerror("Current unit does not have nemesis record, further working not guaranteed")
end
end
end
menu:add("Change adventurer",change_adv)
function log_pos()
local adv=df.global.world.units.active[0]
local wmap=df.global.world.map
local sub_pos={x=adv.pos.x,y=adv.pos.y,z=adv.pos.z}
local region_pos={x=wmap.region_x,y=wmap.region_y,z=wmap.region_z}
local pos={x=sub_pos.x+region_pos.x*48,y=sub_pos.y+region_pos.y*48,z=sub_pos.z+region_pos.z}
local state
if adv.flags1.dead then
state="dead"
else
state="live n kicking"
end
local message=string.format("%s %s at pos={%d,%d,%d} region={%d,%d,%d}",dfhack.TranslateName(adv.name),state,pos.x,pos.y,pos.z,region_pos.x,region_pos.y,region_pos.z)
print(message)
local path="deaths_"..df.global.world.cur_savegame.save_dir..".txt"
local f=io.open(path,"a")
f:write(message)
f:close()
end
menu:add("Log adventurers position",log_pos)
function addSite(x,y,rgn_max_x,rgn_min_x,rgn_max_y,rgn_min_y,civ_id,name,sitetype)
if x==nil or y==nil then
x=(df.global.world.map.region_x+1)/16
y=(df.global.world.map.region_y+1)/16
end
if name==nil then
name=dfhack.lineedit("Site name:")or "Hacked site"
end
if sitetype==nil then
sitetype=tonumber(dfhack.lineedit("Site type (numeric):")) or 7
end
rgn_max_x=rgn_max_x or (df.global.world.map.region_x+1)%16
rgn_max_y=rgn_max_y or (df.global.world.map.region_y+1)%16
rgn_min_y=rgn_min_y or rgn_max_y
rgn_min_x=rgn_min_x or rgn_max_x
print("Region:",rgn_max_x,rgn_min_x,rgn_max_y,rgn_min_y)
--[=[
<angavrilov> global = pos*16 + rgn
<angavrilov> BUT
<angavrilov> for cities global is usually 17x17, i.e. max size
<angavrilov> while rgn designates a small bit in the middle
<angavrilov> for stuff like forts that formula holds exactly
]=]--
local wd=df.global.world.world_data
local nsite=df.world_site:new()
nsite.name.first_name=name
nsite.name.has_name=true
nsite.pos:assign{x=x,y=y}
nsite.rgn_max_x=rgn_max_x
nsite.rgn_min_x=rgn_min_x
nsite.rgn_min_y=rgn_min_y
nsite.rgn_max_y=rgn_max_y
nsite.global_max_x=nsite.pos.x*16+nsite.rgn_max_x
nsite.global_min_x=nsite.pos.x*16+nsite.rgn_min_x
nsite.global_max_y=nsite.pos.y*16+nsite.rgn_max_y
nsite.global_min_y=nsite.pos.y*16+nsite.rgn_min_y
nsite.id=wd.next_site_id
nsite.civ_id=civ_id or -1
nsite.cur_owner_id=civ_id or -1
nsite.type=sitetype --lair = 7
nsite.flags:resize(23)
--nsite.flags[4]=true
--nsite.flags[5]=true
--nsite.flags[6]=true
nsite.index=#wd.sites+1
wd.sites:insert("#",nsite)
wd.next_site_id=wd.next_site_id+1
--might not be needed...
--[[local unk130=df.world_site_unk130:new()
unk130.index=#wd.site_unk130+1
wd.site_unk130:insert("#",unk130)
--wd.next_site_unk136_id=wd.next_site_unk136_id+1--]]
return nsite
end
menu:add("Create site at current location",addSite)
return _ENV

@ -1,124 +0,0 @@
local _ENV = mkmodule('plugins.dfusion.embark')
local dfu=require("plugins.dfusion")
local ms=require("memscan")
local MAX_RACES=100
CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin)
local myos=dfhack.getOSType()
if myos=="windows" then
CustomEmbark.ATTRS{filename="hack/lua/plugins/dfusion/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL}
function CustomEmbark:parseRaces(races)
if #races<7 then
error("caste and race count must be bigger than 6")
end
if #races>MAX_RACES then
error("caste and race count must be less then "..MAX_RACES)
end
local n_to_id=require("plugins.dfusion.tools").build_race_names()
local ids={}
for k,v in pairs(races) do
local race=v[1] or v
ids[k]={}
ids[k][1]=n_to_id[race]
if ids[k][1]==nil then qerror(race.." not found!") end
ids[k][2]=v[2] or -1
end
self.race_caste_data=ids
end
function CustomEmbark:install(race_caste_data)
local stoff=dfhack.internal.getAddress('start_dwarf_count')
if race_caste_data~=nil then
self:parseRaces(race_caste_data)
end
if stoff==nil then
error("address for start_dwarf_count not found!")
end
local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id"))
print(string.format("start=%08x",stoff))
local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id]
local tmp_table=dfu.dwordToTable(race_id_offset)
for k,v in ipairs(tmp_table) do
table.insert(needle,v)
end
local mem=ms.get_code_segment()
print(mem.uint8_t:addr2idx(stoff))
print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff)))
local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber
if trg_offset==nil then
error("address for race_load not found")
end
local call_data={0x90,0x90}
local _,data_offset=df.sizeof(self.data)
dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset))
self.call_patch=self.call_patch or dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"}
needle={0x83,0xc8,0xff} -- or eax, 0xFF
local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil)
if caste_offset==nil or caste_offset-stoff>1000 then
error("Caste change code not found or found too far!")
end
self.disable_castes=self.disable_castes or dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"}
self.disable_castes:apply()
self:setEmbarkParty(self.race_caste_data)
local caste_array=self:get_or_alloc("caste_array","uint16_t",MAX_RACES)
local race_array=self:get_or_alloc("race_array","uint16_t",MAX_RACES)
local race_array_off,caste_array_off
local _
_,race_array_off=df.sizeof(race_array)
_,caste_array_off=df.sizeof(caste_array)
self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess...
self:set_marker_dword("caste",race_array_off)
self:move_to_df()
self.call_patch:apply()
self.installed=true
end
function CustomEmbark:setEmbarkParty(racesAndCastes)
local stoff=dfhack.internal.getAddress('start_dwarf_count')
if self.dwarfcount== nil then
self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"}
self.dwarfcount:apply()
else
self.dwarfcount:repatch(dfu.dwordToTable(#self.race_caste_data))
end
local caste_array=self:get_or_alloc("caste_array","uint16_t",MAX_RACES)
local race_array=self:get_or_alloc("race_array","uint16_t",MAX_RACES)
for k,v in ipairs(self.race_caste_data) do
caste_array[k-1]=v[2] or -1
race_array[k-1]=v[1]
end
end
function CustomEmbark:status()
if self.installed then
return "valid, installed"
else
return "valid, not installed"
end
end
function CustomEmbark:uninstall()
if self.installed then
self.call_patch:remove()
self.disable_castes:remove()
self.dwarfcount:remove()
end
end
function CustomEmbark:unload()
self:uninstall()
if Embark~=nil then
Embark=nil
end
end
Embark=Embark or CustomEmbark()
else
CustomEmbark.status=function() return"invalid, os not supported" end
end
return _ENV

Binary file not shown.

@ -1,121 +0,0 @@
local _ENV = mkmodule('plugins.dfusion.friendship')
local dfu=require("plugins.dfusion")
local ms=require("memscan")
local MAX_RACES=100
local MAX_CODE_DIST=250
FriendshipRainbow=defclass(FriendshipRainbow,dfu.BinaryPlugin)
FriendshipRainbow.name="FriendshipRainbow"
-- os independant... I think...
FriendshipRainbow.ATTRS{filename="hack/lua/plugins/dfusion/friendship.o",name="FriendshipRainbow",race_data=DEFAULT_NIL}
FriendshipRainbow.class_status="valid, not installed"
function FriendshipRainbow:findall_needles(codesg,needle) -- todo move to memscan.lua
local cidx,caddr=codesg.uint8_t:find(needle)
local ret={}
while cidx~=nil do
table.insert(ret,{cidx,caddr})
cidx,caddr=codesg.uint8_t:find(needle,cidx+1)
end
return ret
end
function FriendshipRainbow:find_one(codesg,needle,crace)
dfu.concatTables(needle,dfu.dwordToTable(crace))
return self:findall_needles(codesg,needle)
end
function FriendshipRainbow:find_all()
local code=ms.get_code_segment()
local locations={}
local _,crace=df.sizeof(df.global.ui:_field("race_id"))
dfu.concatTables(locations,self:find_one(code,{0x66,0xa1},crace)) --mov ax,[ptr]
dfu.concatTables(locations,self:find_one(code,{0xa1},crace)) --mov ax,[ptr]
local registers=
{0x05, -- (e)ax
0x1d, --ebx
0x0d, --ecx
0x15, --edx
0x35, --esi
0x3d, --edi
--0x25, --esp not used?
--0x2d, --ebp not used?
}
for k,reg in ipairs(registers) do
dfu.concatTables(locations,self:find_one(code,{0x0f,0xbf,reg},crace)) --movsx reg,[ptr]
dfu.concatTables(locations,self:find_one(code,{0x66,0x8b,reg},crace)) --mov reg,[ptr]
end
return self:filter_locations(code,locations)
end
function FriendshipRainbow:filter_locations(codesg,locations)
local ret={}
local registers={0x80,0x83,0x81,0x82,0x86,0x87,
0x98,0x9b,0x99,0x9a,0x9e,0x9f,
0x88,0x8b,0x89,0x8a,0x8e,0x8f,
0x90,0x93,0x91,0x92,0x96,0x97,
0xb0,0xb3,0xb1,0xb2,0xb6,0xb7,
0xb8,0xbb,0xb9,0xba,0xbe,0xbf}
for _,entry in ipairs(locations) do
for _,r in ipairs(registers) do
local idx,addr=codesg.uint8_t:find({0x39,r,0x8c,0x00,0x00,0x00},
codesg.uint8_t:addr2idx(entry[2]),codesg.uint8_t:addr2idx(entry[2])+MAX_CODE_DIST)
if addr then
table.insert(ret,{addr,r})
break
end
idx,addr=codesg.uint8_t:find({0x3b,r,0x8c,0x00,0x00,0x00},
codesg.uint8_t:addr2idx(entry[2]),codesg.uint8_t:addr2idx(entry[2])+MAX_CODE_DIST)
if addr then
table.insert(ret,{addr,r})
break
end
end
end
return ret
end
function FriendshipRainbow:patchCalls(target)
local addrs=self:find_all()
local swaps={}
for k,adr in ipairs(addrs) do
local newval=dfu.makeCall(adr[1],target)
table.insert(newval,adr[2])
for t,val in ipairs(newval) do
swaps[adr[1]+t-1]=val
end
end
dfhack.internal.patchBytes(swaps)
end
function FriendshipRainbow:set_races(arr)
local n_to_id=require("plugins.dfusion.tools").build_race_names()
local ids={}
for k,v in ipairs(self.race_data) do -- to check if all races are valid.
ids[k]=n_to_id[v]
end
for k,v in ipairs(ids) do
arr[k-1]=ids[k]
end
end
function FriendshipRainbow:install(races)
self.race_data=races or self.race_data
if #self.race_data<1 then
error("race count must be bigger than 0")
end
if #self.race_data>MAX_RACES then
error("race count must be less then "..MAX_RACES)
end
local rarr=self:allocate("race_array",'uint16_t',MAX_RACES)
local _,rarr_offset=df.sizeof(rarr)
self:set_marker_dword("racepointer",rarr_offset)
self:set_races(rarr)
self:set_marker_dword("racecount",#self.race_data)
local safe_loc=self:allocate("safe_loc",'uint32_t',1)
local _1,safe_loc_offset=df.sizeof(safe_loc)
self:set_marker_dword("safeloc1",safe_loc_offset)
self:set_marker_dword("safeloc2",safe_loc_offset)
local addr=self:move_to_df()
self:patchCalls(addr)
self.installed=true
end
Friendship=Friendship or FriendshipRainbow()
return _ENV

Binary file not shown.

@ -1 +0,0 @@
as -anl --32 -o friendship.o friendship.asm

@ -1,7 +0,0 @@
.intel_syntax
mov eax , [esp+0x1C] # loop counter
mark_caste:
movsx ecx, word ptr[eax*2+0xdeadbeef]
mark_race:
movzx eax,word ptr [eax*2+0xDEADBEEF]
ret

@ -1,106 +0,0 @@
.intel_syntax
push eax
mov eax,[esp+0x04]
push ebx
pushfd
mov eax,[eax] # get a byte after the call this procedure to analyze what register holds cr ptr
jmptbl:
cmp al,0x81
jz regC
cmp al,0x82
jz regD
cmp al,0x83
jz regB
cmp al,0x85
jz regBP
cmp al,0x86
jz regESI
cmp al,0x87
jz regEDI
cmp al,0x88
jz regA
cmp al,0x8A
jz regD
cmp al,0x8B
jz regB
cmp al,0x8D
jz regBP
cmp al,0x8E
jz regESI
cmp al,0x8F
jz regEDI
cmp al,0x90
jz regA
cmp al,0x91
jz regC
cmp al,0x93
jz regB
cmp al,0x95
jz regBP
cmp al,0x96
jz regESI
cmp al,0x97
jz regEDI
jmp fail
regA:
mov eax, [esp+0x8]
mov eax, [eax+0x8c]
jmp compare
regC:
mov eax, [ecx+0x8c]
jmp compare
regB:
mov eax, [ebx+0x8c]
jmp compare
regD:
mov eax, [edx+0x8c]
jmp compare
regBP:
mov eax, [ebp+0x8c]
jmp compare
regESI:
mov eax, [esi+0x8c]
jmp compare
regEDI:
mov eax, [edi+0x8c]
#jmp compare
compare:
push ecx
mark_racepointer:
mov ebx,0xDEADBEEF #write a pointer to the list of allowed races
mark_racecount:
mov ecx,0xDEADBEEF #write a number of allowed races
loop1:
cmp word[ebx+ecx*2],ax
jz endok
dec ecx
cmp ecx ,-1
jnz loop1
pop ecx
popfd
jmp fail
endok:
pop ecx
popfd
cmp eax,eax
jmp endfinal
fail:
xor ebx,ebx
xor eax,eax
inc eax
cmp eax,ebx
endfinal:
pop ebx
pop eax
mark_safeloc1:
mov [0xDEADBEEF],eax #write a pointer to safe location (usually after this)
pop eax
pushfd
inc eax #skip one instruction
popfd
push eax
mark_safeloc2:
mov eax,[0xDEADBEEF] #write a pointer to safe location (same as above)
ret

@ -1,243 +0,0 @@
local _ENV = mkmodule('plugins.dfusion.tools')
local dfu=require("plugins.dfusion")
local ms=require "memscan"
menu=dfu.SimpleMenu()
RaceNames={}
function build_race_names()
if #RaceNames~=0 then
return RaceNames
else
for k,v in pairs(df.global.world.raws.creatures.all) do
RaceNames[v.creature_id]=k
end
dfhack.onStateChange.invalidate_races=function(change_id) --todo does this work?
if change_id==SC_WORLD_UNLOADED then
dfhack.onStateChange.invalidate_races=nil
RaceNames={}
end
end
return RaceNames
end
end
function setrace(name)
local RaceTable=build_race_names()
print("Your current race is:"..df.global.world.raws.creatures.all[df.global.ui.race_id].creature_id)
local id
if name == nil then
print("Type new race's token name in full caps (q to quit):")
repeat
local entry=dfhack.lineedit()
if entry=="q" then
return
end
id=RaceTable[entry]
until id~=nil
else
id=RaceTable[name]
if id==nil then
error("Name not found!")
end
end
df.global.ui.race_id=id
end
menu:add("Set current race",setrace)
function GiveSentience(names)
local RaceTable=build_race_names() --slow.If loaded don't load again
local id,ids
if names ==nil then
ids={}
print("Type race's token name in full caps to give sentience to:")
repeat
id=dfhack.lineedit()
id=RaceTable[entry]
if id~=nil then
table.insert(ids,id)
end
until id==nil
else
ids={}
for _,name in pairs(names) do
id=RaceTable[name]
table.insert(ids,id)
end
end
for _,id in pairs(ids) do
local races=df.global.world.raws.creatures.all
local castes=races[id].caste
print(string.format("Caste count:%i",#castes))
for i =0,#castes-1 do
print("Caste name:"..castes[i].caste_id.."...")
local flags=castes[i].flags
--print(string.format("%x",flagoffset))
if flags.CAN_SPEAK then
print("\tis sentient.")
else
print("\tnon sentient. Allocating IQ...")
flags.CAN_SPEAK=true
end
end
end
end
menu:add("Give Sentience",GiveSentience)
function MakeFollow(unit,trgunit)
if unit == nil then
unit=dfhack.gui.getSelectedUnit()
end
if unit== nil then
error("Invalid creature")
end
if trgunit==nil then
trgunit=df.global.world.units.active[0]
end
unit.relations.group_leader_id=trgunit.id
local u_nem=dfhack.units.getNemesis(unit)
local t_nem=dfhack.units.getNemesis(trgunit)
if u_nem then
u_nem.group_leader_id=t_nem.id
end
if t_nem and u_nem then
t_nem.companions:insert(#t_nem.companions,u_nem.id)
end
end
menu:add("Make creature follow",MakeFollow)
function project(unit,trg) --TODO add to menu?
if unit==nil then
unit=getCreatureAtPointer()
end
if unit==nil then
error("Failed to project unit. Unit not selected/valid")
end
-- todo: add projectile to world, point to unit, add flag to unit, add gen-ref to projectile.
local p=df.proj_unitst:new()
local startpos={x=unit.pos.x,y=unit.pos.y,z=unit.pos.z}
p.origin_pos=startpos
p.target_pos=trg
p.cur_pos=startpos
p.prev_pos=startpos
p.unit=unit
--- wtf stuff
p.unk14=100
p.unk16=-1
p.unk23=-1
p.fall_delay=5
p.fall_counter=5
p.collided=true
-- end wtf
local citem=df.global.world.proj_list
local maxid=1
local newlink=df.proj_list_link:new()
newlink.item=p
while citem.item~= nil do
if citem.item.id>maxid then maxid=citem.item.id end
if citem.next ~= nil then
citem=citem.next
else
break
end
end
p.id=maxid+1
newlink.prev=citem
citem.next=newlink
local proj_ref=df.general_ref_projectile:new()
proj_ref.projectile_id=p.id
unit.general_refs:insert(#unit.general_refs,proj_ref)
unit.flags1.projectile=true
end
function empregnate(unit)
if unit==nil then
unit=dfhack.gui.getSelectedUnit()
end
if unit==nil then
error("Failed to empregnate. Unit not selected/valid")
end
if unit.curse then
unit.curse.add_tags2.STERILE=false
end
local genes = unit.appearance.genes
if unit.relations.pregnancy_genes == nil then
print("creating preg ptr.")
if false then
print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_genes"))))
return
end
unit.relations.pregnancy_genes = { new = true, assign = genes }
end
local ngenes = unit.relations.pregnancy_genes
if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then
print("Array sizes incorrect, fixing.")
ngenes:assign(genes);
end
print("Setting preg timer.")
unit.relations.pregnancy_timer=10
unit.relations.pregnancy_caste=1
end
menu:add("Empregnate",empregnate)
function healunit(unit)
if unit==nil then
unit=dfhack.gui.getSelectedUnit()
end
if unit==nil then
error("Failed to Heal unit. Unit not selected/valid")
end
unit.body.wounds:resize(0) -- memory leak here :/
unit.body.blood_count=unit.body.blood_max
--set flags for standing and grasping...
unit.status2.limbs_stand_max=4
unit.status2.limbs_stand_count=4
unit.status2.limbs_grasp_max=4
unit.status2.limbs_grasp_count=4
--should also set temperatures, and flags for breath etc...
unit.flags1.dead=false
unit.flags2.calculated_bodyparts=false
unit.flags2.calculated_nerves=false
unit.flags2.circulatory_spray=false
unit.flags2.vision_good=true
unit.flags2.vision_damaged=false
unit.flags2.vision_missing=false
unit.counters.winded=0
unit.counters.unconscious=0
for k,v in pairs(unit.body.components) do
for kk,vv in pairs(v) do
if k == 'body_part_status' then v[kk].whole = 0 else v[kk] = 0 end
end
end
end
menu:add("Heal unit",healunit)
function powerup(unit,labor_rating,military_rating,skills)
if unit==nil then
unit=dfhack.gui.getSelectedUnit()
end
if unit==nil then
error("Failed to power up unit. Unit not selected/valid")
end
if unit.status.current_soul== nil then
error("Failed to power up unit. Unit has no soul")
end
local utils = require 'utils'
labor_rating = labor_rating or 15
military_rating = military_rating or 70
skill =skill or { 0,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,41,42,43,44,45,46,47,48,49,54,55,57,58,59,60,61,62,63,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,95,96,97,98,99,100,101,102,103,104,105,109,110,111,112,113,114,115 }
local military = { 38,39,41,42,43,44,45,46,54,99,100,101,102,103,104,105 }
for sk,sv in ipairs(skill) do
local new_rating = labor_rating
for _,v in ipairs(military) do
if v == sv then
local new_rating = military_rating
end
end
utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = sv, rating = new_rating, experience = (new_rating * 500) + (new_rating * (new_rating - 1)) * 50}, 'id')
end
end
menu:add("Power up",powerup)
return _ENV

@ -105,13 +105,15 @@ function registerSidebar(shop_name,callback)
dfhack.onStateChange.eventful=unregall
else
local function drawSidebar( wshop )
local valid_focus="dwarfmode/QueryBuilding/Some"
local valid_focus="dwarfmode/QueryBuilding"
local another_overlay="dfhack/lua/WorkshopOverlay"
if wshop:getMaxBuildStage()==wshop:getBuildStage() then
local sidebar=callback{workshop=wshop}
if string.sub(dfhack.gui.getCurFocus(),1,#valid_focus)==valid_focus then
sidebar:show()
else
sidebar:show(dfhack.gui.getCurViewscreen(true).parent)
if string.sub(dfhack.gui.getCurFocus(true),1,#another_overlay)==another_overlay then
dfhack.screen.dismiss(dfhack.gui.getCurViewscreen(true))
end
if string.sub(dfhack.gui.getCurFocus(true),1,#valid_focus)==valid_focus then
sidebar:show(dfhack.gui.getCurViewscreen(true))
end
end
end

@ -272,6 +272,8 @@ function collect_reactions()
materials.wood.adjective = "wooden"
materials.tooth.adjective = "ivory/tooth"
materials.leather.clothing_flag = "LEATHER"
materials.shell.short = true
materials.pearl.short = true
-- Collection and Entrapment
reaction_entry(result, job_types.CollectWebs)
@ -360,6 +362,7 @@ function collect_reactions()
local cloth_mats = {materials.cloth, materials.silk, materials.yarn, materials.leather}
for _, material in ipairs(cloth_mats) do
material_reactions(result, {{job_types.SewImage, "Sew", "Image"}}, material)
material.cloth = true
end
for _, spec in ipairs{materials.bone, materials.shell, materials.tooth, materials.horn, materials.pearl} do
@ -527,6 +530,14 @@ function collect_reactions()
{job_types.MakeFlask, "Forge", "Flask"},
{job_types.MakeChain, "Forge", "Chain"},
{job_types.MakeCrafts, "Make", "Crafts"},
{job_types.MakeFigurine, "Make", "Figurine"},
{job_types.MakeAmulet, "Make", "Amulet"},
{job_types.MakeScepter, "Make", "Scepter"},
{job_types.MakeCrown, "Make", "Crown"},
{job_types.MakeRing, "Make", "Ring"},
{job_types.MakeEarring, "Make", "Earring"},
{job_types.MakeBracelet, "Make", "Bracelet"},
{job_types.MakeGem, "Make Large", "Gem"},
}, mat_flags)
end
@ -719,6 +730,7 @@ function collect_reactions()
end
end
-- Crafts
for _, mat in ipairs{
materials.wood,
materials.rock,
@ -732,7 +744,27 @@ function collect_reactions()
materials.pearl,
materials.yarn,
} do
material_reactions(result, {{job_types.MakeCrafts, "Make", "Crafts"}}, mat)
material_reactions(result, {
{job_types.MakeCrafts, "Make", "Crafts"},
{job_types.MakeAmulet, "Make", "Amulet"},
{job_types.MakeBracelet, "Make", "Bracelet"},
{job_types.MakeEarring, "Make", "Earring"},
}, mat)
if not mat.cloth then
material_reactions(result, {
{job_types.MakeCrown, "Make", "Crown"},
{job_types.MakeFigurine, "Make", "Figurine"},
{job_types.MakeRing, "Make", "Ring"},
{job_types.MakeGem, "Make Large", "Gem"},
}, mat)
if not mat.short then
material_reactions(result, {
{job_types.MakeScepter, "Make", "Scepter"},
}, mat)
end
end
end
-- Siege engine parts

@ -256,10 +256,26 @@ const SkillColumn columns[] = {
{19, 6, profession::NONE, unit_labor::NONE, job_skill::POETRY, "Po"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::READING, "Rd"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::SPEAKING, "Sp"},
{20, 5, profession::NONE, unit_labor::NONE, job_skill::MILITARY_TACTICS, "MT"},
{20, 5, profession::NONE, unit_labor::NONE, job_skill::TRACKING, "Tr"},
{20, 5, profession::NONE, unit_labor::NONE, job_skill::MAGIC_NATURE, "Dr"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::DANCE, "Dn"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::MAKE_MUSIC, "MM"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::SING_MUSIC, "SM"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::PLAY_KEYBOARD_INSTRUMENT, "PK"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::PLAY_STRINGED_INSTRUMENT, "PS"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::PLAY_WIND_INSTRUMENT, "PW"},
{19, 6, profession::NONE, unit_labor::NONE, job_skill::PLAY_PERCUSSION_INSTRUMENT, "PP"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::CRITICAL_THINKING, "CT"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::LOGIC, "Lo"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::MATHEMATICS, "Ma"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::ASTRONOMY, "As"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::CHEMISTRY, "Ch"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::GEOGRAPHY, "Ge"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::OPTICS_ENGINEER, "OE"},
{20, 4, profession::NONE, unit_labor::NONE, job_skill::FLUID_ENGINEER, "FE"},
{21, 5, profession::NONE, unit_labor::NONE, job_skill::MILITARY_TACTICS, "MT"},
{21, 5, profession::NONE, unit_labor::NONE, job_skill::TRACKING, "Tr"},
{21, 5, profession::NONE, unit_labor::NONE, job_skill::MAGIC_NATURE, "Dr"},
};
struct UnitInfo
@ -992,7 +1008,7 @@ public:
}
void select_profession(size_t selected)
{
if (selected >= manager.templates.size())
if (selected > manager.templates.size())
return;
ProfessionTemplate prof = manager.templates[selected - 1];
@ -1127,15 +1143,21 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector<df::unit*> &src, int cur
cur->selected = false;
cur->active_index = active_idx[unit];
if (!Units::isOwnRace(unit))
if (!Units::isOwnCiv(unit))
cur->allowEdit = false;
if (!Units::isOwnCiv(unit))
if (!Units::isOwnGroup(unit))
cur->allowEdit = false;
if (unit->flags1.bits.dead)
cur->allowEdit = false;
if (unit->flags2.bits.visitor)
cur->allowEdit = false;
if (unit->flags3.bits.ghostly)
cur->allowEdit = false;
if (!ENUM_ATTR(profession, can_assign_labor, unit->profession))
cur->allowEdit = false;

@ -1 +0,0 @@
proto/*.pb.*

@ -1,43 +0,0 @@
PROJECT(mapexport)
# add *our* headers here.
SET(PROJECT_HDRS
)
SET(PROJECT_SRCS
mapexport.cpp
)
SET(PROJECT_PROTOS
${CMAKE_CURRENT_SOURCE_DIR}/proto/Tile.proto
${CMAKE_CURRENT_SOURCE_DIR}/proto/Plant.proto
${CMAKE_CURRENT_SOURCE_DIR}/proto/Block.proto
${CMAKE_CURRENT_SOURCE_DIR}/proto/Material.proto
${CMAKE_CURRENT_SOURCE_DIR}/proto/Map.proto
)
#Create new lists of what sources and headers protoc will output after we invoke it
STRING(REPLACE ".proto" ".pb.cc;" PROJECT_PROTO_SRCS ${PROJECT_PROTOS})
STRING(REPLACE ".proto" ".pb.h;" PROJECT_PROTO_HDRS ${PROJECT_PROTOS})
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_PROTO_HDRS} PROPERTIES GENERATED TRUE)
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_PROTO_SRCS} PROPERTIES GENERATED TRUE)
LIST(APPEND PROJECT_HDRS ${PROJECT_PROTO_HDRS})
LIST(APPEND PROJECT_SRCS ${PROJECT_PROTO_SRCS})
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS})
#Generate sources from our proto files and store them in the source tree
ADD_CUSTOM_COMMAND(
OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
COMMAND protoc-bin -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/ --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/ ${PROJECT_PROTOS}
DEPENDS protoc-bin ${PROJECT_PROTOS}
)
IF(WIN32)
DFHACK_PLUGIN(mapexport ${PROJECT_SRCS} ${PROJECT_HDRS} LINK_LIBRARIES protobuf-lite)
ELSE()
DFHACK_PLUGIN(mapexport ${PROJECT_SRCS} ${PROJECT_HDRS} LINK_LIBRARIES protobuf-lite)
ENDIF()

@ -1,325 +0,0 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/MapCache.h"
using namespace DFHack;
#include <fstream>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/gzip_stream.h>
using namespace google::protobuf::io;
#include "DataDefs.h"
#include "df/world.h"
#include "df/plant.h"
#include "modules/Constructions.h"
#include "proto/Map.pb.h"
#include "proto/Block.pb.h"
using namespace DFHack;
using df::global::world;
/*
mapexport
=========
Export the current loaded map as a file. This was used by visualizers for
DF 0.34.11, but is now basically obsolete.
*/
typedef std::vector<df::plant *> PlantList;
command_result mapexport (color_ostream &out, std::vector <std::string> & parameters);
DFHACK_PLUGIN("mapexport");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
commands.push_back(PluginCommand("mapexport", "Exports the current map to a file.", mapexport, true));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
static dfproto::Tile::TileMaterialType toProto(df::tiletype_material mat)
{
/*
* This is surely ugly, but casting enums without officially
* defined numerical values to protobuf enums is against the
* way protobufs are supposed to be used, because it defeats
* the backward compatible nature of the protocols.
*/
switch (mat)
{
#define CONVERT(name) case tiletype_material::name: return dfproto::Tile::name;
case tiletype_material::NONE:
CONVERT(AIR)
case tiletype_material::PLANT:
CONVERT(SOIL)
CONVERT(STONE)
CONVERT(FEATURE)
CONVERT(LAVA_STONE)
CONVERT(MINERAL)
CONVERT(FROZEN_LIQUID)
CONVERT(CONSTRUCTION)
CONVERT(GRASS_LIGHT)
CONVERT(GRASS_DARK)
CONVERT(GRASS_DRY)
CONVERT(GRASS_DEAD)
CONVERT(HFS)
CONVERT(CAMPFIRE)
CONVERT(FIRE)
CONVERT(ASHES)
case tiletype_material::MAGMA:
return dfproto::Tile::MAGMA_TYPE;
CONVERT(DRIFTWOOD)
CONVERT(POOL)
CONVERT(BROOK)
CONVERT(RIVER)
#undef CONVERT
}
return dfproto::Tile::AIR;
}
command_result mapexport (color_ostream &out, std::vector <std::string> & parameters)
{
bool showHidden = false;
int filenameParameter = 1;
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out.print("Exports the currently visible map to a file.\n"
"Usage: mapexport [options] <filename>\n"
"Example: mapexport all embark.dfmap\n"
"Options:\n"
" all - Export the entire map, not just what's revealed.\n"
);
return CR_OK;
}
if (parameters[i] == "all")
{
showHidden = true;
filenameParameter++;
}
}
CoreSuspender suspend;
uint32_t x_max=0, y_max=0, z_max=0;
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
if (parameters.size() < filenameParameter)
{
out.printerr("Please supply a filename.\n");
return CR_FAILURE;
}
std::string filename = parameters[filenameParameter-1];
if (filename.rfind(".dfmap") == std::string::npos) filename += ".dfmap";
out << "Writing to " << filename << "..." << std::endl;
std::ofstream output_file(filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
if (!output_file.is_open())
{
out.printerr("Couldn't open the output file.\n");
return CR_FAILURE;
}
ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&output_file);
GzipOutputStream *zip_output = new GzipOutputStream(raw_output);
CodedOutputStream *coded_output = new CodedOutputStream(zip_output);
coded_output->WriteLittleEndian32(0x50414DDF); //Write our file header
Maps::getSize(x_max, y_max, z_max);
MapExtras::MapCache map;
DFHack::Materials *mats = Core::getInstance().getMaterials();
out << "Writing map info..." << std::endl;
dfproto::Map protomap;
protomap.set_x_size(x_max);
protomap.set_y_size(y_max);
protomap.set_z_size(z_max);
out << "Writing material dictionary..." << std::endl;
for (size_t i = 0; i < world->raws.inorganics.size(); i++)
{
dfproto::Material *protomaterial = protomap.add_inorganic_material();
protomaterial->set_index(i);
protomaterial->set_name(world->raws.inorganics[i]->id);
}
for (size_t i = 0; i < world->raws.plants.all.size(); i++)
{
dfproto::Material *protomaterial = protomap.add_organic_material();
protomaterial->set_index(i);
protomaterial->set_name(world->raws.plants.all[i]->id);
}
std::map<df::coord,std::pair<uint32_t,uint16_t> > constructionMaterials;
if (Constructions::isValid())
{
for (uint32_t i = 0; i < Constructions::getCount(); i++)
{
df::construction *construction = Constructions::getConstruction(i);
constructionMaterials[construction->pos] = std::make_pair(construction->mat_index, construction->mat_type);
}
}
coded_output->WriteVarint32(protomap.ByteSize());
protomap.SerializeToCodedStream(coded_output);
DFHack::t_feature blockFeatureGlobal;
DFHack::t_feature blockFeatureLocal;
out.print("Writing map block information");
for(uint32_t z = 0; z < z_max; z++)
{
for(uint32_t b_y = 0; b_y < y_max; b_y++)
{
for(uint32_t b_x = 0; b_x < x_max; b_x++)
{
if (b_x == 0 && b_y == 0 && z % 10 == 0) out.print(".");
// Get the map block
df::coord2d blockCoord(b_x, b_y);
MapExtras::Block *b = map.BlockAt(DFHack::DFCoord(b_x, b_y, z));
if (!b || !b->is_valid())
{
continue;
}
dfproto::Block protoblock;
protoblock.set_x(b_x);
protoblock.set_y(b_y);
protoblock.set_z(z);
// Find features
b->GetGlobalFeature(&blockFeatureGlobal);
b->GetLocalFeature(&blockFeatureLocal);
int global_z = df::global::world->map.region_z + z;
// Iterate over all the tiles in the block
for(uint32_t y = 0; y < 16; y++)
{
for(uint32_t x = 0; x < 16; x++)
{
df::coord2d coord(x, y);
df::tile_designation des = b->DesignationAt(coord);
df::tile_occupancy occ = b->OccupancyAt(coord);
// Skip hidden tiles
if (!showHidden && des.bits.hidden)
{
continue;
}
dfproto::Tile *prototile = protoblock.add_tile();
prototile->set_x(x);
prototile->set_y(y);
// Check for liquid
if (des.bits.flow_size)
{
prototile->set_liquid_type((dfproto::Tile::LiquidType)des.bits.liquid_type);
prototile->set_flow_size(des.bits.flow_size);
}
df::tiletype type = b->tiletypeAt(coord);
prototile->set_type((dfproto::Tile::TileType)tileShape(type));
prototile->set_tile_material(toProto(tileMaterial(type)));
df::coord map_pos = df::coord(b_x*16+x,b_y*16+y,z);
switch (tileMaterial(type))
{
case tiletype_material::SOIL:
case tiletype_material::STONE:
prototile->set_material_type(0);
prototile->set_material_index(b->layerMaterialAt(coord));
break;
case tiletype_material::MINERAL:
prototile->set_material_type(0);
prototile->set_material_index(b->veinMaterialAt(coord));
break;
case tiletype_material::FEATURE:
if (blockFeatureLocal.type != -1 && des.bits.feature_local)
{
if (blockFeatureLocal.type == feature_type::deep_special_tube
&& blockFeatureLocal.main_material == 0) // stone
{
prototile->set_material_type(0);
prototile->set_material_index(blockFeatureLocal.sub_material);
}
if (blockFeatureGlobal.type != -1 && des.bits.feature_global
&& blockFeatureGlobal.type == feature_type::feature_underworld_from_layer
&& blockFeatureGlobal.main_material == 0) // stone
{
prototile->set_material_type(0);
prototile->set_material_index(blockFeatureGlobal.sub_material);
}
}
break;
case tiletype_material::CONSTRUCTION:
if (constructionMaterials.find(map_pos) != constructionMaterials.end())
{
prototile->set_material_index(constructionMaterials[map_pos].first);
prototile->set_material_type(constructionMaterials[map_pos].second);
}
break;
default:
break;
}
}
}
if (b->getRaw())
{
PlantList *plants = &b->getRaw()->plants;
for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++)
{
const df::plant & plant = *(*it);
df::coord2d loc(plant.pos.x, plant.pos.y);
loc = loc % 16;
if (showHidden || !b->DesignationAt(loc).bits.hidden)
{
dfproto::Plant *protoplant = protoblock.add_plant();
protoplant->set_x(loc.x);
protoplant->set_y(loc.y);
protoplant->set_is_shrub(plant.flags.bits.is_shrub);
protoplant->set_material(plant.material);
}
}
}
coded_output->WriteVarint32(protoblock.ByteSize());
protoblock.SerializeToCodedStream(coded_output);
} // block x
// Clean uneeded memory
map.trash();
} // block y
} // z
delete coded_output;
delete zip_output;
delete raw_output;
mats->Finish();
out.print("\nMap succesfully exported!\n");
return CR_OK;
}

@ -1,14 +0,0 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
import "Tile.proto";
import "Plant.proto";
message Block
{
required uint32 x = 1;
required uint32 y = 2;
required uint32 z = 3;
repeated Tile tile = 4;
repeated Plant plant = 5;
}

@ -1,13 +0,0 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
import "Material.proto";
message Map
{
required uint32 x_size = 1;
required uint32 y_size = 2;
required uint32 z_size = 3;
repeated Material inorganic_material = 4;
repeated Material organic_material = 5;
}

@ -1,8 +0,0 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
message Material
{
required uint32 index= 1;
required string name = 2;
}

@ -1,10 +0,0 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
message Plant
{
required uint32 x = 1;
required uint32 y = 2;
required bool is_shrub = 3;
optional uint32 material = 4;
}

@ -1,63 +0,0 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
message Tile
{
enum TileType
{
EMPTY = 0;
FLOOR = 1;
BOULDER = 2;
PEBBLES = 3;
WALL = 4;
FORTIFICATION = 5;
STAIR_UP = 6;
STAIR_DOWN = 7;
STAIR_UPDOWN = 8;
RAMP = 9;
RAMP_TOP = 10;
BROOK_BED = 11;
BROOK_TOP = 12;
TREE = 13;
SAPLING = 14;
SHRUB = 15;
ENDLESS_PIT = 16;
}
enum LiquidType
{
WATER = 0;
MAGMA = 1;
}
enum TileMaterialType
{
AIR = 0;
SOIL = 1;
STONE = 2;
FEATURE = 3;
LAVA_STONE = 4;
MINERAL = 5;
FROZEN_LIQUID = 6;
CONSTRUCTION = 7;
GRASS_LIGHT = 8;
GRASS_DARK = 9;
GRASS_DRY = 10;
GRASS_DEAD = 11;
HFS = 12;
CAMPFIRE = 13;
FIRE = 14;
ASHES = 15;
MAGMA_TYPE = 16;
DRIFTWOOD = 17;
POOL = 18;
BROOK = 19;
RIVER = 20;
}
required uint32 x = 1;
required uint32 y = 2;
required TileType type = 3;
optional TileMaterialType tile_material = 4;
optional uint32 material_index = 5;
optional uint32 material_type = 6;
optional LiquidType liquid_type = 7;
optional uint32 flow_size = 8;
}

@ -284,6 +284,8 @@ message ViewInfo
optional int32 cursor_pos_x = 6;
optional int32 cursor_pos_y = 7;
optional int32 cursor_pos_z = 8;
optional int32 follow_unit_id = 9 [default = -1];
optional int32 follow_item_id = 10 [default = -1];
}
message MapInfo
@ -360,6 +362,7 @@ message WorldMap
optional int32 center_z = 19;
optional int32 cur_year = 20;
optional int32 cur_year_tick = 21;
optional WorldPoles world_poles = 22;
}
message RegionMaps
@ -453,4 +456,29 @@ message PlantRaw
message PlantRawList
{
repeated PlantRaw plant_raws = 1;
}
message ScreenTile
{
optional uint32 character = 1;
optional uint32 foreground = 2;
optional uint32 background = 3;
}
message ScreenCapture
{
optional uint32 width = 1;
optional uint32 height = 2;
repeated ScreenTile tiles = 3;
}
message KeyboardEvent
{
optional uint32 type = 1;
optional uint32 which = 2;
optional uint32 state = 3;
optional uint32 scancode = 4;
optional uint32 sym = 5;
optional uint32 mod = 6;
optional uint32 unicode = 7;
}

Some files were not shown because too many files have changed in this diff Show More