Merge remote-tracking branch 'angavrilov/master'

develop
Kelly Martin 2012-11-16 13:15:55 -06:00
commit 2a0171a4ff
154 changed files with 7924 additions and 5458 deletions

File diff suppressed because it is too large Load Diff

@ -833,6 +833,14 @@ Job module
Prints info about the job item. Prints info about the job item.
* ``dfhack.job.getGeneralRef(job, type)``
Searches for a general_ref with the given type.
* ``dfhack.job.getSpecificRef(job, type)``
Searches for a specific_ref with the given type.
* ``dfhack.job.getHolder(job)`` * ``dfhack.job.getHolder(job)``
Returns the building holding the job. Returns the building holding the job.
@ -879,6 +887,14 @@ Units module
Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged.
* ``dfhack.units.getGeneralRef(unit, type)``
Searches for a general_ref with the given type.
* ``dfhack.units.getSpecificRef(unit, type)``
Searches for a specific_ref with the given type.
* ``dfhack.units.getContainer(unit)`` * ``dfhack.units.getContainer(unit)``
Returns the container (cage) item or *nil*. Returns the container (cage) item or *nil*.
@ -954,6 +970,10 @@ Units module
Computes the effective rating for the given skill, taking into account exhaustion, pain etc. Computes the effective rating for the given skill, taking into account exhaustion, pain etc.
* ``dfhack.units.getExperience(unit, skill[, total])``
Returns the experience value for the given skill. If ``total`` is true, adds experience implied by the current rating.
* ``dfhack.units.computeMovementSpeed(unit)`` * ``dfhack.units.computeMovementSpeed(unit)``
Computes number of frames * 100 it takes the unit to move in its current state of mind and body. Computes number of frames * 100 it takes the unit to move in its current state of mind and body.
@ -1198,6 +1218,14 @@ Burrows module
Buildings module Buildings module
---------------- ----------------
* ``dfhack.buildings.getGeneralRef(building, type)``
Searches for a general_ref with the given type.
* ``dfhack.buildings.getSpecificRef(building, type)``
Searches for a specific_ref with the given type.
* ``dfhack.buildings.setOwner(item,unit)`` * ``dfhack.buildings.setOwner(item,unit)``
Replaces the owner of the building. If unit is *nil*, removes ownership. Replaces the owner of the building. If unit is *nil*, removes ownership.
@ -1403,31 +1431,14 @@ Basic painting functions:
* ``dfhack.screen.paintTile(pen,x,y[,char,tile])`` * ``dfhack.screen.paintTile(pen,x,y[,char,tile])``
Paints a tile using given parameters. Pen is a table with following possible fields: Paints a tile using given parameters. See below for a description of pen.
``ch``
Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the ``char`` function parameter.
``fg``
Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).
``bg``
Background color for the ordinary tile. Defaults to COLOR_BLACK (0).
``bold``
Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits.
Otherwise should be *true/false*.
``tile``
Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.
``tile_color = true``
Specifies that the tile should be shaded with *fg/bg*.
``tile_fg, tile_bg``
If specified, overrides *tile_color* and supplies shading colors directly.
Returns *false* if coordinates out of bounds, or other error. Returns *false* if coordinates out of bounds, or other error.
* ``dfhack.screen.readTile(x,y)`` * ``dfhack.screen.readTile(x,y)``
Retrieves the contents of the specified tile from the screen buffers. Retrieves the contents of the specified tile from the screen buffers.
Returns a pen, or *nil* if invalid or TrueType. Returns a pen object, or *nil* if invalid or TrueType.
* ``dfhack.screen.paintString(pen,x,y,text)`` * ``dfhack.screen.paintString(pen,x,y,text)``
@ -1458,6 +1469,66 @@ Basic painting functions:
Requests repaint of the screen by setting a flag. Unlike other Requests repaint of the screen by setting a flag. Unlike other
functions in this section, this may be used at any time. functions in this section, this may be used at any time.
* ``dfhack.screen.getKeyDisplay(key)``
Returns the string that should be used to represent the given
logical keybinding on the screen in texts like "press Key to ...".
The "pen" argument used by functions above may be represented by
a table with the following possible fields:
``ch``
Provides the ordinary tile character, as either a 1-character string or a number.
Can be overridden with the ``char`` function parameter.
``fg``
Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).
``bg``
Background color for the ordinary tile. Defaults to COLOR_BLACK (0).
``bold``
Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits.
Otherwise should be *true/false*.
``tile``
Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.
``tile_color = true``
Specifies that the tile should be shaded with *fg/bg*.
``tile_fg, tile_bg``
If specified, overrides *tile_color* and supplies shading colors directly.
Alternatively, it may be a pre-parsed native object with the following API:
* ``dfhack.pen.make(base[,pen_or_fg,bg,bold])``
Creates a new pre-parsed pen by combining its arguments according to the
following rules:
1. The ``base`` argument may be a pen object, a pen table as specified above,
or a single color value. In the single value case, it is split into
``fg`` and ``bold`` properties, and others are initialized to 0.
This argument will be converted to a pre-parsed object and returned
if there are no other arguments.
2. If the ``pen_or_fg`` argument is specified as a table or object, it
completely replaces the base, and is returned instead of it.
3. Otherwise, the non-nil subset of the optional arguments is used
to update the ``fg``, ``bg`` and ``bold`` properties of the base.
If the ``bold`` flag is *nil*, but *pen_or_fg* is a number, ``bold``
is deduced from it like in the simple base case.
This function always returns a new pre-parsed pen, or *nil*.
* ``dfhack.pen.parse(base[,pen_or_fg,bg,bold])``
Exactly like the above function, but returns ``base`` or ``pen_or_fg``
directly if they are already a pre-parsed native object.
* ``pen.property``, ``pen.property = value``, ``pairs(pen)``
Pre-parsed pens support reading and setting their properties,
but don't behave exactly like a simple table would; for instance,
assigning to ``pen.tile_color`` also resets ``pen.tile_fg`` and
``pen.tile_bg`` to *nil*.
In order to actually be able to paint to the screen, it is necessary 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 to create and register a viewscreen (basically a modal dialog) with
the game. the game.
@ -1575,10 +1646,20 @@ and are only documented here for completeness:
Returns the pre-extracted vtable address ``name``, or *nil*. Returns the pre-extracted vtable address ``name``, or *nil*.
* ``dfhack.internal.getImageBase()``
Returns the mmap base of the executable.
* ``dfhack.internal.getRebaseDelta()`` * ``dfhack.internal.getRebaseDelta()``
Returns the ASLR rebase offset of the DF executable. Returns the ASLR rebase offset of the DF executable.
* ``dfhack.internal.adjustOffset(offset[,to_file])``
Returns the re-aligned offset, or *nil* if invalid.
If ``to_file`` is true, the offset is adjusted from memory to file.
This function returns the original value everywhere except windows.
* ``dfhack.internal.getMemRanges()`` * ``dfhack.internal.getMemRanges()``
Returns a sequence of tables describing virtual memory ranges of the process. Returns a sequence of tables describing virtual memory ranges of the process.
@ -1758,6 +1839,11 @@ environment by the mandatory init file dfhack.lua:
safecall, qerror, mkmodule, reload safecall, qerror, mkmodule, reload
* Miscellaneous constants
:NEWLINE, COMMA, PERIOD: evaluate to the relevant character strings.
:DEFAULT_NIL: is an unspecified unique token used by the class module below.
* ``printall(obj)`` * ``printall(obj)``
If the argument is a lua table or DF object reference, prints all fields. If the argument is a lua table or DF object reference, prints all fields.
@ -1868,12 +1954,29 @@ utils
as a guide to which values should be skipped as uninteresting. as a guide to which values should be skipped as uninteresting.
The ``force`` argument makes it always return a non-*nil* value. The ``force`` argument makes it always return a non-*nil* value.
* ``utils.parse_bitfield_int(value, type_ref)``
Given an int ``value``, and a bitfield type in the ``df`` tree,
it returns a lua table mapping the enabled bit keys to *true*,
unless value is 0, in which case it returns *nil*.
* ``utils.list_bitfield_flags(bitfield[, list])``
Adds all enabled bitfield keys to ``list`` or a newly-allocated
empty sequence, and returns it. The ``bitfield`` argument may
be *nil*.
* ``utils.sort_vector(vector,field,cmpfun)`` * ``utils.sort_vector(vector,field,cmpfun)``
Sorts a native vector or lua sequence using the comparator function. Sorts a native vector or lua sequence using the comparator function.
If ``field`` is not *nil*, applies the comparator to the field instead If ``field`` is not *nil*, applies the comparator to the field instead
of the whole object. of the whole object.
* ``utils.linear_index(vector,key[,field])``
Searches for ``key`` in the vector, and returns *index, found_value*,
or *nil* if none found.
* ``utils.binsearch(vector,key,field,cmpfun,min,max)`` * ``utils.binsearch(vector,key,field,cmpfun,min,max)``
Does a binary search in a native vector or lua sequence for Does a binary search in a native vector or lua sequence for
@ -1909,6 +2012,23 @@ utils
Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``. Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``.
* ``utils.call_with_string(obj,methodname,...)``
Allocates a temporary string object, calls ``obj:method(tmp,...)``, and
returns the value written into the temporary after deleting it.
* ``utils.getBuildingName(building)``
Returns the string description of the given building.
* ``utils.getBuildingCenter(building)``
Returns an x/y/z table pointing at the building center.
* ``utils.split_string(string, delimiter)``
Splits the string by the given delimiter, and returns a sequence of results.
* ``utils.prompt_yes_no(prompt, default)`` * ``utils.prompt_yes_no(prompt, default)``
Presents a yes/no prompt to the user. If ``default`` is not *nil*, Presents a yes/no prompt to the user. If ``default`` is not *nil*,
@ -1968,19 +2088,32 @@ Implements a trivial single-inheritance class system.
If the default value should be *nil*, use ``ATTRS { foo = DEFAULT_NIL }``. If the default value should be *nil*, use ``ATTRS { foo = DEFAULT_NIL }``.
Declaring an attribute is mostly the same as defining your ``init`` method like this::
function Class.init(args)
self.attr1 = args.attr1 or default1
self.attr2 = args.attr2 or default2
...
end
The main difference is that attributes are processed as a separate
initialization step, before any ``init`` methods are called. They
also make the directy relation between instance fields and constructor
arguments more explicit.
* ``new_obj = Class{ foo = arg, bar = arg, ... }`` * ``new_obj = Class{ foo = arg, bar = arg, ... }``
Calling the class as a function creates and initializes a new instance. Calling the class as a function creates and initializes a new instance.
Initialization happens in this order: Initialization happens in this order:
1. An empty instance table is created, and its metatable set. 1. An empty instance table is created, and its metatable set.
2. The ``preinit`` method is called via ``invoke_before`` (see below) 2. The ``preinit`` methods are called via ``invoke_before`` (see below)
with the table used as argument to the class. This method is intended with the table used as argument to the class. These methods are intended
for validating and tweaking that argument table. for validating and tweaking that argument table.
3. Declared ATTRS are initialized from the argument table or their default values. 3. Declared ATTRS are initialized from the argument table or their default values.
4. The ``init`` method is called via ``invoke_after`` with the argument table. 4. The ``init`` methods are called via ``invoke_after`` with the argument table.
This is the main constructor method. This is the main constructor method.
5. The ``postinit`` method is called via ``invoke_after`` with the argument table. 5. The ``postinit`` methods are called via ``invoke_after`` with the argument table.
Place code that should be called after the object is fully constructed here. Place code that should be called after the object is fully constructed here.
Predefined instance methods: Predefined instance methods:
@ -1995,6 +2128,14 @@ Predefined instance methods:
properly passing in self, and optionally a number of initial arguments too. properly passing in self, and optionally a number of initial arguments too.
The arguments given to the closure are appended to these. The arguments given to the closure are appended to these.
* ``instance:cb_getfield(field_name)``
Returns a closure that returns the specified field of the object when called.
* ``instance:cb_setfield(field_name)``
Returns a closure that sets the specified field to its argument when called.
* ``instance:invoke_before(method_name, args...)`` * ``instance:invoke_before(method_name, args...)``
Navigates the inheritance chain of the instance starting from the most specific Navigates the inheritance chain of the instance starting from the most specific
@ -2019,6 +2160,715 @@ Predefined instance methods:
To avoid confusion, these methods cannot be redefined. To avoid confusion, these methods cannot be redefined.
==================
In-game UI Library
==================
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
dialogs that integrate in the main game UI window.
These modules make extensive use of the ``class`` module, and define
things ranging from the basic ``Painter``, ``View`` and ``Screen``
classes, to fully functional predefined dialogs.
gui
===
This module defines the most important classes and functions for
implementing interfaces. This documents those of them that are
considered stable.
Misc
----
* ``USE_GRAPHICS``
Contains the value of ``dfhack.screen.inGraphicsMode()``, which cannot be
changed without restarting the game and thus is constant during the session.
* ``CLEAR_PEN``
The black pen used to clear the screen.
* ``simulateInput(screen, keys...)``
This function wraps an undocumented native function that passes a set of
keycodes to a screen, and is the official way to do that.
Every argument after the initial screen may be *nil*, a numeric keycode,
a string keycode, a sequence of numeric or string keycodes, or a mapping
of keycodes to *true* or *false*. For instance, it is possible to use the
table passed as argument to ``onInput``.
* ``mkdims_xy(x1,y1,x2,y2)``
Returns a table containing the arguments as fields, and also ``width`` and
``height`` that contains the rectangle dimensions.
* ``mkdims_wh(x1,y1,width,height)``
Returns the same kind of table as ``mkdims_xy``, only this time it computes
``x2`` and ``y2``.
* ``is_in_rect(rect,x,y)``
Checks if the given point is within a rectangle, represented by a table produced
by one of the ``mkdims`` functions.
* ``blink_visible(delay)``
Returns *true* or *false*, with the value switching to the opposite every ``delay``
msec. This is intended for rendering blinking interface objects.
* ``getKeyDisplay(keycode)``
Wraps ``dfhack.screen.getKeyDisplay`` in order to allow using strings for the keycode argument.
ViewRect class
--------------
This class represents an on-screen rectangle with an associated independent
clip area rectangle. It is the base of the ``Painter`` class, and is used by
``Views`` to track their client area.
* ``ViewRect{ rect = ..., clip_rect = ..., view_rect = ..., clip_view = ... }``
The constructor has the following arguments:
:rect: The ``mkdims`` rectangle in screen coordinates of the logical viewport.
Defaults to the whole screen.
:clip_rect: The clip rectangle in screen coordinates. Defaults to ``rect``.
:view_rect: A ViewRect object to copy from; overrides both ``rect`` and ``clip_rect``.
:clip_view: A ViewRect object to intersect the specified clip area with.
* ``rect:isDefunct()``
Returns *true* if the clip area is empty, i.e. no painting is possible.
* ``rect:inClipGlobalXY(x,y)``
Checks if these global coordinates are within the clip rectangle.
* ``rect:inClipLocalXY(x,y)``
Checks if these coordinates (specified relative to ``x1,y1``) are within the clip rectangle.
* ``rect:localXY(x,y)``
Converts a pair of global coordinates to local; returns *x_local,y_local*.
* ``rect:globalXY(x,y)``
Converts a pair of local coordinates to global; returns *x_global,y_global*.
* ``rect:viewport(x,y,w,h)`` or ``rect:viewport(subrect)``
Returns a ViewRect representing a sub-rectangle of the current one.
The arguments are specified in local coordinates; the ``subrect``
argument must be a ``mkdims`` table. The returned object consists of
the exact specified rectangle, and a clip area produced by intersecting
it with the clip area of the original object.
Painter class
-------------
The painting natives in ``dfhack.screen`` apply to the whole screen, are
completely stateless and don't implement clipping.
The Painter class inherits from ViewRect to provide clipping and local
coordinates, and tracks current cursor position and current pen.
* ``Painter{ ..., pen = ..., key_pen = ... }``
In addition to ViewRect arguments, Painter accepts a suggestion of
the initial value for the main pen, and the keybinding pen. They
default to COLOR_GREY and COLOR_LIGHTGREEN otherwise.
There are also some convenience functions that wrap this constructor:
- ``Painter.new(rect,pen)``
- ``Painter.new_view(view_rect,pen)``
- ``Painter.new_xy(x1,y1,x2,y2,pen)``
- ``Painter.new_wh(x1,y1,width,height,pen)``
* ``painter:isValidPos()``
Checks if the current cursor position is within the clip area.
* ``painter:viewport(x,y,w,h)``
Like the superclass method, but returns a Painter object.
* ``painter:cursor()``
Returns the current cursor *x,y* in local coordinates.
* ``painter:seek(x,y)``
Sets the current cursor position, and returns *self*.
Either of the arguments may be *nil* to keep the current value.
* ``painter:advance(dx,dy)``
Adds the given offsets to the cursor position, and returns *self*.
Either of the arguments may be *nil* to keep the current value.
* ``painter:newline([dx])``
Advances the cursor to the start of the next line plus the given x offset, and returns *self*.
* ``painter:pen(...)``
Sets the current pen to ``dfhack.pen.parse(old_pen,...)``, and returns *self*.
* ``painter:key_pen(...)``
Sets the current keybinding pen to ``dfhack.pen.parse(old_pen,...)``, and returns *self*.
* ``painter:clear()``
Fills the whole clip rectangle with ``CLEAR_PEN``, and returns *self*.
* ``painter:fill(x1,y1,x2,y2[,...])`` or ``painter:fill(rect[,...])``
Fills the specified local coordinate rectangle with ``dfhack.pen.parse(cur_pen,...)``,
and returns *self*.
* ``painter:char([char[, ...]])``
Paints one character using ``char`` and ``dfhack.pen.parse(cur_pen,...)``; returns *self*.
The ``char`` argument, if not nil, is used to override the ``ch`` property of the pen.
* ``painter:tile([char, tile[, ...]])``
Like above, but also allows overriding the ``tile`` property on ad-hoc basis.
* ``painter:string(text[, ...])``
Paints the string with ``dfhack.pen.parse(cur_pen,...)``; returns *self*.
* ``painter:key(keycode[, ...])``
Paints the description of the keycode using ``dfhack.pen.parse(cur_key_pen,...)``; returns *self*.
As noted above, all painting methods return *self*, in order to allow chaining them like this::
painter:pen(foo):seek(x,y):char(1):advance(1):string('bar')...
View class
----------
This class is the common abstract base of both the stand-alone screens
and common widgets to be used inside them. It defines the basic layout,
rendering and event handling framework.
The class defines the following attributes:
:visible: Specifies that the view should be painted.
:active: Specifies that the view should receive events, if also visible.
:view_id: Specifies an identifier to easily identify the view among subviews.
This is reserved for implementation of top-level views, and should
not be used by widgets for their internal subviews.
It also always has the following fields:
:subviews: Contains a table of all subviews. The sequence part of the
table is used for iteration. In addition, subviews are also
indexed under their *view_id*, if any; see ``addviews()`` below.
These fields are computed by the layout process:
:frame_parent_rect: The ViewRect represeting the client area of the parent view.
:frame_rect: The ``mkdims`` rect of the outer frame in parent-local coordinates.
:frame_body: The ViewRect representing the body part of the View's own frame.
The class has the following methods:
* ``view:addviews(list)``
Adds the views in the list to the ``subviews`` sequence. If any of the views
in the list have ``view_id`` attributes that don't conflict with existing keys
in ``subviews``, also stores them under the string keys. Finally, copies any
non-conflicting string keys from the ``subviews`` tables of the listed views.
Thus, doing something like this::
self:addviews{
Panel{
view_id = 'panel',
subviews = {
Label{ view_id = 'label' }
}
}
}
Would make the label accessible as both ``self.subviews.label`` and
``self.subviews.panel.subviews.label``.
* ``view:getWindowSize()``
Returns the dimensions of the ``frame_body`` rectangle.
* ``view:getMousePos()``
Returns the mouse *x,y* in coordinates local to the ``frame_body``
rectangle if it is within its clip area, or nothing otherwise.
* ``view:updateLayout([parent_rect])``
Recomputes layout of the view and its subviews. If no argument is
given, re-uses the previous parent rect. The process goes as follows:
1. Calls ``preUpdateLayout(parent_rect)`` via ``invoke_before``.
2. Uses ``computeFrame(parent_rect)`` to compute the desired frame.
3. Calls ``postComputeFrame(frame_body)`` via ``invoke_after``.
4. Calls ``updateSubviewLayout(frame_body)`` to update children.
5. Calls ``postUpdateLayout(frame_body)`` via ``invoke_after``.
* ``view:computeFrame(parent_rect)`` *(for overriding)*
Called by ``updateLayout`` in order to compute the frame rectangle(s).
Should return the ``mkdims`` rectangle for the outer frame, and optionally
also for the body frame. If only one rectangle is returned, it is used
for both frames, and the margin becomes zero.
* ``view:updateSubviewLayout(frame_body)``
Calls ``updateLayout`` on all children.
* ``view:render(painter)``
Given the parent's painter, renders the view via the following process:
1. Calls ``onRenderFrame(painter, frame_rect)`` to paint the outer frame.
2. Creates a new painter using the ``frame_body`` rect.
3. Calls ``onRenderBody(new_painter)`` to paint the client area.
4. Calls ``renderSubviews(new_painter)`` to paint visible children.
* ``view:renderSubviews(painter)``
Calls ``render`` on all ``visible`` subviews in the order they
appear in the ``subviews`` sequence.
* ``view:onRenderFrame(painter, rect)`` *(for overriding)*
Called by ``render`` to paint the outer frame; by default does nothing.
* ``view:onRenderBody(painter)`` *(for overriding)*
Called by ``render`` to paint the client area; by default does nothing.
* ``view:onInput(keys)`` *(for overriding)*
Override this to handle events. By default directly calls ``inputToSubviews``.
Return a true value from this method to signal that the event has been handled
and should not be passed on to more views.
* ``view:inputToSubviews(keys)``
Calls ``onInput`` on all visible active subviews, iterating the ``subviews``
sequence in *reverse order*, so that topmost subviews get events first.
Returns *true* if any of the subviews handled the event.
Screen class
------------
This is a View subclass intended for use as a stand-alone dialog or screen.
It adds the following methods:
* ``screen:isShown()``
Returns *true* if the screen is currently in the game engine's display stack.
* ``screen:isDismissed()``
Returns *true* if the screen is dismissed.
* ``screen:isActive()``
Returns *true* if the screen is shown and not dismissed.
* ``screen:invalidate()``
Requests a repaint. Note that currently using it is not necessary, because
repaints are constantly requested automatically, due to issues with native
screens happening otherwise.
* ``screen:renderParent()``
Asks the parent native screen to render itself, or clears the screen if impossible.
* ``screen:sendInputToParent(...)``
Uses ``simulateInput`` to send keypresses to the native parent screen.
* ``screen:show([parent])``
Adds the screen to the display stack with the given screen as the parent;
if parent is not specified, places this one one topmost. Before calling
``dfhack.screen.show``, calls ``self:onAboutToShow(parent)``.
* ``screen:onAboutToShow(parent)`` *(for overriding)*
Called when ``dfhack.screen.show`` is about to be called.
* ``screen:onShow()``
Called by ``dfhack.screen.show`` once the screen is successfully shown.
* ``screen:dismiss()``
Dismisses the screen. A dismissed screen does not receive any more
events or paint requests, but may remain in the display stack for
a short time until the game removes it.
* ``screen:onDismiss()`` *(for overriding)*
Called by ``dfhack.screen.dismiss()``.
* ``screen:onDestroy()`` *(for overriding)*
Called by the native code when the screen is fully destroyed and removed
from the display stack. Place code that absolutely must be called whenever
the screen is removed by any means here.
* ``screen:onResize``, ``screen:onRender``
Defined as callbacks for native code.
FramedScreen class
------------------
A Screen subclass that paints a visible frame around its body.
Most dialogs should inherit from this class.
A framed screen has the following attributes:
:frame_style: A table that defines a set of pens to draw various parts of the frame.
:frame_title: A string to display in the middle of the top of the frame.
:frame_width: Desired width of the client area. If *nil*, the screen will occupy the whole width.
:frame_height: Likewise, for height.
:frame_inset: The gap between the frame and the client area. Defaults to 0.
:frame_background: The pen to fill in the frame with. Defaults to CLEAR_PEN.
There are the following predefined frame style tables:
* ``GREY_FRAME``
A plain grey-colored frame.
* ``BOUNDARY_FRAME``
The same frame as used by the usual full-screen DF views, like dwarfmode.
* ``GREY_LINE_FRAME``
A frame consisting of grey lines, similar to the one used by titan announcements.
gui.widgets
===========
This module implements some basic widgets based on the View infrastructure.
Widget class
------------
Base of all the widgets. Inherits from View and has the following attributes:
* ``frame = {...}``
Specifies the constraints on the outer frame of the widget.
If omitted, the widget will occupy the whole parent rectangle.
The frame is specified as a table with the following possible fields:
:l: gap between the left edges of the frame and the parent.
:t: gap between the top edges of the frame and the parent.
:r: gap between the right edges of the frame and the parent.
:b: gap between the bottom edges of the frame and the parent.
:w: maximum width of the frame.
:h: maximum heigth of the frame.
:xalign: X alignment of the frame.
:yalign: Y alignment of the frame.
First the ``l,t,r,b`` fields restrict the available area for
placing the frame. If ``w`` and ``h`` are not specified or
larger then the computed area, it becomes the frame. Otherwise
the smaller frame is placed within the are based on the
``xalign/yalign`` fields. If the align hints are omitted, they
are assumed to be 0, 1, or 0.5 based on which of the ``l/r/t/b``
fields are set.
* ``frame_inset = {...}``
Specifies the gap between the outer frame, and the client area.
The attribute may be a simple integer value to specify a uniform
inset, or a table with the following fields:
:l: left margin.
:t: top margin.
:r: right margin.
:b: bottom margin.
:x: left/right margin, if ``l`` and/or ``r`` are omitted.
:y: top/bottom margin, if ``t`` and/or ``b`` are omitted.
* ``frame_background = pen``
The pen to fill the outer frame with. Defaults to no fill.
Panel class
-----------
Inherits from Widget, and intended for grouping a number of subviews.
Has attributes:
* ``subviews = {}``
Used to initialize the subview list in the constructor.
* ``on_render = function(painter)``
Called from ``onRenderBody``.
Pages class
-----------
Subclass of Panel; keeps exactly one child visible.
* ``Pages{ ..., selected = ... }``
Specifies which child to select initially; defaults to the first one.
* ``pages:getSelected()``
Returns the selected *index, child*.
* ``pages:setSelected(index)``
Selects the specified child, hiding the previous selected one.
It is permitted to use the subview object, or its ``view_id`` as index.
EditField class
---------------
Subclass of Widget; implements a simple edit field.
Attributes:
:text: The current contents of the field.
:text_pen: The pen to draw the text with.
:on_char: Input validation callback; used as ``on_char(new_char,text)``.
If it returns false, the character is ignored.
:on_change: Change notification callback; used as ``on_change(new_text,old_text)``.
:on_submit: Enter key callback; if set the field will handle the key and call ``on_submit(text)``.
Label class
-----------
This Widget subclass implements flowing semi-static text.
It has the following attributes:
:text_pen: Specifies the pen for active text.
:text_dpen: Specifies the pen for disabled text.
:disabled: Boolean or a callback; if true, the label is disabled.
:enabled: Boolean or a callback; if false, the label is disabled.
:auto_height: Sets self.frame.h from the text height.
:auto_width: Sets self.frame.w from the text width.
The text itself is represented as a complex structure, and passed
to the object via the ``text`` argument of the constructor, or via
the ``setText`` method, as one of:
* A simple string, possibly containing newlines.
* A sequence of tokens.
Every token in the sequence in turn may be either a string, possibly
containing newlines, or a table with the following possible fields:
* ``token.text = ...``
Specifies the main text content of a token, and may be a string, or
a callback returning a string.
* ``token.gap = ...``
Specifies the number of character positions to advance on the line
before rendering the token.
* ``token.tile = pen``
Specifies a pen to paint as one tile before the main part of the token.
* ``token.key = '...'``
Specifies the keycode associated with the token. The string description
of the key binding is added to the text content of the token.
* ``token.key_sep = '...'``
Specifies the separator to place between the keybinding label produced
by ``token.key``, and the main text of the token. If the separator is
'()', the token is formatted as ``text..' ('..binding..')'``. Otherwise
it is simply ``binding..sep..text``.
* ``token.enabled``, ``token.disabled``
Same as the attributes of the label itself, but applies only to the token.
* ``token.pen``, ``token.dpen``
Specify the pen and disabled pen to be used for the token's text.
The field may be either the pen itself, or a callback that returns it.
* ``token.on_activate``
If this field is not nil, and ``token.key`` is set, the token will actually
respond to that key binding unless disabled, and call this callback. Eventually
this may be extended with mouse click support.
* ``token.id``
Specifies a unique identifier for the token.
* ``token.line``, ``token.x1``, ``token.x2``
Reserved for internal use.
The Label widget implements the following methods:
* ``label:setText(new_text)``
Replaces the text currently contained in the widget.
* ``label:itemById(id)``
Finds a token by its ``id`` field.
* ``label:getTextHeight()``
Computes the height of the text.
* ``label:getTextWidth()``
Computes the width of the text.
List class
----------
The List widget implements a simple list with paging.
It has the following attributes:
:text_pen: Specifies the pen for deselected list entries.
:cursor_pen: Specifies the pen for the selected entry.
:inactive_pen: If specified, used for the cursor when the widget is not active.
:icon_pen: Default pen for icons.
:on_select: Selection change callback; called as ``on_select(index,choice)``.
:on_submit: Enter key callback; if specified, the list reacts to the key
and calls it as ``on_submit(index,choice)``.
:row_height: Height of every row in text lines.
:icon_width: If not *nil*, the specified number of character columns
are reserved to the left of the list item for the icons.
:scroll_keys: Specifies which keys the list should react to as a table.
Every list item may be specified either as a string, or as a lua table
with the following fields:
:text: Specifies the label text in the same format as the Label text.
:caption, [1]: Deprecated legacy aliases for **text**.
:text_*: Reserved for internal use.
:key: Specifies a keybinding that acts as a shortcut for the specified item.
:icon: Specifies an icon string, or a pen to paint a single character. May be a callback.
:icon_pen: When the icon is a string, used to paint it.
The list supports the following methods:
* ``List{ ..., choices = ..., selected = ... }``
Same as calling ``setChoices`` after construction.
* ``list:setChoices(choices[, selected])``
Replaces the list of choices, possibly also setting the currently selected index.
* ``list:setSelected(selected)``
Sets the currently selected index. Returns the index after validation.
* ``list:getChoices()``
Returns the list of choices.
* ``list:getSelected()``
Returns the selected *index, choice*, or nothing if the list is empty.
* ``list:getContentWidth()``
Returns the minimal width to draw all choices without clipping.
* ``list:getContentHeight()``
Returns the minimal width to draw all choices without scrolling.
* ``list:submit()``
Call the ``on_submit`` callback, as if the Enter key was handled.
FilteredList class
------------------
This widget combines List, EditField and Label into a combo-box like
construction that allows filtering the list by subwords of its items.
In addition to passing through all attributes supported by List, it
supports:
:edit_pen: If specified, used instead of ``cursor_pen`` for the edit field.
:not_found_label: Specifies the text of the label shown when no items match the filter.
The list choices may include the following attributes:
:search_key: If specified, used instead of **text** to match against the filter.
The widget implements:
* ``list:setChoices(choices[, selected])``
Resets the filter, and passes through to the inner list.
* ``list:getChoices()``
Returns the list of *all* choices.
* ``list:getFilter()``
Returns the current filter string, and the *filtered* list of choices.
* ``list:setFilter(filter[,pos])``
Sets the new filter string, filters the list, and selects the item at
index ``pos`` in the *unfiltered* list if possible.
* ``list:canSubmit()``
Checks if there are currently any choices in the filtered list.
* ``list:getSelected()``, ``list:getContentWidth()``, ``list:getContentHeight()``, ``list:submit()``
Same as with an ordinary list.
======= =======
Plugins Plugins

16
NEWS

@ -10,19 +10,35 @@ DFHack future
- fastdwarf: new mode using debug flags, and some internal consistency fixes. - fastdwarf: new mode using debug flags, and some internal consistency fixes.
- added a small stand-alone utility for applying and removing binary patches. - added a small stand-alone utility for applying and removing binary patches.
- removebadthoughts: add --dry-run option - removebadthoughts: add --dry-run option
New tweaks:
- tweak military-training: speed up melee squad training up to 10x (depends on unit count).
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.
- dfusion: misc scripts with a text based menu.
- embark: lets you embark anywhere.
New GUI scripts: New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders. - 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/workshop-job: displays inputs of a workshop job and allows tweaking them.
- gui/workflow: a front-end for the workflow plugin. - gui/workflow: a front-end for the workflow plugin.
- gui/assign-rack: works together with a binary patch to fix weapon racks. - 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.
Workflow plugin: Workflow plugin:
- properly considers minecarts assigned to routes busy. - properly considers minecarts assigned to routes busy.
- code for deducing job outputs rewritten in lua for flexibility. - code for deducing job outputs rewritten in lua for flexibility.
- logic fix: collecting webs produces silk, and ungathered webs are not thread. - 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.
New Fix Armory plugin: New Fix Armory plugin:
Together with a couple of binary patches and the gui/assign-rack script, Together with a couple of binary patches and the gui/assign-rack script,
this plugin makes weapon racks, armor stands, chests and cabinets in this plugin makes weapon racks, armor stands, chests and cabinets in
properly designated barracks be used again for storage of squad equipment. properly designated barracks be used again for storage of squad equipment.
New Search plugin by falconne:
Adds an incremental search function to the Stocks, Trading and Unit List screens.
Dfusion plugin:
Reworked to make use of lua modules, now all the scripts can be used from other scripts.
DFHack v0.34.11-r2 DFHack v0.34.11-r2

File diff suppressed because it is too large Load Diff

@ -58,9 +58,35 @@ The stonesense plugin might require some additional libraries on Linux.
If any of the plugins or dfhack itself refuses to load, check the stderr.log If any of the plugins or dfhack itself refuses to load, check the stderr.log
file created in your DF folder. file created in your DF folder.
Getting started
===============
If DFHack is installed correctly, it will automatically pop up a console
window once DF is started as usual on windows. Linux and Mac OS X require
running the dfhack script from the terminal, and will use that terminal for
the console.
**NOTE**: The dfhack-run executable is there for calling DFHack commands in
an already running DF+DFHack instance from external OS scripts and programs,
and is *not* the way how you use DFHack normally.
DFHack has a lot of features, which can be accessed by typing commands in the
console, or by mapping them to keyboard shortcuts. Most of the newer and more
user-friendly tools are designed to be at least partially used via the latter
way.
In order to set keybindings, you have to create a text configuration file
called ``dfhack.init``; the installation comes with an example version called
``dfhack.init-example``, which is fully functional, covers all of the recent
features and can be simply renamed to ``dfhack.init``. You are encouraged to look
through it to learn which features it makes available under which key combinations.
For more information, refer to the rest of this document.
============ ============
Using DFHack Using DFHack
============ ============
DFHack basically extends what DF can do with something similar to the drop-down DFHack basically extends what DF can do with something similar to the drop-down
console found in Quake engine games. On Windows, this is a separate command line console found in Quake engine games. On Windows, this is a separate command line
window. On linux, the terminal used to launch the dfhack script is taken over window. On linux, the terminal used to launch the dfhack script is taken over
@ -1077,7 +1103,7 @@ fix-armory
Enables a fix for storage of squad equipment in barracks. Enables a fix for storage of squad equipment in barracks.
Specifically, it prevents your haulers from moving that equipment Specifically, it prevents your haulers from moving squad equipment
to stockpiles, and instead queues jobs to store it on weapon racks, to stockpiles, and instead queues jobs to store it on weapon racks,
armor stands, and in containers. armor stands, and in containers.
@ -1087,9 +1113,10 @@ armor stands, and in containers.
manually assigned to a squad. See documentation for ``gui/assign-rack`` manually assigned to a squad. See documentation for ``gui/assign-rack``
below. below.
Also, the default capacity of armor stands is way too low, so check out Also, the default capacity of armor stands is way too low, so you
may want to also apply the ``armorstand-capacity`` patch. Check out
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
for a patch addressing that too. for more information about the bugs.
Note that the buildings in the armory are used as follows: Note that the buildings in the armory are used as follows:
@ -1258,10 +1285,18 @@ Usage:
List workflow-controlled jobs (if in a workshop, filtered by it). List workflow-controlled jobs (if in a workshop, filtered by it).
``workflow list`` ``workflow list``
List active constraints, and their job counts. List active constraints, and their job counts.
``workflow count <constraint-spec> <cnt-limit> [cnt-gap], workflow amount <constraint-spec> <cnt-limit> [cnt-gap]`` ``workflow list-commands``
Set a constraint. The first form counts each stack as only 1 item. List active constraints as workflow commands that re-create them;
this list can be copied to a file, and then reloaded using the
``script`` built-in command.
``workflow count <constraint-spec> <cnt-limit> [cnt-gap]``
Set a constraint, counting every stack as 1 item.
``workflow amount <constraint-spec> <cnt-limit> [cnt-gap]``
Set a constraint, counting all items within stacks.
``workflow unlimit <constraint-spec>`` ``workflow unlimit <constraint-spec>``
Delete a constraint. Delete a constraint.
``workflow unlimit-all``
Delete all constraints.
Function Function
........ ........
@ -1279,6 +1314,34 @@ the frequency of jobs being toggled.
Check out the ``gui/workflow`` script below for a simple front-end integrated Check out the ``gui/workflow`` script below for a simple front-end integrated
in the game UI. in the game UI.
Constraint format
.................
The contstraint spec consists of 4 parts, separated with '/' characters::
ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,<quality>]
The first part is mandatory and specifies the item type and subtype,
using the raw tokens for items, in the same syntax you would e.g. use
for a custom reaction input. See this list for more info: http://dwarffortresswiki.org/index.php/Item_token
The subsequent parts are optional:
- A generic material spec constrains the item material to one of
the hard-coded generic classes, which currently include::
PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN
METAL STONE SAND GLASS CLAY MILK
- A specific material spec chooses the material exactly, using the
raw syntax for reaction input materials, e.g. INORGANIC:IRON,
although for convenience it also allows just IRON, or ACACIA:WOOD etc.
See this page for more details on the unabbreviated raw syntax:
http://dwarffortresswiki.org/index.php/Material_token
- A comma-separated list of miscellaneous flags, which currently can
be used to ignore imported items or items below a certain quality.
Constraint examples Constraint examples
................... ...................
@ -1304,10 +1367,15 @@ Make sure there are always 25-30 empty bins/barrels/bags.
Make sure there are always 15-20 coal and 25-30 copper bars. Make sure there are always 15-20 coal and 25-30 copper bars.
:: ::
workflow count BAR//COAL 20 workflow count BAR//COAL 20
workflow count BAR//COPPER 30 workflow count BAR//COPPER 30
Produce 15-20 gold crafts.
::
workflow count CRAFTS//GOLD 20
Collect 15-20 sand bags and clay boulders. Collect 15-20 sand bags and clay boulders.
:: ::
@ -1319,9 +1387,16 @@ Make sure there are always 80-100 units of dimple dye.
workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
.. note::
In order for this to work, you have to set the material of the PLANT input In order for this to work, you have to set the material of the PLANT input
on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material' on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material'
command. command. Otherwise the plugin won't be able to deduce the output material.
Maintain 10-100 locally-made crafts of exceptional quality.
::
workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
Fortress activity management Fortress activity management
@ -1611,19 +1686,17 @@ twice.
dfusion dfusion
------- -------
This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin. This is the DFusion lua plugin system by Warmist, running as a DFHack plugin. There are two parts to this plugin: an interactive script that shows a text based menu and lua modules. Some of the functionality of is intentionaly left out of the menu:
:Friendship: a binary plugin that allows multi race forts (to use make a script that imports plugins.dfusion.friendship and use Friendship:install{table} table should contain list of race names.)
See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15 :Embark: a binary plugin that allows multi race embark (to use make a script that imports plugins.dfusion.embark and use Embark:install{table} table should contain list of race names or list of pairs (race-name, caste_id)).
Confirmed working DFusion plugins: See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=93317.0
:simple_embark: allows changing the number of dwarves available on embark.
.. note:: .. note::
* Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
* This is currently working only on Windows. * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond.
* The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
misery misery
------ ------
@ -1687,6 +1760,17 @@ gui/*
Scripts that implement dialogs inserted into the main game window are put in this Scripts that implement dialogs inserted into the main game window are put in this
directory. directory.
binpatch
========
Checks, applies or removes binary patches 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
the version appropriate for the currently loaded executable.
quicksave quicksave
========= =========
@ -1836,6 +1920,31 @@ deathcause
Focus a body part ingame, and this script will display the cause of death of Focus a body part ingame, and this script will display the cause of death of
the creature. the creature.
lua
===
There are the following ways to invoke this command:
1. ``lua`` (without any parameters)
This starts an interactive lua interpreter.
2. ``lua -f "filename"`` or ``lua --file "filename"``
This loads and runs the file indicated by filename.
3. ``lua -s ["filename"]`` or ``lua --save ["filename"]``
This loads and runs the file indicated by filename from the save
directory. If the filename is not supplied, it loads "dfhack.lua".
4. ``:lua`` *lua statement...*
Parses and executes the lua statement like the interactive interpreter would.
embark
======
Allows to embark anywhere. Currently windows only.
======================= =======================
In-game interface tools In-game interface tools
@ -1898,6 +2007,31 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen. directly to the main dwarf mode screen.
Search
======
The search plugin adds search to the Stocks, Trading and Unit List screens.
Searching works the same way as the search option in "Move to Depot" does.
You will see the Search option displayed on screen with a hotkey (usually 's').
Pressing it lets you start typing a query and the relevant list will start
filtering automatically.
Pressing ENTER, ESC or the arrow keys will return you to browsing the now
filtered list, which still functions as normal. You can clear the filter
by either going back into search mode and backspacing to delete it, or
pressing the "shifted" version of the search hotkey while browsing the
list (e.g. if the hotkey is 's', then hitting 'shift-s' will clear any
filter).
Leaving any screen automatically clears the filter.
In the Trade screen, the actual trade will always only act on items that
are actually visible in the list; the same effect applies to the Trade
Value numbers displayed by the screen. Because of this, pressing the 't'
key while search is active clears the search instead of executing the trade.
gui/liquids gui/liquids
=========== ===========
@ -2047,7 +2181,9 @@ work again. The existing issues are:
the game does this. This issue is what this script addresses. the game does this. This issue is what this script addresses.
* Even if assigned by the script, **the game will unassign the racks again without a binary patch**. * Even if assigned by the script, **the game will unassign the racks again without a binary patch**.
Check the comments for this bug to get it: This patch is called ``weaponrack-unassign``, and can be applied via
the binpatch program, or the matching script. See this for more info
about the bug:
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
* Haulers still take equpment stored in the armory away to the stockpiles, * Haulers still take equpment stored in the armory away to the stockpiles,

@ -2,23 +2,34 @@
# Generic dwarfmode bindings # # Generic dwarfmode bindings #
############################## ##############################
# toggle the display of water level as 1-7 tiles
keybinding add Ctrl-W twaterlvl keybinding add Ctrl-W twaterlvl
# with cursor: # with cursor:
# designate the whole vein for digging
keybinding add Ctrl-V digv keybinding add Ctrl-V digv
keybinding add Ctrl-Shift-V "digv x" keybinding add Ctrl-Shift-V "digv x"
# clean the selected tile of blood etc
keybinding add Ctrl-C spotclean keybinding add Ctrl-C spotclean
# destroy items designated for dump in the selected tile
keybinding add Ctrl-Shift-K autodump-destroy-here keybinding add Ctrl-Shift-K autodump-destroy-here
# any item: # with an item selected:
# destroy the selected item
keybinding add Ctrl-K autodump-destroy-item keybinding add Ctrl-K autodump-destroy-item
# scripts:
# quicksave, only in main dwarfmode screen and menu page # quicksave, only in main dwarfmode screen and menu page
keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave
# gui/rename script # gui/rename script - rename units and buildings
keybinding add Ctrl-Shift-N gui/rename keybinding add Ctrl-Shift-N gui/rename
keybinding add Alt-Shift-P "gui/rename unit-profession" keybinding add Ctrl-Shift-T "gui/rename unit-profession"
############################## ##############################
# Generic adv mode bindings # # Generic adv mode bindings #
@ -31,10 +42,10 @@ keybinding add Ctrl-Shift-B "adv-bodyswap force"
# Context-specific bindings # # Context-specific bindings #
############################# #############################
# q->stockpile; p # q->stockpile; p - copy & paste stockpiles
keybinding add Alt-P copystock keybinding add Alt-P copystock
# q->workshop # q->workshop - duplicate the selected job
keybinding add Ctrl-D job-duplicate keybinding add Ctrl-D job-duplicate
# materials: q->workshop; b->select items # materials: q->workshop; b->select items
@ -48,7 +59,7 @@ keybinding add Shift-O "job-material OBSIDIAN"
keybinding add Shift-T "job-material ORTHOCLASE" keybinding add Shift-T "job-material ORTHOCLASE"
keybinding add Shift-G "job-material GLASS_GREEN" keybinding add Shift-G "job-material GLASS_GREEN"
# sort units and items # sort units and items in the on-screen list
keybinding add Alt-Shift-N "sort-units name" "sort-items description" keybinding add Alt-Shift-N "sort-units name" "sort-items description"
keybinding add Alt-Shift-R "sort-units arrival" keybinding add Alt-Shift-R "sort-units arrival"
keybinding add Alt-Shift-T "sort-units profession" "sort-items type material" keybinding add Alt-Shift-T "sort-units profession" "sort-items type material"
@ -60,7 +71,7 @@ keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
# browse rooms of same owner # browse rooms of same owner
keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list
# interface for the liquids plugin # interface for the liquids plugin - spawn water/magma/obsidian
keybinding add Alt-L@dwarfmode/LookAround gui/liquids keybinding add Alt-L@dwarfmode/LookAround gui/liquids
# machine power sensitive pressure plate construction # machine power sensitive pressure plate construction
@ -121,3 +132,29 @@ tweak fast-trade
tweak military-stable-assign tweak military-stable-assign
# in same list, color units already assigned to squads in brown & green # in same list, color units already assigned to squads in brown & green
tweak military-color-assigned tweak military-color-assigned
# remove inverse dependency of squad training speed on unit list size and use more sparring
tweak military-training
#######################################################
# Apply binary patches at runtime #
# #
# Commented out by default; enable the ones you want. #
#######################################################
# Bug 5994 - items teleported when removing a construction
#binpatch apply deconstruct-teleport
#binpatch apply deconstruct-heapfall
# Bug 4406 - hospital overstocking on all items
#binpatch apply hospital-overstocking
# Bug 808 - custom reactions completely using up all of their reagents
#binpatch apply custom-reagent-size
# Bug 4530 - marksdwarves not training when quiver full of combat-only ammo
#binpatch apply training-ammo
# Bug 1445 - weapon racks broken, armor stand capacity too low
#binpatch apply weaponrack-unassign
#binpatch apply armorstand-capacity

@ -346,6 +346,10 @@ install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts
PATTERN "*.rb" PATTERN "*.rb"
) )
install(DIRECTORY ${dfhack_SOURCE_DIR}/patches
DESTINATION ${DFHACK_DATA_DESTINATION}
FILES_MATCHING PATTERN "*.dif")
# Unused for so long that it's not even relevant now... # Unused for so long that it's not even relevant now...
if(BUILD_DEVEL) if(BUILD_DEVEL)
if(WIN32) if(WIN32)

@ -725,6 +725,316 @@ static void OpenMatinfo(lua_State *state)
lua_pop(state, 1); lua_pop(state, 1);
} }
/**************
* Pen object *
**************/
static int DFHACK_PEN_TOKEN = 0;
void Lua::Push(lua_State *L, const Screen::Pen &info)
{
if (!info.valid())
{
lua_pushnil(L);
return;
}
void *pdata = lua_newuserdata(L, sizeof(Pen));
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
lua_setmetatable(L, -2);
new (pdata) Pen(info);
}
static Pen *check_pen_native(lua_State *L, int index)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2))
luaL_argerror(L, index, "not a pen object");
lua_pop(L, 2);
return (Pen*)lua_touserdata(L, index);
}
void Lua::CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil, bool allow_color)
{
index = lua_absindex(L, index);
luaL_checkany(L, index);
if (lua_isnil(L, index))
{
if (!allow_nil)
luaL_argerror(L, index, "nil pen not allowed");
*pen = Pen(0,0,0,-1);
}
else if (lua_isuserdata(L, index))
{
*pen = *check_pen_native(L, index);
}
else if (allow_color && lua_isnumber(L, index))
{
*pen = Pen(0, lua_tointeger(L, index)&15, 0);
}
else
{
luaL_checktype(L, index, LUA_TTABLE);
decode_pen(L, *pen, index);
}
}
static int adjust_pen(lua_State *L, bool no_copy)
{
lua_settop(L, 4);
Pen pen;
int iidx = 1;
Lua::CheckPen(L, &pen, 1, true, true);
if (!lua_isnil(L, 2) || !lua_isnil(L, 3) || !lua_isnil(L, 4))
{
if (lua_isnumber(L, 2) || lua_isnil(L, 2))
{
if (!pen.valid())
pen = Pen();
iidx = -1;
pen.fg = luaL_optint(L, 2, pen.fg) & 15;
pen.bg = luaL_optint(L, 3, pen.bg);
if (!lua_isnil(L, 4))
pen.bold = lua_toboolean(L, 4);
else if (!lua_isnil(L, 2))
{
pen.bold = !!(pen.fg & 8);
pen.fg &= 7;
}
}
else
{
iidx = 2;
Lua::CheckPen(L, &pen, 2, false, false);
}
}
if (no_copy && iidx > 0 && lua_isuserdata(L, iidx))
lua_pushvalue(L, iidx);
else
Lua::Push(L, pen);
return 1;
}
static int dfhack_pen_parse(lua_State *L)
{
return adjust_pen(L, true);
}
static int dfhack_pen_make(lua_State *L)
{
return adjust_pen(L, false);
}
static void make_pen_table(lua_State *L, Pen &pen)
{
if (!pen.valid())
luaL_error(L, "invalid pen state");
else
{
lua_newtable(L);
lua_pushinteger(L, (unsigned char)pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
}
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
lua_pushboolean(L, false); lua_setfield(L, -2, "tile_color");
break;
}
}
}
static void get_pen_mirror(lua_State *L, int idx)
{
lua_getuservalue(L, idx);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
Pen pen;
Lua::CheckPen(L, &pen, idx, false, false);
make_pen_table(L, pen);
lua_dup(L);
lua_setuservalue(L, idx);
}
}
static int dfhack_pen_index(lua_State *L)
{
lua_settop(L, 2);
luaL_checktype(L, 1, LUA_TUSERDATA);
// check metatable
if (!lua_getmetatable(L, 1))
luaL_argerror(L, 1, "must be a pen");
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1))
return 1;
// otherwise read from the mirror table, creating it if necessary
lua_settop(L, 2);
get_pen_mirror(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int pen_pnext(lua_State *L)
{
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, lua_upvalueindex(1)))
return 2;
lua_pushnil(L);
return 1;
}
static int dfhack_pen_pairs(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
get_pen_mirror(L, 1);
lua_pushcclosure(L, pen_pnext, 1);
lua_pushnil(L);
lua_pushnil(L);
return 3;
}
const char *const pen_fields[] = {
"ch", "fg", "bold", "bg", "tile", "tile_color", "tile_fg", "tile_bg", NULL
};
static int dfhack_pen_newindex(lua_State *L)
{
lua_settop(L, 3);
luaL_checktype(L, 1, LUA_TUSERDATA);
int id = luaL_checkoption(L, 2, NULL, pen_fields);
int arg = 0;
Pen &pen = *check_pen_native(L, 1);
bool wipe_tile = false, wipe_tc = false;
switch (id) {
case 0:
if (lua_type(L, 3) != LUA_TNUMBER)
arg = (unsigned char)*luaL_checkstring(L, 3);
else
arg = luaL_checkint(L, 3);
pen.ch = arg;
lua_pushinteger(L, (unsigned char)pen.ch);
break;
case 1:
pen.fg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.fg);
break;
case 2:
pen.bold = lua_toboolean(L, 3);
lua_pushboolean(L, pen.bold);
break;
case 3:
pen.bg = luaL_checkint(L, 3) & 15;
lua_pushinteger(L, pen.bg);
break;
case 4:
arg = lua_isnil(L, 3) ? 0 : luaL_checkint(L, 3);
if (arg < 0)
luaL_argerror(L, 3, "invalid tile index");
pen.tile = arg;
if (pen.tile)
lua_pushinteger(L, pen.tile);
else
lua_pushnil(L);
break;
case 5:
wipe_tile = (pen.tile_mode == Pen::TileColor);
pen.tile_mode = lua_toboolean(L, 3) ? Pen::CharColor : Pen::AsIs;
lua_pushboolean(L, pen.tile_mode == Pen::CharColor);
break;
case 6:
if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_bg = 0; }
pen.tile_fg = luaL_checkint(L, 3) & 15;
pen.tile_mode = Pen::TileColor;
lua_pushinteger(L, pen.tile_fg);
break;
case 7:
if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_fg = 7; }
pen.tile_bg = luaL_checkint(L, 3) & 15;
pen.tile_mode = Pen::TileColor;
lua_pushinteger(L, pen.tile_bg);
break;
}
lua_getuservalue(L, 1);
if (!lua_isnil(L, -1))
{
lua_remove(L, 3);
lua_insert(L, 2);
lua_rawset(L, 2);
if (wipe_tc) {
lua_pushnil(L); lua_setfield(L, 2, "tile_color");
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, 2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, 2, "tile_bg");
}
if (wipe_tile) {
lua_pushnil(L); lua_setfield(L, 2, "tile_fg");
lua_pushnil(L); lua_setfield(L, 2, "tile_bg");
}
}
return 0;
}
static const luaL_Reg dfhack_pen_funcs[] = {
{ "parse", dfhack_pen_parse },
{ "make", dfhack_pen_make },
{ "__index", dfhack_pen_index },
{ "__pairs", dfhack_pen_pairs },
{ "__newindex", dfhack_pen_newindex },
{ NULL, NULL }
};
static void OpenPen(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "pen");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
luaL_setfuncs(state, dfhack_pen_funcs, 0);
lua_pop(state, 1);
}
/************************ /************************
* Wrappers for C++ API * * Wrappers for C++ API *
************************/ ************************/
@ -811,6 +1121,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,cloneJobStruct), WRAPM(Job,cloneJobStruct),
WRAPM(Job,printItemDetails), WRAPM(Job,printItemDetails),
WRAPM(Job,printJobDetails), WRAPM(Job,printJobDetails),
WRAPM(Job,getGeneralRef),
WRAPM(Job,getSpecificRef),
WRAPM(Job,getHolder), WRAPM(Job,getHolder),
WRAPM(Job,getWorker), WRAPM(Job,getWorker),
WRAPM(Job,checkBuildingsNow), WRAPM(Job,checkBuildingsNow),
@ -847,6 +1159,8 @@ static const luaL_Reg dfhack_job_funcs[] = {
/***** Units module *****/ /***** Units module *****/
static const LuaWrapper::FunctionReg dfhack_units_module[] = { static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getGeneralRef),
WRAPM(Units, getSpecificRef),
WRAPM(Units, getContainer), WRAPM(Units, getContainer),
WRAPM(Units, setNickname), WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName), WRAPM(Units, getVisibleName),
@ -866,6 +1180,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, getAge), WRAPM(Units, getAge),
WRAPM(Units, getNominalSkill), WRAPM(Units, getNominalSkill),
WRAPM(Units, getEffectiveSkill), WRAPM(Units, getEffectiveSkill),
WRAPM(Units, getExperience),
WRAPM(Units, computeMovementSpeed), WRAPM(Units, computeMovementSpeed),
WRAPM(Units, getProfessionName), WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName), WRAPM(Units, getCasteProfessionName),
@ -1117,6 +1432,8 @@ static bool buildings_containsTile(df::building *bld, int x, int y, bool room) {
} }
static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, getGeneralRef),
WRAPM(Buildings, getSpecificRef),
WRAPM(Buildings, setOwner), WRAPM(Buildings, setOwner),
WRAPM(Buildings, allocInstance), WRAPM(Buildings, allocInstance),
WRAPM(Buildings, checkFreeTiles), WRAPM(Buildings, checkFreeTiles),
@ -1251,7 +1568,7 @@ static int screen_getWindowSize(lua_State *L)
static int screen_paintTile(lua_State *L) static int screen_paintTile(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
if (lua_gettop(L) >= 4 && !lua_isnil(L, 4)) if (lua_gettop(L) >= 4 && !lua_isnil(L, 4))
@ -1272,44 +1589,14 @@ static int screen_readTile(lua_State *L)
int x = luaL_checkint(L, 1); int x = luaL_checkint(L, 1);
int y = luaL_checkint(L, 2); int y = luaL_checkint(L, 2);
Pen pen = Screen::readTile(x, y); Pen pen = Screen::readTile(x, y);
Lua::Push(L, pen);
if (!pen.valid())
{
lua_pushnil(L);
}
else
{
lua_newtable(L);
lua_pushinteger(L, pen.ch); lua_setfield(L, -2, "ch");
lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg");
lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg");
lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold");
if (pen.tile)
{
lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile");
switch (pen.tile_mode) {
case Pen::CharColor:
lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color");
break;
case Pen::TileColor:
lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg");
lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg");
break;
default:
break;
}
}
}
return 1; return 1;
} }
static int screen_paintString(lua_State *L) static int screen_paintString(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x = luaL_checkint(L, 2); int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3); int y = luaL_checkint(L, 3);
const char *text = luaL_checkstring(L, 4); const char *text = luaL_checkstring(L, 4);
@ -1320,7 +1607,7 @@ static int screen_paintString(lua_State *L)
static int screen_fillRect(lua_State *L) static int screen_fillRect(lua_State *L)
{ {
Pen pen; Pen pen;
decode_pen(L, pen, 1); Lua::CheckPen(L, &pen, 1);
int x1 = luaL_checkint(L, 2); int x1 = luaL_checkint(L, 2);
int y1 = luaL_checkint(L, 3); int y1 = luaL_checkint(L, 3);
int x2 = luaL_checkint(L, 4); int x2 = luaL_checkint(L, 4);
@ -1438,9 +1725,11 @@ static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
return rv; return rv;
} }
static uint32_t getImageBase() { return Core::getInstance().p->getBase(); }
static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); } static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); }
static const LuaWrapper::FunctionReg dfhack_internal_module[] = { static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAP(getImageBase),
WRAP(getRebaseDelta), WRAP(getRebaseDelta),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1494,6 +1783,18 @@ static int internal_getVTable(lua_State *L)
return 1; return 1;
} }
static int internal_adjustOffset(lua_State *L)
{
lua_settop(L, 2);
int off = luaL_checkint(L, 1);
int rv = Core::getInstance().p->adjustOffset(off, lua_toboolean(L, 2));
if (rv >= 0)
lua_pushinteger(L, rv);
else
lua_pushnil(L);
return 1;
}
static int internal_getMemRanges(lua_State *L) static int internal_getMemRanges(lua_State *L)
{ {
std::vector<DFHack::t_memrange> ranges; std::vector<DFHack::t_memrange> ranges;
@ -1701,6 +2002,7 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "getAddress", internal_getAddress }, { "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress }, { "setAddress", internal_setAddress },
{ "getVTable", internal_getVTable }, { "getVTable", internal_getVTable },
{ "adjustOffset", internal_adjustOffset },
{ "getMemRanges", internal_getMemRanges }, { "getMemRanges", internal_getMemRanges },
{ "patchMemory", internal_patchMemory }, { "patchMemory", internal_patchMemory },
{ "patchBytes", internal_patchBytes }, { "patchBytes", internal_patchBytes },
@ -1720,6 +2022,7 @@ void OpenDFHackApi(lua_State *state)
{ {
OpenPersistent(state); OpenPersistent(state);
OpenMatinfo(state); OpenMatinfo(state);
OpenPen(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module); LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "gui", dfhack_gui_module);

@ -220,9 +220,14 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
}*/ }*/
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
return 0; return 0x1000000;
}
int Process::adjustOffset(int offset, bool /*to_file*/)
{
return offset;
} }
static int getdir (string dir, vector<string> &files) static int getdir (string dir, vector<string> &files)
@ -299,4 +304,29 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect); result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect);
return result==0; return result==0;
} }
// returns -1 on error
void* Process::memAlloc(const int length)
{
return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
}
int Process::memDealloc(const void *ptr, const int length)
{
return munmap(ptr, length);
}
int Process::memProtect(const void *ptr, const int length, const int prot)
{
int prot_native = 0;
if (prot & Process::MemProt::READ)
prot_native |= PROT_READ;
if (prot & Process::MemProt::WRITE)
prot_native |= PROT_WRITE;
if (prot & Process::MemProt::EXEC)
prot_native |= PROT_EXEC;
return mprotect(ptr, length, prot_native);
}

@ -155,9 +155,14 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
fclose(mapFile); fclose(mapFile);
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
return 0; return 0x8048000;
}
int Process::adjustOffset(int offset, bool /*to_file*/)
{
return offset;
} }
static int getdir (string dir, vector<string> &files) static int getdir (string dir, vector<string> &files)
@ -230,4 +235,29 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect); result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect);
return result==0; return result==0;
} }
// returns -1 on error
void* Process::memAlloc(const int length)
{
return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
}
int Process::memDealloc(void *ptr, const int length)
{
return munmap(ptr, length);
}
int Process::memProtect(void *ptr, const int length, const int prot)
{
int prot_native = 0;
if (prot & Process::MemProt::READ)
prot_native |= PROT_READ;
if (prot & Process::MemProt::WRITE)
prot_native |= PROT_WRITE;
if (prot & Process::MemProt::EXEC)
prot_native |= PROT_EXEC;
return mprotect(ptr, length, prot_native);
}

@ -160,7 +160,7 @@ Process::Process(VersionInfoFactory * factory)
identified = true; identified = true;
// give the process a data model and memory layout fixed for the base of first module // give the process a data model and memory layout fixed for the base of first module
my_descriptor = new VersionInfo(*vinfo); my_descriptor = new VersionInfo(*vinfo);
my_descriptor->rebaseTo((uint32_t)d->base); my_descriptor->rebaseTo(getBase());
for(size_t i = 0; i < threads_ids.size();i++) for(size_t i = 0; i < threads_ids.size();i++)
{ {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]); HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]);
@ -394,13 +394,46 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
} }
} }
uint32_t Process::getBase() uintptr_t Process::getBase()
{ {
if(d) if(d)
return (uint32_t) d->base; return (uintptr_t) d->base;
return 0x400000; return 0x400000;
} }
int Process::adjustOffset(int offset, bool to_file)
{
if (!d)
return -1;
for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++)
{
auto &section = d->sections[i];
if (to_file)
{
unsigned delta = offset - section.VirtualAddress;
if (delta >= section.Misc.VirtualSize)
continue;
if (!section.PointerToRawData || delta >= section.SizeOfRawData)
return -1;
return (int)(section.PointerToRawData + delta);
}
else
{
unsigned delta = offset - section.PointerToRawData;
if (!section.PointerToRawData || delta >= section.SizeOfRawData)
continue;
if (delta >= section.Misc.VirtualSize)
return -1;
return (int)(section.VirtualAddress + delta);
}
}
return -1;
}
string Process::doReadClassName (void * vptr) string Process::doReadClassName (void * vptr)
{ {
char * rtti = readPtr((char *)vptr - 0x4); char * rtti = readPtr((char *)vptr - 0x4);
@ -440,3 +473,42 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
return result; return result;
} }
void* Process::memAlloc(const int length)
{
void *ret;
// returns 0 on error
ret = VirtualAlloc(0, length, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (!ret)
ret = (void*)-1;
return ret;
}
int Process::memDealloc(const void *ptr, const int length)
{
// can only free the whole region at once
// vfree returns 0 on error
return !VirtualFree(ptr, 0, MEM_RELEASE)
}
int Process::memProtect(const void *ptr, const int length, const int prot)
{
int prot_native = 0;
int old_prot = 0;
// only support a few constant combinations
if (prot == 0)
prot_native = PAGE_NOACCESS;
else if (prot == Process::MemProt::READ)
prot_native = PAGE_READONLY;
else if (prot == Process::MemProt::READ | Process::MemProt::WRITE)
prot_native = PAGE_READWRITE;
else if (prot == Process::MemProt::READ | Process::MemProt::WRITE | Process::MemProt::EXECUTE)
prot_native = PAGE_EXECUTE_READWRITE;
else if (prot == Process::MemProt::READ | Process::MemProt::EXECUTE)
prot_native = PAGE_EXECUTE_READ;
else
return -1;
return !VirtualProtect(ptr, length, prot_native, &old_prot);
}

@ -100,6 +100,24 @@ bool DFHack::removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type
return false; return false;
} }
df::item *DFHack::findItemRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getItem() : NULL;
}
df::building *DFHack::findBuildingRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getBuilding() : NULL;
}
df::unit *DFHack::findUnitRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
{
auto ref = findRef(vec, type);
return ref ? ref->getUnit() : NULL;
}
df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type) df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type)
{ {
for (int i = vec.size()-1; i >= 0; i--) for (int i = vec.size()-1; i >= 0; i--)

@ -103,13 +103,13 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
{ {
mem->setOS(OS_LINUX); mem->setOS(OS_LINUX);
// this is wrong... I'm not going to do base image relocation on linux though. // this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x0); mem->setBase(0x8048000);
} }
else if(os == "darwin") else if(os == "darwin")
{ {
mem->setOS(OS_APPLE); mem->setOS(OS_APPLE);
// this is wrong... I'm not going to do base image relocation on linux though. // this is wrong... I'm not going to do base image relocation on linux though.
mem->setBase(0x0); mem->setBase(0x1000000);
} }
else else
{ {

@ -41,6 +41,9 @@ namespace DFHack {
namespace Units { namespace Units {
struct NoblePosition; struct NoblePosition;
} }
namespace Screen {
struct Pen;
};
} }
namespace DFHack {namespace Lua { namespace DFHack {namespace Lua {
@ -285,6 +288,7 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj);
void Push(lua_State *state, const Units::NoblePosition &pos); void Push(lua_State *state, const Units::NoblePosition &pos);
DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info); DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info);
DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info);
template<class T> inline void Push(lua_State *state, T *ptr) { template<class T> inline void Push(lua_State *state, T *ptr) {
PushDFObject(state, ptr); PushDFObject(state, ptr);
} }
@ -315,6 +319,8 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos);
DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true);
DFHACK_EXPORT bool IsCoreContext(lua_State *state); DFHACK_EXPORT bool IsCoreContext(lua_State *state);
namespace Event { namespace Event {
@ -474,3 +480,18 @@ namespace DFHack {namespace Lua {
name##_event.invoke(out, 5); \ name##_event.invoke(out, 5); \
} \ } \
} }
#define DEFINE_LUA_EVENT_6(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6) { \
handler(out, arg1, arg2, arg3, arg4, arg5, 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); \
} \
}

@ -275,11 +275,13 @@ namespace DFHack
{ {
return my_descriptor; return my_descriptor;
}; };
uint32_t getBase(); uintptr_t getBase();
/// get the DF Process ID /// get the DF Process ID
int getPID(); int getPID();
/// get the DF Process FilePath /// get the DF Process FilePath
std::string getPath(); std::string getPath();
/// Adjust between in-memory and in-file image offset
int adjustOffset(int offset, bool to_file = false);
/// millisecond tick count, exactly as DF uses /// millisecond tick count, exactly as DF uses
uint32_t getTickCount(); uint32_t getTickCount();
@ -289,6 +291,27 @@ namespace DFHack
/// write a possibly read-only memory area /// write a possibly read-only memory area
bool patchMemory(void *target, const void* src, size_t count); bool patchMemory(void *target, const void* src, size_t count);
/// allocate new memory pages for code or stuff
/// returns -1 on error (0 is a valid address)
void* memAlloc(const int length);
/// free memory pages from memAlloc
/// should have length = alloced length for portability
/// returns 0 on success
int memDealloc(void *ptr, const int length);
/// change memory page permissions
/// prot is a bitwise OR of the MemProt enum
/// returns 0 on success
int memProtect(void *ptr, const int length, const int prot);
enum MemProt {
READ = 1,
WRITE = 2,
EXEC = 4
};
private: private:
VersionInfo * my_descriptor; VersionInfo * my_descriptor;
PlatformSpecific *d; PlatformSpecific *d;

@ -203,6 +203,12 @@ namespace DFHack
return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype)); return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype));
} }
inline
bool FlowPassableDown(df::tiletype tiletype)
{
return ENUM_ATTR(tiletype_shape, passable_flow_down, tileShape(tiletype));
}
inline inline
bool isWalkable(df::tiletype tiletype) bool isWalkable(df::tiletype tiletype)
{ {

@ -80,6 +80,10 @@ namespace DFHack
DFHACK_EXPORT df::general_ref *findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type); DFHACK_EXPORT df::general_ref *findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id); DFHACK_EXPORT bool removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id);
DFHACK_EXPORT df::item *findItemRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::building *findBuildingRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::unit *findUnitRef(std::vector<df::general_ref*> &vec, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type); DFHACK_EXPORT df::specific_ref *findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type);
DFHACK_EXPORT bool removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr); DFHACK_EXPORT bool removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr);
}// namespace DFHack }// namespace DFHack

@ -92,6 +92,9 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building);
*/ */
DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map <uint32_t, std::string> & btypes); DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map <uint32_t, std::string> & btypes);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::building *building, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::building *building, df::specific_ref_type type);
/** /**
* Sets the owner unit for the building. * Sets the owner unit for the building.
*/ */

@ -28,6 +28,8 @@ distribution.
#include "Export.h" #include "Export.h"
#include "Module.h" #include "Module.h"
#include "Types.h"
#include <ostream> #include <ostream>
#include "DataDefs.h" #include "DataDefs.h"
@ -55,6 +57,9 @@ namespace DFHack
DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx); DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::job *job, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::job *job, df::specific_ref_type type);
DFHACK_EXPORT df::building *getHolder(df::job *job); DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(df::job *job); DFHACK_EXPORT df::unit *getWorker(df::job *job);

@ -76,6 +76,8 @@ namespace DFHack
bool valid() const { return tile >= 0; } bool valid() const { return tile >= 0; }
bool empty() const { return ch == 0 && tile == 0; } bool empty() const { return ch == 0 && tile == 0; }
// NOTE: LuaApi.cpp assumes this struct is plain data and has empty destructor
Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false)
: ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)),
tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0)

@ -205,6 +205,9 @@ DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target);
/// Returns the true position of the unit (non-trivial in case of caged). /// Returns the true position of the unit (non-trivial in case of caged).
DFHACK_EXPORT df::coord getPosition(df::unit *unit); DFHACK_EXPORT df::coord getPosition(df::unit *unit);
DFHACK_EXPORT df::general_ref *getGeneralRef(df::unit *unit, df::general_ref_type type);
DFHACK_EXPORT df::specific_ref *getSpecificRef(df::unit *unit, df::specific_ref_type type);
DFHACK_EXPORT df::item *getContainer(df::unit *unit); DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
@ -235,6 +238,8 @@ DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false);
DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false); 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); DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id);
DFHACK_EXPORT int getExperience(df::unit *unit, df::job_skill skill_id, bool total = false);
DFHACK_EXPORT int computeMovementSpeed(df::unit *unit); DFHACK_EXPORT int computeMovementSpeed(df::unit *unit);
struct NoblePosition { struct NoblePosition {

@ -0,0 +1,121 @@
-- Simple binary patch with IDA dif file support.
local function load_patch(name)
local filename = name
if not string.match(filename, '[./\\]') then
filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif'
end
local file, err = io.open(filename, 'r')
if not file then
if string.match(err, ': No such file or directory') then
return nil, 'patch not found'
end
end
local old_bytes = {}
local new_bytes = {}
for line in file:lines() do
if string.match(line, '^%x+:') then
local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$')
if not offset then
file:close()
return nil, 'could not parse: '..line
end
offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16)
if oldv > 255 or newv > 255 then
file:close()
return nil, 'invalid byte values: '..line
end
old_bytes[offset] = oldv
new_bytes[offset] = newv
end
end
return { name = name, old_bytes = old_bytes, new_bytes = new_bytes }
end
local function rebase_table(input)
local output = {}
local base = dfhack.internal.getImageBase()
for k,v in pairs(input) do
local offset = dfhack.internal.adjustOffset(k)
if not offset then
return nil, string.format('invalid offset: %x', k)
end
output[base + offset] = v
end
return output
end
local function rebase_patch(patch)
local nold, err = rebase_table(patch.old_bytes)
if not nold then return nil, err end
local nnew, err = rebase_table(patch.new_bytes)
if not nnew then return nil, err end
return { name = patch.name, old_bytes = nold, new_bytes = nnew }
end
BinaryPatch = defclass(BinaryPatch)
BinaryPatch.ATTRS {
name = DEFAULT_NIL,
old_bytes = DEFAULT_NIL,
new_bytes = DEFAULT_NIL,
}
function load_dif_file(name)
local patch, err = load_patch(name)
if not patch then return nil, err end
local rpatch, err = rebase_patch(patch)
if not rpatch then return nil, err end
return BinaryPatch(rpatch)
end
function BinaryPatch:status()
local old_ok, err, addr = dfhack.internal.patchBytes({}, self.old_bytes)
if old_ok then
return 'removed'
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
return 'applied'
else
return 'conflict', addr
end
end
function BinaryPatch:isApplied()
return dfhack.internal.patchBytes({}, self.new_bytes)
end
function BinaryPatch:apply()
local ok, err, addr = dfhack.internal.patchBytes(self.new_bytes, self.old_bytes)
if ok then
return true, 'applied the patch'
elseif dfhack.internal.patchBytes({}, self.new_bytes) then
return true, 'patch is already applied'
else
return false, string.format('conflict at address %x', addr)
end
end
function BinaryPatch:isRemoved()
return dfhack.internal.patchBytes({}, self.old_bytes)
end
function BinaryPatch:remove()
local ok, err, addr = dfhack.internal.patchBytes(self.old_bytes, self.new_bytes)
if ok then
return true, 'removed the patch'
elseif dfhack.internal.patchBytes({}, self.old_bytes) then
return true, 'patch is already removed'
else
return false, string.format('conflict at address %x', addr)
end
end
return _ENV

@ -6,7 +6,9 @@ local dscreen = dfhack.screen
USE_GRAPHICS = dscreen.inGraphicsMode() USE_GRAPHICS = dscreen.inGraphicsMode()
CLEAR_PEN = {ch=32,fg=0,bg=0} local to_pen = dfhack.pen.parse
CLEAR_PEN = to_pen{ch=32,fg=0,bg=0}
function simulateInput(screen,...) function simulateInput(screen,...)
local keys = {} local keys = {}
@ -116,16 +118,6 @@ function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0 return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end end
function to_pen(default, pen, bg, bold)
if pen == nil then
return default or {}
elseif type(pen) ~= 'table' then
return {fg=pen,bg=bg,bold=bold}
else
return pen
end
end
function getKeyDisplay(code) function getKeyDisplay(code)
if type(code) == 'string' then if type(code) == 'string' then
code = df.interface_key[code] code = df.interface_key[code]
@ -215,7 +207,8 @@ Painter = defclass(Painter, ViewRect)
function Painter:init(args) function Painter:init(args)
self.x = self.x1 self.x = self.x1
self.y = self.y1 self.y = self.y1
self.cur_pen = to_pen(nil, args.pen or COLOR_GREY) self.cur_pen = to_pen(args.pen or COLOR_GREY)
self.cur_key_pen = to_pen(args.key_pen or COLOR_LIGHTGREEN)
end end
function Painter.new(rect, pen) function Painter.new(rect, pen)
@ -241,6 +234,7 @@ end
function Painter:viewport(x,y,w,h) function Painter:viewport(x,y,w,h)
local vp = ViewRect.viewport(x,y,w,h) local vp = ViewRect.viewport(x,y,w,h)
vp.cur_pen = self.cur_pen vp.cur_pen = self.cur_pen
vp.cur_key_pen = self.cur_key_pen
return mkinstance(Painter, vp):seek(0,0) return mkinstance(Painter, vp):seek(0,0)
end end
@ -280,10 +274,12 @@ function Painter:pen(pen,...)
end end
function Painter:color(fg,bold,bg) function Painter:color(fg,bold,bg)
self.cur_pen = copyall(self.cur_pen) self.cur_pen = to_pen(self.cur_pen, fg, bg, bold)
self.cur_pen.fg = fg return self
self.cur_pen.bold = bold end
if bg then self.cur_pen.bg = bg end
function Painter:key_pen(pen,...)
self.cur_key_pen = to_pen(self.cur_key_pen, pen, ...)
return self return self
end end
@ -339,10 +335,10 @@ function Painter:string(text,pen,...)
return self:advance(#text, nil) return self:advance(#text, nil)
end end
function Painter:key(code,pen,bg,...) function Painter:key(code,pen,...)
return self:string( return self:string(
getKeyDisplay(code), getKeyDisplay(code),
pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... to_pen(self.cur_key_pen, pen, ...)
) )
end end
@ -557,28 +553,28 @@ end
-- Plain grey-colored frame. -- Plain grey-colored frame.
GREY_FRAME = { GREY_FRAME = {
frame_pen = { ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY }, frame_pen = to_pen{ ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY },
title_pen = { fg = COLOR_BLACK, bg = COLOR_WHITE }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_WHITE },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
} }
-- The usual boundary used by the DF screens. Often has fancy pattern in tilesets. -- The usual boundary used by the DF screens. Often has fancy pattern in tilesets.
BOUNDARY_FRAME = { BOUNDARY_FRAME = {
frame_pen = { ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_BLACK, bg = COLOR_DARKGREY }, signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_DARKGREY },
} }
GREY_LINE_FRAME = { GREY_LINE_FRAME = {
frame_pen = { ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK }, frame_pen = to_pen{ ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK },
h_frame_pen = { ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK }, h_frame_pen = to_pen{ ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK },
v_frame_pen = { ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK }, v_frame_pen = to_pen{ ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK },
lt_frame_pen = { ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK }, lt_frame_pen = to_pen{ ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK },
lb_frame_pen = { ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK }, lb_frame_pen = to_pen{ ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK },
rt_frame_pen = { ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK }, rt_frame_pen = to_pen{ ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK },
rb_frame_pen = { ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK }, rb_frame_pen = to_pen{ ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK },
title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY },
signature_pen = { fg = COLOR_DARKGREY, bg = COLOR_BLACK }, signature_pen = to_pen{ fg = COLOR_DARKGREY, bg = COLOR_BLACK },
} }
function paint_frame(x1,y1,x2,y2,style,title) function paint_frame(x1,y1,x2,y2,style,title)

@ -160,8 +160,8 @@ function ListBox:preinit(info)
end end
function ListBox:init(info) function ListBox:init(info)
local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false) local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false)
local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true)
local list_widget = widgets.List local list_widget = widgets.List
if self.with_filter then if self.with_filter then

@ -504,7 +504,7 @@ function List:onRenderBody(dc)
local icon = getval(obj.icon) local icon = getval(obj.icon)
if icon then if icon then
dc:seek(0, y) dc:seek(0, y)
if type(icon) == 'table' then if type(icon) ~= 'string' then
dc:char(nil,icon) dc:char(nil,icon)
else else
if current then if current then
@ -573,7 +573,7 @@ FilteredList = defclass(FilteredList, Widget)
function FilteredList:init(info) function FilteredList:init(info)
self.edit = EditField{ self.edit = EditField{
text_pen = info.cursor_pen, text_pen = info.edit_pen or info.cursor_pen,
frame = { l = info.icon_width, t = 0 }, frame = { l = info.icon_width, t = 0 },
on_change = self:callback('onFilterChange'), on_change = self:callback('onFilterChange'),
on_char = self:callback('onFilterChar'), on_char = self:callback('onFilterChar'),
@ -583,6 +583,7 @@ function FilteredList:init(info)
text_pen = info.text_pen, text_pen = info.text_pen,
cursor_pen = info.cursor_pen, cursor_pen = info.cursor_pen,
inactive_pen = info.inactive_pen, inactive_pen = info.inactive_pen,
icon_pen = info.icon_pen,
row_height = info.row_height, row_height = info.row_height,
scroll_keys = info.scroll_keys, scroll_keys = info.scroll_keys,
icon_width = info.icon_width, icon_width = info.icon_width,

@ -24,7 +24,7 @@ function CheckedArray:__len()
return self.count return self.count
end end
function CheckedArray:__index(idx) function CheckedArray:__index(idx)
if type(idx) == number then if type(idx) == "number" then
if idx >= self.count then if idx >= self.count then
error('Index out of bounds: '..tostring(idx)) error('Index out of bounds: '..tostring(idx))
end end
@ -195,6 +195,26 @@ function MemoryArea:delete()
for k,v in pairs(self) do self[k] = nil end for k,v in pairs(self) do self[k] = nil end
end end
-- Static code segment search
function get_code_segment()
local cstart, cend
for i,mem in ipairs(dfhack.internal.getMemRanges()) do
if mem.read and mem.execute
and (string.match(mem.name,'/dwarfort%.exe$')
or string.match(mem.name,'/Dwarf_Fortress$')
or string.match(mem.name,'Dwarf Fortress%.exe'))
then
cstart = mem.start_addr
cend = mem.end_addr
end
end
if cstart and cend then
return MemoryArea.new(cstart, cend)
end
end
-- Static data segment search -- Static data segment search
local function find_data_segment() local function find_data_segment()

@ -457,6 +457,7 @@ function getBuildingCenter(building)
return xyz2pos(building.centerx, building.centery, building.z) return xyz2pos(building.centerx, building.centery, building.z)
end end
-- Split the string by the given delimiter
function split_string(self, delimiter) function split_string(self, delimiter)
local result = { } local result = { }
local from = 1 local from = 1

@ -178,6 +178,20 @@ bool Buildings::ReadCustomWorkshopTypes(map <uint32_t, string> & btypes)
return true; return true;
} }
df::general_ref *Buildings::getGeneralRef(df::building *building, df::general_ref_type type)
{
CHECK_NULL_POINTER(building);
return findRef(building->general_refs, type);
}
df::specific_ref *Buildings::getSpecificRef(df::building *building, df::specific_ref_type type)
{
CHECK_NULL_POINTER(building);
return findRef(building->specific_refs, type);
}
bool Buildings::setOwner(df::building *bld, df::unit *unit) bool Buildings::setOwner(df::building *bld, df::unit *unit)
{ {
CHECK_NULL_POINTER(bld); CHECK_NULL_POINTER(bld);
@ -895,7 +909,7 @@ static bool linkForConstruct(df::job* &job, df::building *bld)
job = new df::job(); job = new df::job();
job->job_type = df::job_type::ConstructBuilding; job->job_type = df::job_type::ConstructBuilding;
job->pos = df::coord(bld->centerx, bld->centery, bld->z); job->pos = df::coord(bld->centerx, bld->centery, bld->z);
job->references.push_back(ref); job->general_refs.push_back(ref);
bld->jobs.push_back(job); bld->jobs.push_back(job);

@ -509,7 +509,7 @@ df::general_ref *Items::getGeneralRef(df::item *item, df::general_ref_type type)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
return findRef(item->itemrefs, type); return findRef(item->general_refs, type);
} }
df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type) df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type)
@ -530,9 +530,9 @@ bool Items::setOwner(df::item *item, df::unit *unit)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
for (int i = item->itemrefs.size()-1; i >= 0; i--) for (int i = item->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref)) if (!strict_virtual_cast<df::general_ref_unit_itemownerst>(ref))
continue; continue;
@ -546,7 +546,7 @@ bool Items::setOwner(df::item *item, df::unit *unit)
} }
delete ref; delete ref;
vector_erase_at(item->itemrefs, i); vector_erase_at(item->general_refs, i);
} }
item->flags.bits.owned = false; item->flags.bits.owned = false;
@ -561,7 +561,7 @@ bool Items::setOwner(df::item *item, df::unit *unit)
ref->unit_id = unit->id; ref->unit_id = unit->id;
insert_into_vector(unit->owned_items, item->id); insert_into_vector(unit->owned_items, item->id);
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
} }
return true; return true;
@ -580,9 +580,9 @@ void Items::getContainedItems(df::item *item, std::vector<df::item*> *items)
items->clear(); items->clear();
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
if (ref->getType() != general_ref_type::CONTAINS_ITEM) if (ref->getType() != general_ref_type::CONTAINS_ITEM)
continue; continue;
@ -617,9 +617,9 @@ df::coord Items::getPosition(df::item *item)
if (item->flags.bits.in_inventory) if (item->flags.bits.in_inventory)
{ {
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -716,9 +716,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
if (item->world_data_id != -1) if (item->world_data_id != -1)
return false; return false;
for (size_t i = 0; i < item->itemrefs.size(); i++) for (size_t i = 0; i < item->general_refs.size(); i++)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -748,9 +748,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
{ {
bool found = false; bool found = false;
for (int i = item->itemrefs.size()-1; i >= 0; i--) for (int i = item->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = item->itemrefs[i]; df::general_ref *ref = item->general_refs[i];
switch (ref->getType()) switch (ref->getType())
{ {
@ -767,7 +767,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
item2->flags.bits.weight_computed = false; item2->flags.bits.weight_computed = false;
removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id); removeRef(item2->general_refs, general_ref_type::CONTAINS_ITEM, item->id);
} }
break; break;
@ -799,7 +799,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item)
} }
found = true; found = true;
vector_erase_at(item->itemrefs, i); vector_erase_at(item->general_refs, i);
delete ref; delete ref;
} }
@ -878,10 +878,10 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df:
container->flags.bits.weight_computed = false; container->flags.bits.weight_computed = false;
ref1->item_id = item->id; ref1->item_id = item->id;
container->itemrefs.push_back(ref1); container->general_refs.push_back(ref1);
ref2->item_id = container->id; ref2->item_id = container->id;
item->itemrefs.push_back(ref2); item->general_refs.push_back(ref2);
return true; return true;
} }
@ -912,7 +912,7 @@ bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::
item->flags.bits.in_building=true; item->flags.bits.in_building=true;
ref->building_id=building->id; ref->building_id=building->id;
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
auto con=new df::building_actual::T_contained_items; auto con=new df::building_actual::T_contained_items;
con->item=item; con->item=item;
@ -955,7 +955,7 @@ bool DFHack::Items::moveToInventory(
unit->inventory.push_back(newInventoryItem); unit->inventory.push_back(newInventoryItem);
holderReference->unit_id = unit->id; holderReference->unit_id = unit->id;
item->itemrefs.push_back(holderReference); item->general_refs.push_back(holderReference);
resetUnitInvFlags(unit, newInventoryItem); resetUnitInvFlags(unit, newInventoryItem);
@ -1016,7 +1016,7 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item)
proj->item = item; proj->item = item;
ref->projectile_id = proj->id; ref->projectile_id = proj->id;
item->itemrefs.push_back(ref); item->general_refs.push_back(ref);
linked_list_append(&world->proj_list, proj->link); linked_list_append(&world->proj_list, proj->link);

@ -35,6 +35,7 @@ using namespace std;
#include "Error.h" #include "Error.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "MiscUtils.h" #include "MiscUtils.h"
#include "Types.h"
#include "modules/Job.h" #include "modules/Job.h"
#include "modules/Materials.h" #include "modules/Materials.h"
@ -71,14 +72,14 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job)
pnew->specific_refs.clear(); pnew->specific_refs.clear();
// Clone refs // Clone refs
for (int i = pnew->references.size()-1; i >= 0; i--) for (int i = pnew->general_refs.size()-1; i >= 0; i--)
{ {
df::general_ref *ref = pnew->references[i]; df::general_ref *ref = pnew->general_refs[i];
if (virtual_cast<df::general_ref_unit_workerst>(ref)) if (virtual_cast<df::general_ref_unit>(ref))
vector_erase_at(pnew->references, i); vector_erase_at(pnew->general_refs, i);
else else
pnew->references[i] = ref->clone(); pnew->general_refs[i] = ref->clone();
} }
// Clone items // Clone items
@ -96,8 +97,8 @@ void DFHack::Job::deleteJobStruct(df::job *job)
// Only allow free-floating job structs // Only allow free-floating job structs
assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); assert(!job->list_link && job->items.empty() && job->specific_refs.empty());
for (int i = job->references.size()-1; i >= 0; i--) for (int i = job->general_refs.size()-1; i >= 0; i--)
delete job->references[i]; delete job->general_refs[i];
for (int i = job->job_items.size()-1; i >= 0; i--) for (int i = job->job_items.size()-1; i >= 0; i--)
delete job->job_items[i]; delete job->job_items[i];
@ -228,13 +229,27 @@ void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
printItemDetails(out, job->job_items[i], i); printItemDetails(out, job->job_items[i], i);
} }
df::general_ref *Job::getGeneralRef(df::job *job, df::general_ref_type type)
{
CHECK_NULL_POINTER(job);
return findRef(job->general_refs, type);
}
df::specific_ref *Job::getSpecificRef(df::job *job, df::specific_ref_type type)
{
CHECK_NULL_POINTER(job);
return findRef(job->specific_refs, type);
}
df::building *DFHack::Job::getHolder(df::job *job) df::building *DFHack::Job::getHolder(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++) for (size_t i = 0; i < job->general_refs.size(); i++)
{ {
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]); VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]);
if (ref) if (ref)
return ref->getBuilding(); return ref->getBuilding();
} }
@ -246,9 +261,9 @@ df::unit *DFHack::Job::getWorker(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++) for (size_t i = 0; i < job->general_refs.size(); i++)
{ {
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]); VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
if (ref) if (ref)
return ref->getUnit(); return ref->getUnit();
} }

@ -190,6 +190,8 @@ bool MaterialInfo::find(const std::vector<std::string> &items)
} }
else if (items.size() == 2) else if (items.size() == 2)
{ {
if (items[1] == "NONE" && findBuiltin(items[0]))
return true;
if (findPlant(items[0], items[1])) if (findPlant(items[0], items[1]))
return true; return true;
if (findCreature(items[0], items[1])) if (findCreature(items[0], items[1]))
@ -210,7 +212,7 @@ bool MaterialInfo::findBuiltin(const std::string &token)
} }
df::world_raws &raws = world->raws; df::world_raws &raws = world->raws;
for (int i = 1; i < NUM_BUILTIN; i++) for (int i = 0; i < NUM_BUILTIN; i++)
{ {
auto obj = raws.mat_table.builtin[i]; auto obj = raws.mat_table.builtin[i];
if (obj && obj->id == token) if (obj && obj->id == token)
@ -312,7 +314,7 @@ std::string MaterialInfo::getToken()
else if (index == 1) else if (index == 1)
return "COAL:CHARCOAL"; return "COAL:CHARCOAL";
} }
return material->id + ":NONE"; return material->id;
case Inorganic: case Inorganic:
return "INORGANIC:" + inorganic->id; return "INORGANIC:" + inorganic->id;
case Creature: case Creature:
@ -423,6 +425,8 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat)
TEST(glass, IS_GLASS); TEST(glass, IS_GLASS);
if (cat.bits.clay && linear_index(material->reaction_product.id, std::string("FIRED_MAT")) >= 0) if (cat.bits.clay && linear_index(material->reaction_product.id, std::string("FIRED_MAT")) >= 0)
return true; return true;
if (cat.bits.milk && linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0)
return true;
return false; return false;
} }

@ -519,18 +519,25 @@ df::coord Units::getPosition(df::unit *unit)
return unit->pos; return unit->pos;
} }
df::item *Units::getContainer(df::unit *unit) df::general_ref *Units::getGeneralRef(df::unit *unit, df::general_ref_type type)
{ {
CHECK_NULL_POINTER(unit); CHECK_NULL_POINTER(unit);
for (size_t i = 0; i < unit->refs.size(); i++) return findRef(unit->general_refs, type);
{ }
df::general_ref *ref = unit->refs[i];
if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM)
return ref->getItem();
}
return NULL; df::specific_ref *Units::getSpecificRef(df::unit *unit, df::specific_ref_type type)
{
CHECK_NULL_POINTER(unit);
return findRef(unit->specific_refs, type);
}
df::item *Units::getContainer(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM);
} }
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) static df::assumed_identity *getFigureIdentity(df::historical_figure *figure)
@ -607,9 +614,9 @@ df::nemesis_record *Units::getNemesis(df::unit *unit)
if (!unit) if (!unit)
return NULL; return NULL;
for (unsigned i = 0; i < unit->refs.size(); i++) for (unsigned i = 0; i < unit->general_refs.size(); i++)
{ {
df::nemesis_record *rv = unit->refs[i]->getNemesis(); df::nemesis_record *rv = unit->general_refs[i]->getNemesis();
if (rv && rv->unit == unit) if (rv && rv->unit == unit)
return rv; return rv;
} }
@ -902,6 +909,24 @@ int Units::getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust
return 0; return 0;
} }
int Units::getExperience(df::unit *unit, df::job_skill skill_id, bool total)
{
CHECK_NULL_POINTER(unit);
if (!unit->status.current_soul)
return 0;
auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, skill_id);
if (!skill)
return 0;
int xp = skill->experience;
// exact formula used by the game:
if (total && skill->rating > 0)
xp += 500*skill->rating + 100*skill->rating*(skill->rating - 1)/2;
return xp;
}
int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id)
{ {
/* /*

@ -71,15 +71,15 @@ bool Vegetation::copyPlant(const int32_t index, t_plant &out)
out.material = out.origin->material; out.material = out.origin->material;
out.pos = out.origin->pos; out.pos = out.origin->pos;
out.grow_counter = out.origin->grow_counter; out.grow_counter = out.origin->grow_counter;
out.temperature_1 = out.origin->temperature_1; out.temperature_1 = out.origin->temperature.whole;
out.temperature_2 = out.origin->temperature_2; out.temperature_2 = out.origin->temperature.fraction;
out.is_burning = out.origin->is_burning; out.is_burning = out.origin->is_burning;
out.hitpoints = out.origin->hitpoints; out.hitpoints = out.origin->hitpoints;
out.update_order = out.origin->update_order; out.update_order = out.origin->update_order;
//out.unk1 = out.origin->anon_1; //out.unk1 = out.origin->anon_1;
//out.unk2 = out.origin->anon_2; //out.unk2 = out.origin->anon_2;
//out.temperature_3 = out.origin->temperature_3; //out.temperature_3 = out.origin->temperature_unk;
//out.temperature_4 = out.origin->temperature_4; //out.temperature_4 = out.origin->min_safe_temp;
//out.temperature_5 = out.origin->temperature_5; //out.temperature_5 = out.origin->max_safe_temp;
return true; return true;
} }

@ -197,11 +197,15 @@ PersistentDataItem World::AddPersistentData(const std::string &key)
std::vector<df::historical_figure*> &hfvec = df::historical_figure::get_vector(); std::vector<df::historical_figure*> &hfvec = df::historical_figure::get_vector();
df::historical_figure *hfig = new df::historical_figure(); df::historical_figure *hfig = new df::historical_figure();
hfig->id = next_persistent_id--; hfig->id = next_persistent_id;
hfig->name.has_name = true; hfig->name.has_name = true;
hfig->name.first_name = key; hfig->name.first_name = key;
memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); memset(hfig->name.words, 0xFF, sizeof(hfig->name.words));
if (!hfvec.empty())
hfig->id = std::min(hfig->id, hfvec[0]->id-1);
next_persistent_id = hfig->id-1;
hfvec.insert(hfvec.begin(), hfig); hfvec.insert(hfvec.begin(), hfig);
persistent_index.insert(T_persistent_item(key, -hfig->id)); persistent_index.insert(T_persistent_item(key, -hfig->id));

@ -1 +1 @@
Subproject commit fcacacce7cf09cf70f011fea87b5be416da73457 Subproject commit 9b4dc47a54c8b15db2f30fbf926deb8c1bf992f6

@ -0,0 +1,142 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x2ac6b
CC CC CC CC CC
66 39 E8 EB 53
.text:0042B86B loc_42B86B:
.text:0042B86B cmp ax, bp
.text:0042B86E jmp short loc_42B8C3
0x2ac7b
CC CC CC CC CC
E9 96 A2 00 00
.text:0042B87B loc_42B87B:
.text:0042B87B jmp loc_435B16
0x2acc3
CC CC CC CC CC CC CC CC CC CC CC CC CC
75 0A 66 FF 4C 24 16 79 03 58 EB AC C3
.text:0042B8C3 loc_42B8C3:
.text:0042B8C3 jnz short locret_42B8CF
.text:0042B8C5 dec word ptr [esp+16h] ; 4+8+8+2
.text:0042B8CA jns short locret_42B8CF
.text:0042B8CC pop eax
.text:0042B8CD jmp short loc_42B87B
.text:0042B8CF locret_42B8CF:
.text:0042B8CF retn
0x2b2a1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
66 C7 44 24 0E 01 00 8B 90 44 01 00 00 C3 CC
.text:0042BEA1 loc_42BEA1:
.text:0042BEA1 mov word ptr [esp+0Eh], 1 ; 4+8+2
.text:0042BEA8 mov edx, [eax+144h]
.text:0042BEAE retn
0x34d91
8B 90 44 01 00 00
E8 0B 65 FF FF 90
<<<<
.text:00435991 mov edx, [eax+144h]
====
.text:00435991 call loc_42BEA1
.text:00435996 nop
>>>>
0x34e53
0F 84 BD 00 00 00
E8 6B 5E FF FF 90
<<<<
.text:00435A53 jz loc_435B16
====
.text:00435A53 call loc_42B8C3
.text:00435A58 nop
>>>>
0x34ef3
66 3B C5 74 1E
E8 73 5D FF FF
<<<<
.text:00435AF3 cmp ax, bp
.text:00435AF6 jz short loc_435B16
====
.text:00435AF3 call loc_42B86B
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC6B: CC 66
0002AC6C: CC 39
0002AC6D: CC E8
0002AC6E: CC EB
0002AC6F: CC 53
0002AC7B: CC E9
0002AC7C: CC 96
0002AC7D: CC A2
0002AC7E: CC 00
0002AC7F: CC 00
0002ACC3: CC 75
0002ACC4: CC 0A
0002ACC5: CC 66
0002ACC6: CC FF
0002ACC7: CC 4C
0002ACC8: CC 24
0002ACC9: CC 16
0002ACCA: CC 79
0002ACCB: CC 03
0002ACCC: CC 58
0002ACCD: CC EB
0002ACCE: CC AC
0002ACCF: CC C3
0002B2A1: CC 66
0002B2A2: CC C7
0002B2A3: CC 44
0002B2A4: CC 24
0002B2A5: CC 0E
0002B2A6: CC 01
0002B2A7: CC 00
0002B2A8: CC 8B
0002B2A9: CC 90
0002B2AA: CC 44
0002B2AB: CC 01
0002B2AC: CC 00
0002B2AD: CC 00
0002B2AE: CC C3
00034D91: 8B E8
00034D92: 90 0B
00034D93: 44 65
00034D94: 01 FF
00034D95: 00 FF
00034D96: 00 90
00034E53: 0F E8
00034E54: 84 6B
00034E55: BD 5E
00034E56: 00 FF
00034E57: 00 FF
00034E58: 00 90
00034EF3: 66 E8
00034EF4: 3B 73
00034EF5: C5 5D
00034EF6: 74 FF
00034EF7: 1E FF

@ -0,0 +1,91 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
Original code:
.text:00916BCE mov edi, ebp
.text:00916BD0 call eax
.text:00916BD2 test eax, eax
.text:00916BD4 jnz short loc_916C1C
.text:00916C0A mov edi, ebp
.text:00916C14 mov edi, ebp
Patch:
0x2ac34:
CC CC CC CC CC CC CC CC CC CC CC CC
8B 7C 24 78 8B 3C B7 FF D0 EB 25 CC
.text:0042B834 loc_42B834:
.text:0042B834 mov edi, [esp+78h]
.text:0042B838 mov edi, [edi+esi*4]
.text:0042B83B call eax
.text:0042B83D jmp short unk_42B864
0x2ac64
CC CC CC CC CC CC CC CC CC CC CC CC
85 C0 E9 69 B3 4E 00 CC CC CC CC CC
.text:0042B864 loc_42B864:
.text:0042B864 test eax, eax
.text:0042B866 jmp loc_916BD4
0x515fce
8B FD FF D0 85 C0
E9 61 4C B1 FF 90
.text:00916BCE jmp loc_42B834
.text:00916BD3 nop
.text:00916BD4 loc_916BD4:
0x51600a
8B FD
90 90
.text:00916C0A nop
.text:00916C0B nop
0x516014
8B FD
90 90
.text:00916C14 nop
.text:00916C15 nop
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC34: CC 8B
0002AC35: CC 7C
0002AC36: CC 24
0002AC37: CC 78
0002AC38: CC 8B
0002AC39: CC 3C
0002AC3A: CC B7
0002AC3B: CC FF
0002AC3C: CC D0
0002AC3D: CC EB
0002AC3E: CC 25
0002AC64: CC 85
0002AC65: CC C0
0002AC66: CC E9
0002AC67: CC 69
0002AC68: CC B3
0002AC69: CC 4E
0002AC6A: CC 00
00515FCE: 8B E9
00515FCF: FD 61
00515FD0: FF 4C
00515FD1: D0 B1
00515FD2: 85 FF
00515FD3: C0 90
0051600A: 8B 90
0051600B: FD 90
00516014: 8B 90
00516015: FD 90

@ -0,0 +1,61 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:008629BD mov edi, [eax+38h]
.text:008629C0 mov eax, [eax+3Ch]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp edi, eax
.text:008629C9 jnb short loc_862A22
.text:008629CB jmp short loc_8629D0
.text:008629CD lea ecx, [ecx+0]
...
.text:00862A19 add edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jb short loc_8629D0
Patch:
0x461dbd
8B 78 38 8B 40 3C 89 44 24 1C 3B F8
8B 78 3C 8B 40 38 89 44 24 1C 39 F8
.text:008629BD mov edi, [eax+3Ch]
.text:008629C0 mov eax, [eax+38h]
.text:008629C3 mov [esp+1Ch], eax
.text:008629C7 cmp eax, edi
0x461dcb
EB 03 8D 49 00
83 EF 04 90 90
.text:008629CB sub edi, 4
.text:008629CE nop
.text:008629CF nop
0x461e19
83 C7 04 3B 7C 24 1C 72 AE
83 EF 04 3B 7C 24 1C 73 AE
.text:00862A19 sub edi, 4
.text:00862A1C cmp edi, [esp+1Ch]
.text:00862A20 jnb short loc_8629D0
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00461DBF: 38 3C
00461DC2: 3C 38
00461DC7: 3B 39
00461DCB: EB 83
00461DCC: 03 EF
00461DCD: 8D 04
00461DCE: 49 90
00461DCF: 00 90
00461E1A: C7 EF
00461E20: 72 73

@ -0,0 +1,104 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x461de2
F6 46 0C 01 74 31
E9 0A 8E BC FF 90
.text:008629E2 jmp near ptr loc_42B7F1 ; << CAVE
.text:008629E7 nop
.text:008629E8 loc_8629E8:
0x2abf1
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 4C 24 2C F6 46 0C 01 75 08 E9 19 72 43 00
.text:0042B7F1 loc_42B7F1:
.text:0042B7F1 mov ecx, [esp+2Ch] ; job
.text:0042B7F5 test byte ptr [esi+0Ch], 1
.text:0042B7F9 jnz short near ptr loc_42B803
.text:0042B7FB coord_test_jfail:
.text:0042B7FB jmp loc_862A19
0x2ac03
CC CC CC CC CC CC CC CC CC CC CC CC CC
8B 41 10 3B 46 04 75 F0 EB 06 CC CC CC
.text:0042B803 loc_42B803:
.text:0042B803 mov eax, [ecx+10h] ; job->pos.(x,y)
.text:0042B806 cmp eax, [esi+4] ; item->pos.(x,y)
.text:0042B809 jnz short coord_test_jfail
.text:0042B80B jmp short near ptr loc_42B813
0x2ac13
CC CC CC CC CC CC CC CC CC CC CC CC CC
66 8B 41 14 66 3B 46 08 75 DE EB 06 CC
text:0042B813 loc_42B813:
.text:0042B813 mov ax, [ecx+14h] ; job->pos.z
.text:0042B817 cmp ax, [esi+8] ; item->pos.z
.text:0042B81B jnz short coord_test_jfail
.text:0042B81D jmp short near ptr loc_42B825
0x2ac25
CC CC CC CC CC CC CC CC CC CC CC
E9 BE 71 43 00 CC CC CC CC CC CC
.text:0042B825 loc_42B825:
.text:0042B825 jmp loc_8629E8
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002ABF1: CC 8B
0002ABF2: CC 4C
0002ABF3: CC 24
0002ABF4: CC 2C
0002ABF5: CC F6
0002ABF6: CC 46
0002ABF7: CC 0C
0002ABF8: CC 01
0002ABF9: CC 75
0002ABFA: CC 08
0002ABFB: CC E9
0002ABFC: CC 19
0002ABFD: CC 72
0002ABFE: CC 43
0002ABFF: CC 00
0002AC03: CC 8B
0002AC04: CC 41
0002AC05: CC 10
0002AC06: CC 3B
0002AC07: CC 46
0002AC08: CC 04
0002AC09: CC 75
0002AC0A: CC F0
0002AC0B: CC EB
0002AC0C: CC 06
0002AC13: CC 66
0002AC14: CC 8B
0002AC15: CC 41
0002AC16: CC 14
0002AC17: CC 66
0002AC18: CC 3B
0002AC19: CC 46
0002AC1A: CC 08
0002AC1B: CC 75
0002AC1C: CC DE
0002AC1D: CC EB
0002AC1E: CC 06
0002AC25: CC E9
0002AC26: CC BE
0002AC27: CC 71
0002AC28: CC 43
0002AC29: CC 00
00461DE2: F6 E9
00461DE3: 46 0A
00461DE4: 0C 8E
00461DE5: 01 BC
00461DE6: 74 FF
00461DE7: 31 90

@ -0,0 +1,62 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x68a63
0F 85 58 01 00 00
90 90 90 90 90 90
<<<<
.text:00469663 jnz loc_4697C1
====
.text:00469663 nop
.text:00469664 nop
.text:00469665 nop
.text:00469666 nop
.text:00469667 nop
.text:00469668 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x3dcbf9
8B 0C 90 8B 81 80 00 00 00
8B 3C 90 8B 87 80 00 00 00
<<<<
.text:007DD7F9 mov ecx, [eax+edx*4]
.text:007DD7FC mov eax, [ecx+80h]
====
.text:007DD7F9 mov edi, [eax+edx*4]
.text:007DD7FC mov eax, [edi+80h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
00068A63: 0F 90
00068A64: 85 90
00068A65: 58 90
00068A66: 01 90
00068A67: 00 90
00068A68: 00 90
003DCBFA: 0C 3C
003DCBFD: 81 87

@ -0,0 +1,83 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530
0x2ac85
CC CC CC CC CC CC CC CC CC CC CC
29 44 24 24 8B 9D 28 01 00 00 C3
.text:0042B885 loc_42B885:
.text:0042B885 sub [esp+24h], eax
.text:0042B889 mov ebx, [ebp+128h]
.text:0042B88F retn
0x2ac94
CC CC CC CC CC CC CC CC CC CC CC CC
89 C1 8B 00 FF 90 34 02 00 00 EB E5
.text:0042B894 loc_42B894:
.text:0042B894 mov ecx, eax
.text:0042B896 mov eax, [eax]
.text:0042B898 call dword ptr [eax+234h]
.text:0042B89E jmp short loc_42B885
0x6e28ff
29 44 24 20
90 90 90 90
<<<<
.text:00AE34FF sub [esp+20h], eax
====
.text:00AE34FF nop
.text:00AE3500 nop
.text:00AE3501 nop
.text:00AE3502 nop
>>>>
0x6e2999
8B 9D 28 01 00 00
E8 F6 82 94 FF 90
<<<<
.text:00AE3599 mov ebx, [ebp+128h]
====
.text:00AE3599 call loc_42B894
.text:00AE359E nop
>>>>
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
0002AC85: CC 29
0002AC86: CC 44
0002AC87: CC 24
0002AC88: CC 24
0002AC89: CC 8B
0002AC8A: CC 9D
0002AC8B: CC 28
0002AC8C: CC 01
0002AC8D: CC 00
0002AC8E: CC 00
0002AC8F: CC C3
0002AC94: CC 89
0002AC95: CC C1
0002AC96: CC 8B
0002AC97: CC 00
0002AC98: CC FF
0002AC99: CC 90
0002AC9A: CC 34
0002AC9B: CC 02
0002AC9C: CC 00
0002AC9D: CC 00
0002AC9E: CC EB
0002AC9F: CC E5
006E28FF: 29 90
006E2900: 44 90
006E2901: 24 90
006E2902: 20 90
006E2999: 8B E8
006E299A: 9D F6
006E299B: 28 82
006E299C: 01 94
006E299D: 00 FF
006E299E: 00 90

@ -0,0 +1,61 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x4c05c4
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C11C4 mov ecx, [esp+98h+var_18]
====
.text:008C11C4 mov ecx, eax
.text:008C11C6 nop
.text:008C11C7 nop
.text:008C11C8 nop
.text:008C11C9 nop
.text:008C11CA nop
>>>>
0x4c06a1
8B 8C 24 80 00 00 00
89 C1 90 90 90 90 90
<<<<
.text:008C12A1 mov ecx, [esp+98h+var_18]
====
.text:008C12A1 mov ecx, eax
.text:008C12A3 nop
.text:008C12A4 nop
.text:008C12A5 nop
.text:008C12A6 nop
.text:008C12A7 nop
>>>>
basically:
b_squad_id = building->getSpecificSquad();
- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4))
+ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4))
unassign(building);
the reason being, some_other_squad_id contains irrelevant garbage at this point
---8<---
This difference file is created by The Interactive Disassembler
Dwarf Fortress.exe
004C05C4: 8B 89
004C05C5: 8C C1
004C05C6: 24 90
004C05C7: 80 90
004C05C8: 00 90
004C05C9: 00 90
004C05CA: 00 90
004C06A1: 8B 89
004C06A2: 8C C1
004C06A3: 24 90
004C06A4: 80 90
004C06A5: 00 90
004C06A6: 00 90
004C06A7: 00 90

@ -0,0 +1,147 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
0x9461
90 90 90 90 90 90 90 90 90 90 90 90 90 90
C7 44 24 18 01 00 00 00 FF A0 44 01 00 00
.text:08051461 sub_8051461 proc near
.text:08051461 mov dword ptr [esp+18h], 1
.text:08051469 jmp dword ptr [eax+144h]
.text:08051469 sub_8051461 endp
0x9548
90 90 90 90 90 90 90 90
FF 4C 24 14 78 08 EB 0B
.text:08051548 loc_8051548:
.text:08051548 dec dword ptr [esp+14h]
.text:0805154C js short loc_8051556
.text:0805154E jmp short loc_805155B
0x9556
90 90 90 90 90 90 90 90 90 90
E9 F6 8C 05 00 E9 D0 8D 05 00
.text:08051556 loc_8051556:
.text:08051556 jmp loc_80AA251
.text:0805155B loc_805155B:
.text:0805155B jmp loc_80AA330
0x9568
90 90 90 90 90 90 90 90
FF 4C 24 14 78 E8 EB 06
.text:08051568 loc_8051568:
.text:08051568 dec [esp+14h]
.text:0805156C js short loc_8051556
.text:0805156E jmp short loc_8051576
0x9576
90 90 90 90 90
E9 4D 8E 05 00
.text:08051576 loc_8051576:
.text:08051576 jmp loc_80AA3C8
0x62243
FF 90 44 01 00 00
E8 19 72 FA FF 90
<<<<
.text:080AA243 call dword ptr [eax+144h]
====
.text:080AA243 call sub_8051461
.text:080AA248 nop
>>>>
0x62369
E9 E3 FE FF FF
E9 DA 71 FA FF
<<<<
.text:080AA369 jmp loc_80AA251
====
.text:080AA369 jmp loc_8051548
>>>>
0x623f6
E9 56 FE FF FF
E9 6D 71 FA FF
<<<<
.text:080AA3F6 jmp loc_80AA251
====
.text:080AA3F6 jmp loc_8051568
>>>>
basically:
+ int allowed_count = 1; // to mean 2
...
- if (type(item) == new_type)
+ if (type(item) == new_type && --allowed_count < 0)
return false;
to allow up to two items of the same type at the same time
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009461: 90 C7
00009462: 90 44
00009463: 90 24
00009464: 90 18
00009465: 90 01
00009466: 90 00
00009467: 90 00
00009468: 90 00
00009469: 90 FF
0000946A: 90 A0
0000946B: 90 44
0000946C: 90 01
0000946D: 90 00
0000946E: 90 00
00009548: 90 FF
00009549: 90 4C
0000954A: 90 24
0000954B: 90 14
0000954C: 90 78
0000954D: 90 08
0000954E: 90 EB
0000954F: 90 0B
00009556: 90 E9
00009557: 90 F6
00009558: 90 8C
00009559: 90 05
0000955A: 90 00
0000955B: 90 E9
0000955C: 90 D0
0000955D: 90 8D
0000955E: 90 05
0000955F: 90 00
00009568: 90 FF
00009569: 90 4C
0000956A: 90 24
0000956B: 90 14
0000956C: 90 78
0000956D: 90 E8
0000956E: 90 EB
0000956F: 90 06
00009576: 90 E9
00009577: 90 4D
00009578: 90 8E
00009579: 90 05
0000957A: 90 00
00062243: FF E8
00062244: 90 19
00062245: 44 72
00062246: 01 FA
00062247: 00 FF
00062248: 00 90
0006236A: E3 DA
0006236B: FE 71
0006236C: FF FA
000623F7: 56 6D
000623F8: FE 71
000623F9: FF FA

@ -0,0 +1,40 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=808
for (i = 0; i < num_items; i++)
{
ridx = reagent_idx[i];
sz = reagent_quantity[ridx]; // used quantity
if (sz <= 0) continue;
reag = reagent[ridx];
if (reag->flags.PRESERVE_REAGENT) continue;
rsz = items[i]->getTotalDimension();
<<<<<<<<
if (reag->flags3.ANY_RAW_MATERIAL)
rsz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(rsz))
========
/* Not in patch, but necessary for full correctness:
if (reag->flags3.ANY_RAW_MATERIAL)
rsz /= BASE_SIZE(items[i]->getType());
*/
if (reag->flags3.ANY_RAW_MATERIAL)
sz *= BASE_SIZE(items[i]->getType());
if (items[i]->subtractDimension(sz))
>>>>>>>>
destroy_item(items[i]);
reagent_quantity[ridx] -= rsz
if (reagent_quantity[ridx] < 0)
reagent_quantity[ridx] = 0;
}
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
0087F7EF: F8 D8
0087F86F: F8 D8
0087F9AD: C7 C3
0087F9ED: C7 C3

@ -0,0 +1,83 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
Original code:
.text:087AC378 cmp edx, eax
.text:087AC37A mov [esp+4Ch], eax
.text:087AC37E jnb loc_87A7034
.text:087AC384 mov [esp+48h], edx
.text:087AC388 mov [esp+54h], ebx
...
.text:087AC440 add dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D ja loc_87AC38C
Patch:
0x76437a
89 44 24 4C
89 54 24 4C
.text:087AC37A mov [esp+4Ch], edx
0x764384
89 54 24 48 89 5C 24 54
E8 8A 51 8A FF 90 90 90
.text:087AC384 call sub_8051513
.text:087AC389 nop
.text:087AC38A nop
.text:087AC38B nop
0x764440
83 44 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 87 39 FF FF FF
83 6C 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 86 39 FF FF FF
.text:087AC440 sub dword ptr [esp+48h], 4
.text:087AC445 mov ebp, [esp+48h]
.text:087AC449 cmp [esp+4Ch], ebp
.text:087AC44D jbe loc_87AC38C
0x9513
90 90 90 90 90 90 90 90 90 90 90 90 90
83 E8 04 89 44 24 4C 89 5C 24 58 C3 90
.text:08051513 sub_8051513 proc near
.text:08051513 sub eax, 4
.text:08051516 mov [esp+4Ch], eax ; 48h
.text:0805151A mov [esp+58h], ebx ; 54h
.text:0805151E retn
.text:0805151E sub_8051513 endp
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009513: 90 83
00009514: 90 E8
00009515: 90 04
00009516: 90 89
00009517: 90 44
00009518: 90 24
00009519: 90 4C
0000951A: 90 89
0000951B: 90 5C
0000951C: 90 24
0000951D: 90 58
0000951E: 90 C3
0076437B: 44 54
00764384: 89 E8
00764385: 54 8A
00764386: 24 51
00764387: 48 8A
00764388: 89 FF
00764389: 5C 90
0076438A: 24 90
0076438B: 54 90
00764441: 44 6C
0076444E: 87 86

@ -0,0 +1,139 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994
0x7643f8
F6 46 0C 01 74 42
E9 B6 50 8A FF 90
.text:087AC3F8 jmp loc_80514B3 ; << CAVE
.text:087AC3FD nop
.text:087AC3FE loc_87AC3FE:
0x94b3
90 90 90 90 90 90 90 90 90 90 90 90 90
F6 46 0C 01 75 0A E9 82 AF 75 00 90 90
.text:080514B3 loc_80514B3:
.text:080514B3 test byte ptr [esi+0Ch], 1
.text:080514B7 jnz short loc_80514C3
.text:080514B9 coord_test_jfail:
.text:080514B9 jmp loc_87AC440
0x94c3
90 90 90 90 90 90 90 90 90 90 90 90 90
8D 9C 24 60 03 00 00 0F BF 03 EB 07 90
.text:080514C3 loc_80514C3:
.text:080514C3 lea ebx, [esp+360h]
.text:080514CA movsx eax, word ptr [ebx] ; job_z
.text:080514CD jmp short loc_80514D6
0x94d6
90 90 90 90 90 90 90 90 90 90
66 3B 46 08 75 DD EB 05 90 90
.text:080514D6 loc_80514D6:
.text:080514D6 cmp ax, [esi+8] ; item->pos.z
.text:080514DA jnz short coord_test_jfail
.text:080514DC jmp short loc_80514E3
0x94e3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 10 66 3B 46 04 75 CC EB 04 90
.text:080514E3 loc_80514E3:
.text:080514E3 movsx eax, word ptr [ebx+10h] ; job_x
.text:080514E7 cmp ax, [esi+4] ; item->pos.x
.text:080514EB jnz short coord_test_jfail
.text:080514ED jmp short loc_80514F3
0x94f3
90 90 90 90 90 90 90 90 90 90 90 90 90
0F BF 43 20 66 3B 46 06 75 BC EB 04 90
.text:080514F3 loc_80514F3:
.text:080514F3 movsx eax, word ptr [ebx+20h] ; job_y
.text:080514F7 cmp ax, [esi+6] ; item->pos.y
.text:080514FB jnz short coord_test_jfail
.text:080514FD jmp short loc_8051503
0x9503
90 90 90 90 90 90 90 90 90 90 90 90 90
E9 F6 AE 75 00 90 90 90 90 90 90 90 90
.text:08051503 loc_8051503:
.text:08051503 jmp loc_87AC3FE
You can use this script to apply the generated patch below:
http://stalkr.net/files/ida/idadif.py
----8<----
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000094B3: 90 F6
000094B4: 90 46
000094B5: 90 0C
000094B6: 90 01
000094B7: 90 75
000094B8: 90 0A
000094B9: 90 E9
000094BA: 90 82
000094BB: 90 AF
000094BC: 90 75
000094BD: 90 00
000094C3: 90 8D
000094C4: 90 9C
000094C5: 90 24
000094C6: 90 60
000094C7: 90 03
000094C8: 90 00
000094C9: 90 00
000094CA: 90 0F
000094CB: 90 BF
000094CC: 90 03
000094CD: 90 EB
000094CE: 90 07
000094D6: 90 66
000094D7: 90 3B
000094D8: 90 46
000094D9: 90 08
000094DA: 90 75
000094DB: 90 DD
000094DC: 90 EB
000094DD: 90 05
000094E3: 90 0F
000094E4: 90 BF
000094E5: 90 43
000094E6: 90 10
000094E7: 90 66
000094E8: 90 3B
000094E9: 90 46
000094EA: 90 04
000094EB: 90 75
000094EC: 90 CC
000094ED: 90 EB
000094EE: 90 04
000094F3: 90 0F
000094F4: 90 BF
000094F5: 90 43
000094F6: 90 20
000094F7: 90 66
000094F8: 90 3B
000094F9: 90 46
000094FA: 90 06
000094FB: 90 75
000094FC: 90 BC
000094FD: 90 EB
000094FE: 90 04
00009503: 90 E9
00009504: 90 F6
00009505: 90 AE
00009506: 90 75
00009507: 90 00
007643F8: F6 E9
007643F9: 46 B6
007643FA: 0C 50
007643FB: 01 8A
007643FC: 74 FF
007643FD: 42 90

@ -0,0 +1,60 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406
1. Include store in hospital jobs when recomputing counters
0x746d7
75 D7
90 90
<<<<
.text:080BC6D7 jnz short loc_80BC6B0
====
.text:080BC6D7 nop
.text:080BC6D8 nop
>>>>
- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue;
+ // NOP
This reference points to the containers, not the hospital civzone.
Since fixing this properly is too hard for a patch, just remove the
check. Most people have only one hospital anyway, and it is better
to err on the side of caution here.
2. Make the stockpiling code increment the right stock counters
0x67cb0e
0B 04 90
8B 1C 90
0x67cb18
8B 40 74
8B 43 74
<<<<
.text:086C4B0E mov eax, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [eax+74h]
====
.text:086C4B0E mov ebx, [eax+edx*4]
.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4]
.text:086C4B18 mov eax, [ebx+74h]
>>>>
- id = civzones[i]->children[child_idx[i]]->id
+ cur_civzone = civzones[i] // existing var from previous loop
+ id = cur_civzone->children[child_idx[i]]->id
The reason being, later code uses that var (at this point containing
useless data) to increment counters and amounts in the hospital.
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
000746D7: 75 90
000746D8: D7 90
0067CB0F: 04 1C
0067CB19: 40 43

@ -0,0 +1,85 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530
0x9508
90 90 90 90 90 90 90 90
E9 13 76 9C 00 90 90 90
.text:08051508 loc_8051508:
.text:08051508 jmp sub_8A18B20
0x9523
90 90 90 90 90 90 90 90 90 90 90 90 90
50 8B 03 53 FF 90 34 02 00 00 EB 09 90
.text:08051523 sub_8051523:
.text:08051523 push eax
.text:08051524 mov eax, [ebx]
.text:08051526 push ebx
.text:08051527 call dword ptr [eax+234h]
.text:0805152D jmp short loc_8051538
0x9538
90 90 90 90 90 90 90 90
29 44 24 64 5B 58 EB C8
.text:08051538 loc_8051538:
.text:08051538 sub [esp+64h], eax
.text:0805153C pop ebx
.text:0805153D pop eax
.text:0805153E jmp short loc_8051508
0xa5cdd0
29 44 24 58
90 90 90 90
.text:08AA4DD0 nop
.text:08AA4DD1 nop
.text:08AA4DD2 nop
.text:08AA4DD3 nop
0xa5e2c3
E8 58 28 F7 FF
E8 5B B2 5A FF
.text:08AA62C3 call sub_8051523
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
00009508: 90 E9
00009509: 90 13
0000950A: 90 76
0000950B: 90 9C
0000950C: 90 00
00009523: 90 50
00009524: 90 8B
00009525: 90 03
00009526: 90 53
00009527: 90 FF
00009529: 90 34
0000952A: 90 02
0000952B: 90 00
0000952C: 90 00
0000952D: 90 EB
0000952E: 90 09
00009538: 90 29
00009539: 90 44
0000953A: 90 24
0000953B: 90 64
0000953C: 90 5B
0000953D: 90 58
0000953E: 90 EB
0000953F: 90 C8
00A5CDD0: 29 90
00A5CDD1: 44 90
00A5CDD2: 24 90
00A5CDD3: 58 90
00A5E2C4: 58 5B
00A5E2C5: 28 B2
00A5E2C6: F7 5A

@ -0,0 +1,45 @@
http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
Fix use of uninitialized variables to stop auto-unassigning racks:
0x7ee948
8B 7C 24 3C
89 C7 90 90
.text:08836948 mov edi, eax
.text:0883694A nop
.text:0883694B nop
0x7eea2f
8B 7C 24 3C
89 C7 90 90
.text:08836A2F mov edi, eax
.text:08836A31 nop
.text:08836A32 nop
basically:
b_squad_id = building->getSpecificSquad();
- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4))
+ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4))
unassign(building);
the reason being, some_other_squad_id contains irrelevant garbage at this point
---8<---
This difference file is created by The Interactive Disassembler
Dwarf_Fortress
007EE948: 8B 89
007EE949: 7C C7
007EE94A: 24 90
007EE94B: 3C 90
007EEA2F: 8B 89
007EEA30: 7C C7
007EEA31: 24 90
007EEA32: 3C 90

@ -117,12 +117,14 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(regrass regrass.cpp)
DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp)
DFHACK_PLUGIN(manipulator manipulator.cpp) DFHACK_PLUGIN(manipulator manipulator.cpp)
DFHACK_PLUGIN(search search.cpp)
# this one exports functions to lua # this one exports functions to lua
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(steam-engine steam-engine.cpp) DFHACK_PLUGIN(steam-engine steam-engine.cpp)
DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(reactionhooks reactionhooks.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(add-spatter add-spatter.cpp) DFHACK_PLUGIN(add-spatter add-spatter.cpp)
DFHACK_PLUGIN(fix-armory fix-armory.cpp) DFHACK_PLUGIN(fix-armory fix-armory.cpp)
# not yet. busy with other crud again... # not yet. busy with other crud again...

@ -1,5 +1,5 @@
include_directories(include) include_directories(include)
include_directories("${dfhack_SOURCE_DIR}/library/depends/tthread")
FILE(GLOB DFUSION_CPPS src/*.c*) FILE(GLOB DFUSION_CPPS src/*.c*)
set( set(
DFUSION_CPPS_ALL DFUSION_CPPS_ALL
@ -9,6 +9,3 @@ set(
FILE(GLOB DFUSION_HS include/*) FILE(GLOB DFUSION_HS include/*)
SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE ) SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE )
DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread) DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread)
# installs into DF root
install(DIRECTORY luafiles/ DESTINATION dfusion)

@ -6,18 +6,11 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "tinythread.h"
#include "luamain.h" #include "luamain.h"
#include "lua_Process.h" #include "lua_Process.h"
#include "lua_Hexsearch.h" #include "lua_Hexsearch.h"
#include "lua_Misc.h" #include "lua_Misc.h"
#include "lua_VersionInfo.h"
#include "functioncall.h"
#include "lua_FunctionCall.h"
#include "lua_Offsets.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "LuaTools.h" #include "LuaTools.h"
@ -25,177 +18,71 @@ using std::vector;
using std::string; using std::string;
using namespace DFHack; using namespace DFHack;
static tthread::mutex* mymutex=0;
static tthread::thread* thread_dfusion=0;
uint64_t timeLast=0;
DFHACK_PLUGIN("dfusion")
command_result dfusion (color_ostream &out, std::vector <std::string> &parameters);
command_result dfuse (color_ostream &out, std::vector <std::string> &parameters);
command_result lua_run (color_ostream &out, std::vector <std::string> &parameters);
command_result lua_run_file (color_ostream &out, std::vector <std::string> &parameters);
DFhackCExport const char * plugin_name ( void )
{
return "dfusion";
}
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
lua::state st=lua::glua::Get();
//maybe remake it to run automaticaly
Lua::Open(out, st);
lua::RegisterProcess(st);
lua::RegisterHexsearch(st);
lua::RegisterMisc(st);
lua::RegisterVersionInfo(st);
lua::RegisterFunctionCall(st);
lua::RegisterEngine(st);
#ifdef LINUX_BUILD
st.push(1);
st.setglobal("LINUX");
#else
st.push(1);
st.setglobal("WINDOWS");
#endif
commands.push_back(PluginCommand("dfusion","Run dfusion system (interactive i.e. can input further commands).",dfusion,true));
commands.push_back(PluginCommand("dfuse","Init dfusion system (not interactive).",dfuse,false));
commands.push_back(PluginCommand("lua", "Run interactive interpreter. Use 'lua <filename>' to run <filename> instead.",lua_run,true));
commands.push_back(PluginCommand("runlua", "Run non-interactive interpreter. Use 'runlua <filename>' to run <filename>.",lua_run_file,false));
mymutex=new tthread::mutex;
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( Core * c )
{
// shutdown stuff DFHACK_PLUGIN("dfusion")
if(thread_dfusion)
delete thread_dfusion;
delete mymutex;
return CR_OK;
}
DFhackCExport command_result plugin_onupdate_DISABLED ( Core * c ) static int loadObjectFile(lua_State* L)
{ {
uint64_t time2 = GetTimeMs64(); std::string path;
uint64_t delta = time2-timeLast;
if(delta<100) path=luaL_checkstring(L,1);
return CR_OK;
timeLast = time2; OutFile::File f(path);
mymutex->lock(); lua_newtable(L);
lua::state s=lua::glua::Get(); int table_pos=lua_gettop(L);
s.getglobal("OnTick"); size_t size=f.GetTextSize();
if(s.is<lua::function>()) Lua::Push(L,size);
{ lua_setfield(L,table_pos,"data_size");
try{ char* buf=new char[size];
s.pcall(); f.GetText(buf);
}
catch(lua::exception &e) //Lua::PushDFObject(L,DFHack::,buf);
{ //Lua::Push(L,buf);
c->getConsole().printerr("Error OnTick:%s\n",e.what()); lua_pushlightuserdata(L,buf);
c->getConsole().printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str()); lua_setfield(L,table_pos,"data");
c->getConsole().msleep(1000); const OutFile::vSymbol &symbols=f.GetSymbols();
} lua_newtable(L);
} for(size_t i=0;i<symbols.size();i++)
s.settop(0); {
mymutex->unlock(); Lua::Push(L,i);
return CR_OK; lua_newtable(L);
} Lua::Push(L,symbols[i].name);
command_result lua_run_file (color_ostream &out, std::vector <std::string> &parameters) lua_setfield(L,-2,"name");
{ Lua::Push(L,symbols[i].pos);
if(parameters.size()==0) lua_setfield(L,-2,"pos");
{
out.printerr("runlua without file to run!");
return CR_FAILURE; lua_settable(L,-3);
} }
return lua_run(out,parameters); lua_setfield(L,table_pos,"symbols");
return 1;
} }
command_result lua_run (color_ostream &out, std::vector <std::string> &parameters) static int markAsExecutable(lua_State* L)
{ {
if (!parameters.empty()) 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 (parameters[0] == "--core-context") if(ranges[i].isInRange((void*)addr))
{
Lua::InterpreterLoop(out, Lua::Core::State, "core lua");
return CR_OK;
}
else if (parameters[0] == "--core-reload")
{ {
CoreSuspender suspend; DFHack::t_memrange newperm=ranges[i];
newperm.execute=true;
for (size_t i = 1; i < parameters.size(); i++) DFHack::Core::getInstance().p->setPermisions(ranges[i],newperm);
{ return 0;
lua_getglobal(Lua::Core::State, "reload");
lua_pushstring(Lua::Core::State, parameters[i].c_str());
Lua::SafeCall(out, Lua::Core::State, 1, 0);
}
return CR_OK;
} }
} }
lua_pushlstring(L,"Memory range not found",23);
mymutex->lock(); lua_error(L);
lua::state s=lua::glua::Get(); return 0;
if(parameters.size()>0)
{
try{
s.loadfile(parameters[0]); //load file
for(size_t i=1;i<parameters.size();i++)
s.push(parameters[i]);
Lua::SafeCall(out, s, parameters.size()-1,0);// run it
}
catch(lua::exception &e)
{
out.printerr("Error:%s\n",e.what());
out.printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str());
}
}
else
{
Lua::InterpreterLoop(out, s);
}
s.settop(0);// clean up
mymutex->unlock();
return CR_OK;
}
void RunDfusion(color_ostream &out, std::vector <std::string> &parameters)
{
mymutex->lock();
lua::state s=lua::glua::Get();
try{
s.loadfile("dfusion/init.lua"); //load script
for(size_t i=0;i<parameters.size();i++)
s.push(parameters[i]);
Lua::SafeCall(out, s, parameters.size(),0);
}
catch(lua::exception &e)
{
out.printerr("Error:%s\n",e.what());
out.printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str());
}
s.settop(0);// clean up
mymutex->unlock();
} }
command_result dfuse(color_ostream &out, std::vector <std::string> &parameters) 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)
{ {
lua::state s=lua::glua::Get(); return CR_OK;
s.push(1); }
s.setglobal("INIT");
RunDfusion(out,parameters);
return CR_OK;
}
command_result dfusion (color_ostream &out, std::vector <std::string> &parameters)
{
lua::state s=lua::glua::Get();
s.push();
s.setglobal("INIT");
RunDfusion(out,parameters);
return CR_OK;
}

@ -105,7 +105,7 @@ public:
void GetText(char *ptr); void GetText(char *ptr);
size_t GetTextSize(); size_t GetTextSize();
void LoadSymbols(); void LoadSymbols();
vSymbol GetSymbols(){LoadSymbols();return symbols;}; const vSymbol& GetSymbols(){LoadSymbols();return symbols;};
void PrintSymbols(); void PrintSymbols();
void PrintRelocations(); void PrintRelocations();
protected: protected:

@ -1,26 +0,0 @@
#ifndef FUNCTIONCALL__H
#define FUNCTIONCALL__H
#include <vector>
using std::vector;
using std::size_t;
class FunctionCaller
{
public:
enum callconv
{
STD_CALL, //__stdcall - all in stack
FAST_CALL, //__fastcall - as much in registers as fits
THIS_CALL, //__thiscall - eax ptr to this, rest in stack
CDECL_CALL //__cdecl - same as stdcall but no stack realign
};
FunctionCaller(size_t base):base_(base){};
int CallFunction(size_t func_ptr,callconv conv,const vector<int> &arguments);
private:
int CallF(size_t count,callconv conv,void* f,const vector<int> &arguments);
size_t base_;
};
#endif //FUNCTIONCALL__H

@ -1,14 +0,0 @@
#ifndef LUA_FUNCTIONCALL__H
#define LUA_FUNCTIONCALL__H
#include "luamain.h"
#include "functioncall.h"
namespace lua
{
void RegisterFunctionCall(lua::state &st);
}
#endif

@ -7,7 +7,6 @@
#include <MemAccess.h> #include <MemAccess.h>
#include "luamain.h" #include "luamain.h"
#include "OutFile.h" #include "OutFile.h"
#include "functioncall.h"
#include "LuaTools.h" #include "LuaTools.h"
namespace lua namespace lua

@ -1,13 +0,0 @@
#ifndef LUA_OFFSETS_H
#define LUA_OFFSETS_H
#include "luamain.h"
namespace lua
{
void RegisterEngine(lua::state &st);
}
#endif

@ -1,12 +0,0 @@
#ifndef LUA_VERSIONINFO_H
#define LUA_VERSIONINFO_H
#include "Core.h"
#include <VersionInfo.h>
#include "luamain.h"
namespace lua
{
void RegisterVersionInfo(lua::state &st);
}
#endif

@ -1,133 +0,0 @@
adv_tools= {}
adv_tools.menu=MakeMenu()
--TODO make every tool generic (work for both modes)
function adv_tools.reincarnate(swap_soul) --only for adventurer i guess
if swap_soul==nil then
swap_soul=true
end
local adv=df.global.world.units.active[0]
if adv.flags1.dead==false then
error("You are not dead (yet)!")
end
local hist_fig=getNemesis(adv).figure
if hist_fig==nil then
error("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].hfid==hist_fig.id then
--print("Is same id:"..i)
trg_hist_fig=events[i].slayer
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
error("Slayer not found")
end
local trg_unit=trg_hist_fig.unit_id
if trg_unit==nil then
error("Unit id not found!")
end
local trg_unit_final=df.unit.find(trg_unit)
tools.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
adv_tools.menu:add("Reincarnate",adv_tools.reincarnate)
function adv_tools.ressurect()
v2=engine.peek(vector:getval(indx),ptr_Creature.hurt1)
for i=0,v2:size()-1 do
v2:setval(i,0)
end
v2=engine.peek(vector:getval(indx),ptr_Creature.hurt2)
v2.type=DWORD
for i=0,v2:size()-1 do
v2:setval(i,0)
end
engine.poke(vector:getval(indx),ptr_Creature.bloodlvl,60000) --give blood
engine.poke(vector:getval(indx),ptr_Creature.bleedlvl,0) --stop some bleeding...
local flg=engine.peek(vector:getval(indx),ptr_Creature.flags)
flg:set(1,false) --ALIVE
flg:set(39,false) -- leave body yet again
flg:set(37,false) -- something todo with wounds- lets you walk again.
flg:set(58,true) -- makes them able to breathe
flg:set(61,true) -- gives them sight
engine.poke(vector:getval(indx),ptr_Creature.flags,flg)
end
function adv_tools.wagonmode() --by rumrusher
--first three lines same as before (because we will need an offset of creature at location x,y,z)
myoff=offsets.getEx("AdvCreatureVec")
vector=engine.peek(myoff,ptr_vector)
indx=GetCreatureAtPos(getxyz())
--indx=0
--print(string.format("%x",vector:getval(indx)))
flg=engine.peek(vector:getval(indx),ptr_Creature.flags) --get flags
flg:set(1,false)
flg:set(74,false)
engine.poke(vector:getval(indx),ptr_Creature.flags,flg)
print("To stay normal press y, else hit Enter turn Wagon mode on.")
r=io.stdin:read() -- repeat for it too work... also creature will be dead.
if r== "y" then
flg=engine.peek(vector:getval(indx),ptr_Creature.flags)
flg:set(1,false)
engine.poke(vector:getval(indx),ptr_Creature.flags,flg)
else
flg=engine.peek(vector:getval(indx),ptr_Creature.flags)
flg:set(1,false)
flg:flip(74)
engine.poke(vector:getval(indx),ptr_Creature.flags,flg)
end
end
function selectall()
local retvec={} --return vector (or a list)
myoff=offsets.getEx("AdvCreatureVec")
vector=engine.peek(myoff,ptr_vector) --standart start
for i=0,vector:size()-1 do --check all creatures
local off
off=vector:getval(i)
local flags=engine.peek(off,ptr_Creature.flags)
if flags:get(1)==true then --if dead ...
table.insert(retvec,off)--... add it to return vector
end
end
return retvec --return the "return vector" :)
end
function adv_tools.hostilate()
vector=engine.peek(offsets.getEx("AdvCreatureVec"),ptr_vector)
id=GetCreatureAtPos(getxyz())
print(string.format("Vec:%d cr:%d",vector:size(),id))
off=vector:getval(id)
crciv=engine.peek(vector:getval(id),ptr_Creature.civ)
curciv=engine.peek(vector:getval(0),ptr_Creature.civ)
if curciv==crciv then
print("Friendly-making enemy")
engine.poke(off,ptr_Creature.civ,-1)
flg=engine.peek(off,ptr_Creature.flags)
flg:set(17,true)
engine.poke(off,ptr_Creature.flags,flg)
else
print("Enemy- making friendly")
engine.poke(off,ptr_Creature.civ,curciv)
flg=engine.peek(off,ptr_Creature.flags)
flg:set(17,false)
flg:set(19,false)
engine.poke(off,ptr_Creature.flags,flg)
end
end

@ -1,3 +0,0 @@
if not(FILE) then
adv_tools.menu:display()
end

@ -1,24 +0,0 @@
ptr_building={}
ptr_building.RTI={off=0,rtype=DWORD}
ptr_building.xs={off=4,rtype=DWORD}
ptr_building.ys={off=6,rtype=DWORD}
ptr_building.zs={off=8,rtype=DWORD}
ptr_building.xe={off=12,rtype=DWORD}
ptr_building.ye={off=16,rtype=DWORD}
ptr_building.ze={off=20,rtype=DWORD}
ptr_building.flags={off=24,rtype=ptt_dfflag.new(4)}
ptr_building.materials={off=28,rtype=DWORD}
ptr_building.builditems={off=228,rtype=ptr_vector}
function ptr_building.getname(self,RTI)
if RTI == nil then
return string.sub(RTTI_GetName(self.RTI),5,-3)
else
return string.sub(RTTI_GetName(RTI),5,-3)
end
end
ptr_subbuilding={}
ptr_subbuilding["building_trapst"]={}
ptr_subbuilding["building_trapst"].state={off=250,rtype=DWORD} -- atleast lever has this
ptr_subbuilding["building_doorst"]={}
ptr_subbuilding["building_doorst"].flg={off=248,rtype=WORD} --maybe flags?
ptr_subbuilding["building_doorst"].state={off=250,rtype=DWORD}

@ -1,548 +0,0 @@
dofile("dfusion/offsets_misc.lua")
STD_STRING=0
DWORD=1
WORD=2
BYTE=3
QWORD=4
DOUBLE=5
FLOAT=6
getline=function (inp)
return dfhack.lineedit(inp or "")
end
io.stdin=nil
function printd(...)
if DEBUG then
print(...)
end
end
function GetTextRegion()
if __TEXT ~=nil then --Chache this, not going to change.
return __TEXT
end
ranges__=Process.getMemRanges()
--print("Ranges:"..#ranges__)
for k,v in pairs(ranges__) do
for k2,v2 in pairs(v) do
--print(string.format("%d %s->%s",k,tostring(k2),tostring(v2)))
end
--local num
--flgs=""
--if(v["read"])then flgs=flgs..'r' end
--if(v["write"])then flgs=flgs..'w' end
--if(v["execute"]) then flgs=flgs..'e' end
--if num>=100 then
--print(string.format("%d %x->%x %s %s",k,v["start"],v["end"],v.name or "",flgs))
--end
local pos=string.find(v.name,"Dwarf Fortress.exe") or string.find(v.name,"libs/Dwarf_Fortress")
if(pos~=nil) and v["execute"] then
__TEXT=v;
return v;
end
end
error(".Text region not found!")
end
function UpdateRanges()
ranges__=Process.getMemRanges()
end
function GetRegionIn(pos)
ranges__=ranges__ or Process.getMemRanges()
for k,v in pairs(ranges__) do
if pos>=v.start and pos<v["end"] then
return v
end
end
return nil
end
function GetRegionIn2(pos)
ranges__=ranges__ or Process.getMemRanges()
local cr=nil
for k,v in pairs(ranges__) do
--for k2,v2 in pairs(v) do
-- print(string.format("%d %s->%s",k,tostring(k2),tostring(v2)))
--end
--local num
--num=0
--if(v["read"])then num=num+1 end
--if(v["write"])then num=num+10 end
--if(v["execute"]) then num=num+100 end
--print(string.format("%d %x->%x %s %x",k,v["start"],v["end"],v.name,pos))
if pos>=v.start then --This is a hack to counter .text region suddenly shrinking.
if cr~=nil then
if v.start < cr.start then -- find region that start is closest
cr=v
end
else
cr=v
end
end
end
return cr
end
function ValidOffset(pos)
ranges__=ranges__ or Process.getMemRanges()
return GetRegionIn(pos)~=nil
end
function unlockDF()
local reg=GetTextRegion()
reg["write"]=true
Process.setPermisions(reg,reg)
end
function lockDF()
local reg=GetTextRegion()
reg["write"]=false
Process.setPermisions(reg,reg)
end
function SetExecute(pos)
UpdateRanges()
local reg=GetRegionIn(pos)
reg.execute=true
reg["write"]=true
Process.setPermisions(reg,reg) -- TODO maybe make a page with only execute permisions or sth
end
-- engine bindings
engine=engine or {}
--[=[ use default peek/pokes for now
engine.peekd=Process.readDWord
engine.poked=Process.writeDWord
engine.peekb=Process.readByte
engine.pokeb=Process.writeByte
engine.peekw=Process.readWord
engine.pokew=Process.writeWord
engine.peekstr_stl=Process.readSTLString
engine.pokestr_stl=Process.writeSTLString
engine.peekstr=Process.readCString
--engine.pokestr=Process.readCString
engine.peekarb=Process.read
engine.pokearb=Process.write
--]=]
function engine.peek(offset,rtype)
if type(rtype)=="table" then
if rtype.off ==nil then
return engine.peekpattern(offset,rtype)
else
return engine.peek(rtype.off+offset,rtype.rtype)
end
end
if rtype==STD_STRING then
return engine.peekstr2(offset)
elseif rtype==DWORD then
return engine.peekd(offset)
elseif rtype==WORD then
return engine.peekw(offset)
elseif rtype==BYTE then
return engine.peekb(offset)
elseif rtype==QWORD then
return engine.peekq(offset)
elseif rtype==FLOAT then
return engine.peekfloat(offset)
elseif rtype==DOUBLE then
return engine.peekdouble(offset)
else
error("Invalid peek type")
return
end
end
function engine.poke(offset,rtype,val)
if type(rtype)=="table" then
if rtype.off ==nil then
return engine.pokepattern(offset,rtype,val)
else
return engine.poke(rtype.off+offset,rtype.rtype,val)
end
end
if rtype==STD_STRING then
return engine.pokestr2(offset,val)
elseif rtype==DWORD then
return engine.poked(offset,val)
elseif rtype==WORD then
return engine.pokew(offset,val)
elseif rtype==BYTE then
return engine.pokeb(offset,val)
elseif rtype==QWORD then
return engine.pokeq(offset,val)
elseif rtype==FLOAT then
return engine.pokefloat(offset,val)
elseif rtype==DOUBLE then
return engine.pokedouble(offset,val)
else
error("Invalid poke type:"..tostring(rtype))
return
end
end
function engine.sizeof(rtype)
if rtype==STD_STRING then
error("String has no constant size")
return
elseif rtype==DWORD then
return 4;
elseif rtype==WORD then
return 2;
elseif rtype==BYTE then
return 1;
else
error("Invalid sizeof type")
return
end
end
function engine.peekpattern(offset,pattern)
local ret={}
for k,v in pairs(pattern) do
--print("k:"..k.." v:"..type(v))
if type(v)=="table" then
ret[k]=engine.peek(offset+v.off,v.rtype)
--print(k.." peeked:"..offset+v.off)
else
ret[k]=v
end
end
ret.__offset=offset
return ret
end
function engine.pokepattern(offset,pattern,val)
for k,v in pairs(pattern) do
--print("k:"..k.." v:"..type(v))
if type(v)=="table" then
engine.poke(offset+v.off,v.rtype,val[k])
end
end
end
function engine.LoadModData(file)
local T2={}
T2.symbols={}
T2.data,T2.size=engine.loadobj(file)
data,modsize=engine.loadobj(file)
local T=engine.loadobjsymbols(file)
for k,v in pairs(T) do
if v.pos~=0 then
T2.symbols[v.name]=v.pos
end
end
return T2
end
function engine.FindMarkerCall(moddata,name)
if moddata.symbols[name] ~=nil then
return moddata.symbols[name]+1
end
end
function engine.FindMarker(moddata,name)
if moddata.symbols[name] ~=nil then
return engine.findmarker(0xDEADBEEF,moddata.data,moddata.size,moddata.symbols[name])
end
end
function engine.installMod(file,name,bonussize)
local T=engine.LoadModData(file)
local modpos,modsize=engine.loadmod(file,name,bonussize)
T.pos=modpos
return T
end
it_menu={}
it_menu.__index=it_menu
function it_menu:add(name,func)
table.insert(self.items,{func,name})
end
function it_menu: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=getline("")
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("incorrect choice")
end
until ans~=nil and (ans<=#self.items and ans>0)
self.items[ans][1]()
end
function MakeMenu()
local ret={}
ret.items={}
setmetatable(ret,it_menu)
return ret
end
function PrintPattern(loadedpattern)
for k,v in pairs(loadedpattern) do
if type(v)== "string" then
print(k.." "..v)
else
print(string.format("%s %d inhex:%x",k,v,v))
end
end
end
function printPattern(pattern)
local i=0;
local names={}
names[STD_STRING]="std_string (STD_STRING)"
names[DWORD]= "Double word (DWORD)"
names[WORD]= "Word (WORD)"
names[STD_STRING]="Byte (BYTE)"
ret={}
for k,v in pairs(pattern) do
if type(v)=="table" and v.off~=nil then
if names[v.rtype]~=nil then
lname=names[v.rtype]
else
if type(v.rtype)=="table" then
lname="Table (prob subpattern)"
else
lname="Other"
end
end
print(string.format("%d. %s is %s with offset %x",i,k,lname,v.off))
table.insert(ret,k)
else
print(string.format("%d. %s",i,k))
end
i=i+1
end
return ret;
end
function editPattern(offset,pattern,name)
if type(pattern[name].rtype)=="table" then
if pattern[name].rtype.setval~=nil then
print(string.format("%x",offset+pattern[name].off))
local t=engine.peek(offset+pattern[name].off,pattern[name].rtype)
print("Value is now:"..t:getval())
print("Enter new value:")
val=io.stdin:read()
t:setval(val)
else
ModPattern(offset+pattern[name].off,pattern[name].rtype)
end
return
end
val=engine.peek(offset,pattern[name])
print("Value is now:"..val)
print("Enter new value:")
if pattern[name].rtype==STD_STRING then
val=io.stdin:read()
else
val=tonumber(io.stdin:read())
end
engine.poke(offset,pattern[name],val)
end
function ModPattern(itemoffset,pattern)
print("Select what to edit:")
nm=printPattern(pattern)
q=tonumber(io.stdin:read())
if q~=nil and q<#nm then
editPattern(itemoffset,pattern,nm[q+1])
end
end
function findVectors()
if __VECTORS ~=nil then --chache
return __VECTORS
end
local text=GetTextRegion()
local h=hexsearch(text.start,text["end"],0x8b,ANYBYTE,ANYDWORD,0x8b,ANYBYTE,ANYDWORD,0x2b)
local pos=h:findall()
local T={}
for k,v in pairs(pos) do
local loc1,loc2
loc1=engine.peekd(v+2)
loc2=engine.peekd(v+8)
--print(string.format("%x - %x=%x",loc1,loc2,loc1-loc2))
if(loc1-loc2==4) then
if T[loc1-4]~=nil then
T[loc1-4]=T[loc1-4]+1
else
T[loc1-4]=1
end
end
end
__VECTORS=T
return T
end
function GetRaceToken(p) --actually gets token...
local vec=df.global.world.raws.creatures.all
return vec[p].creature_id
end
function BuildNameTable()
local rtbl={}
local vec=df.global.world.raws.creatures.all
--print(string.format("Vector start:%x",vec.st))
--print(string.format("Vector end:%x",vec.en))
--print("Creature count:"..vec.size)
for k=0,#vec-1 do
local name=vec[k].creature_id
--print(k.." "..tostring(name))
rtbl[name]=k
end
return rtbl;
end
function BuildMaterialTable()
local rtbl={}
local vec=engine.peek(offsets.getEx('Materials'),ptr_vector)
--print(string.format("Vector start:%x",vec.st))
--print(string.format("Vector end:%x",vec.en))
--local i=0
for p=0,vec:size()-1 do
local off=vec:getval(p)
--print("First member:"..off)
local name=engine.peek(off,ptt_dfstring)
--print("Loading:"..p.."="..name:getval())
rtbl[name:getval()]=p
--i=i+1
--if i>100 then
-- io.stdin:read()
-- i=0
--end
end
return rtbl;
end
function BuildWordTables()
local names={}
local rnames={}
local vector=engine.peek(offsets.getEx('WordVec'),ptr_vector)
for i =0,vector:size()-1 do
local off=vector:getval(i)
local n=engine.peekstr(off)
names[i]=n
rnames[n]=i
end
return names,rnames
end
function ParseScript(file)
io.input(file)
f=""
first=0
nobraces=0
function updFunction()
if f~="" then
first=0
if nobraces==0 then
f=f.."}"
end
nobraces=0
print("Doing:"..f)
assert(loadstring(f))()
f=""
end
end
while true do
local line = io.read("*line")
if line == nil then break end
if string.sub(line,1,2)==">>" then
updFunction()
if string.find(line,"%b()") then
f=string.sub(line,3)
nobraces=1
else
f=string.sub(line,3).."{"
end
--print(string.sub(line,3)..)
else
if first~=0 then
f=f..","
else
first=1
end
f=f..string.format('%q',line)
end
end
updFunction()
end
function ParseNames(path)
local ret={}
local ff=io.open(path)
for n in ff:lines() do
table.insert(ret,n)
end
return ret
end
function getSelectedUnit()
if df.global.ui.main.mode~=23 then
return nil
end
local unit_indx=df.global.ui_selected_unit
if unit_indx<#df.global.world.units.active-1 then
return df.global.world.units.active[unit_indx]
else
return nil
end
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function getCreatureAtPointer()
return getCreatureAtPos(getxyz())
end
function getCreature()
local unit=getSelectedUnit()
if unit==nil then
unit=getCreatureAtPointer()
end
--any other selection methods...
return unit
end
function getNemesisId(unit)
for k,v in pairs(unit.refs) do
if df.general_ref_is_nemesisst:is_instance(v) then
return v.nemesis_id
end
end
end
function getNemesis(unit)
local id=getNemesisId(unit)
if id then
return df.nemesis_record.find(id)
end
end
function Allocate(size)
local ptr=engine.getmod('General_Space')
if ptr==nil then
ptr=engine.newmod("General_Space",4096) -- some time later maybe make some more space
engine.poked(ptr,4)
end
local curptr=engine.peekd(ptr)
curptr=curptr+size
engine.poked(ptr,curptr)
return curptr-size+ptr
end
dofile("dfusion/patterns.lua")
dofile("dfusion/patterns2.lua")
dofile("dfusion/itempatterns.lua")
dofile("dfusion/buildingpatterns.lua")

@ -1,150 +0,0 @@
function getTypename(object)
local tbl
local ret={}
if getmetatable(object)~=nil then
local tbl=getmetatable(object)
for k,v in pairs(xtypes) do
if v==tbl then
return k
end
end
for k,v in pairs(xtypes.containers) do
if v==tbl then
return k
end
end
end
if object.name~= nil then
return object.name
end
return "?"
end
function getFields(object)
local tbl
local ret={}
if getmetatable(object)==xtypes["struct-type"].wrap then
tbl=rawget(object,"mtype")
elseif getmetatable(object)==xtypes["struct-type"] then
tbl=object
else
error("Not an class_type or a class_object")
end
for k,v in pairs(tbl.types) do
table.insert(ret,{k,v[2],getTypename(v[1])})
--ret[v[2]]=k
--print(string.format("%s %x",k,v[2]))
end
table.sort(ret,function (a,b) return a[2]>b[2] end)
return ret
end
function editField(tbl,field,typename)
if EditType[typename] ~= nil then
EditType[typename](tbl[field])
else
print("Cur value:"..tostring(tbl[field]))
val=getline("Enter newvalue:")
tbl[field]=val
end
end
EditType={}
EditType["df-flagarray"]=function(trg)
local fields=rawget(trg,"mtype").index.names
print("Flag count:"..trg.size)
print("Name count:"..#fields)
for i=0,#fields do
print(string.format("%3d %20s %s",i,fields[i],tostring(trg[i-1])))
end
number=getline("enter flag id to flip:")
number=tonumber(number)
if number then
trg[fields[number]]= not trg[fields[number]]
end
end
EditType["enum-type"]=function(trg)
local fields=rawget(trg,"mtype").names
local typename=getTypename(rawget(trg,"mtype").etype)
for k,v in pairs(fields) do
print(string.format("%3d %s",k,v))
end
local cval=trg:get()
if fields[cval]~= nil then
print(string.format("Current value:%d (%s)",cval,fields[cval]))
else
print(string.format("Current value:%d",cval))
end
number=getline("enter new value:")
number=tonumber(number)
if number then
trg:set(number)
end
end
EditType["static-array"]=function(trg)
local item_type=rawget(trg,"mtype").item_type
local typename=getTypename(item_type)
number=getline(string.format("Select item (max %d, item-type '%s'):",trg.size,typename))
number=tonumber(number)
if number then
EditType[typename](trg[number])
end
end
EditType["stl-vector"]=EditType["static-array"]
EditType["df-array"]=EditType["static-array"]
EditType["struct-type"]=function(trg)
local mtype=rawget(trg,"mtype")
local fields=getFields(trg)
for k,v in pairs(fields) do
print(string.format("%4d %25s %s",k,v[1],v[3]))
end
number=getline("Choose field to edit:")
number=tonumber(number)
if number then
local v=fields[number]
editField(trg,v[1],v[3])
end
end
EditType["pointer"]=function(trg)
local mtype=rawget(trg,"mtype").ptype
local typename=getTypename(mtype)
if(trg:tonumber()==0) then
print("pointer points to nowhere!")
return
end
print("Auto dereferencing pointer! type:"..typename)
if EditType[typename] ~= nil then
EditType[typename](trg:deref())
else
print("Cur value:"..tostring(trg:deref()))
val=getline("Enter newvalue:")
trg:setref(val)
end
end
function EditDF()
local i=1
local tbl={}
for k,v in pairs(rawget(df,"types")) do
print(string.format("%4d %25s %s",i,k,getTypename(v)))
tbl[i]={k,getTypename(v)}
i=i+1
end
number=dfhack.lineedit("select item to edit (q to quit):")
if number and tonumber(number) then
local entry=tbl[tonumber(number)]
if entry==nil then
return
end
editField(df,entry[1],entry[2])
--[=[
if EditType[entry[2]] ~= nil then
EditType[entry[2]](df[entry[1]])
else
print("Cur value:"..tostring(df[entry[1]]))
val=getline("Enter newvalue:")
df[entry[1]]=val
end
--]=]
end
end
function EditObject(obj)
EditType[getTypename(obj)](obj)
end

@ -1 +0,0 @@
as -a --32 -o embark.o embark.asm

@ -1,83 +0,0 @@
function MakeTable(modpos,modsize,names)
count=0
castes={}
--print("Making table")
for _,line in pairs(names) do
--print("Line:"..line)
tpos=string.find(line,":")
if tpos~=nil then
--print("Was line:"..line)
table.insert(castes,tonumber(string.sub(line,tpos+1)))
line=string.sub(line,1,tpos-1)
--print("IS line:"..line)
else
table.insert(castes,-1)
end
if RaceTable[line] == nil then
error("Failure, "..line.." not found!")
end
--print("adding:"..line.." id:"..RaceTable[line])
engine.pokew(modpos+modsize+count*2,RaceTable[line]) -- add race
count = count + 1
end
i=0
for _,caste in pairs(castes) do
engine.pokew(modpos+modsize+count*2+i*2,caste) -- add caste
i= i+1
end
engine.poked(stoff,count)
return count
end
function embark(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod('Embark')
stoff=VersionInfo.getAddress('start_dwarf_count')
if mypos then --if mod already loaded
print("Mod already loaded @:"..mypos.." just updating")
modpos=mypos
_,modsize=engine.loadobj('dfusion/embark/embark.o')
count=MakeTable(modpos,modsize,names) --just remake tables
else
_,tofind=df.sizeof(df.global.ui:_field("race_id"))
loc=offsets.find(stoff,0x0f,0xb7,0x0d,DWORD_,tofind) --MOVZX ECX,WORD PTR[]
print(string.format("found:%x",loc))
if((loc~=0)and(loc-stoff<1000)) then
loc2=offsets.find(loc,0x83,0xc8,0xff) -- or eax, ffffff (for caste)
if loc2== 0 then
error ("Location for caste nulling not found!")
end
engine.pokeb(loc2,0x90)
engine.pokeb(loc2+1,0x90)
engine.pokeb(loc2+2,0x90)
ModData=engine.installMod("dfusion/embark/embark.o","Embark",256)
modpos=ModData.pos
modsize=ModData.size
local castepos=modpos+engine.FindMarker(ModData,"caste")
local racepos=modpos+engine.FindMarker(ModData,"race")
count=MakeTable(modpos,modsize,names)
engine.poked(castepos,modpos+modsize) --fix array start for race
engine.poked(racepos,modpos+modsize+count*2) --fix array start for caste
print("sucess loading mod @:"..modpos)
-- build race vector after module.
--finaly poke in the call!
engine.pokeb(loc,0x90)
engine.pokeb(loc+1,0x90)
engine.pokeb(loc+2,0xe8)
engine.poked(loc+3,modpos-loc-7)
--engine.pokeb(loc+5,0x90)
SetExecute(modpos)
else
error("did not find patch location, failing...")
end
end
end

@ -1,5 +0,0 @@
if not(FILE)then
names=ParseNames("dfusion/embark/races.txt")--io.open("plugins/embark/races.txt"):lines()
embark(names)
end

@ -1,9 +0,0 @@
ANT_MAN:0
ANT_MAN:0
ANT_MAN:0
ANT_MAN:1
ANT_MAN:1
ANT_MAN:0
ANT_MAN:0
ANT_MAN:2
ANT_MAN:3

@ -1,45 +0,0 @@
function analyzeF(off)
pos=offsets.find(off,0x39,ANYBYTE,0x8c,00,00,00)
print(string.format("Compare at:%x",pos))
if pos ==0 then
return 0
end
if(pos-off>0x100) then
print(string.format("Distance to cmp:%x",pos-off))
pos =offsets.find(off,CALL)
print(string.format("Distance to call:%x",pos-off))
return 0
--return analyzeF(pos)
else
return pos
end
end
function minEx(list)
local imin=list[1]
for _,v in ipairs(list) do
if imin> v and v~=0 then
imin=v
end
end
return imin
end
function signDword(dw)
if(dw>0xFFFFFFFF) then
return dw-0xFFFFFFFF
end
return dw
end
--[[
Warning: not all mov's are acounted for. Found one: mov EAX,WORD PTR[EBP+1EF4] WTF??
Two more compares are missing. There are calls instead (same function)
]]--
friendship_in={}
dofile("dfusion/friendship/install.lua")
dofile("dfusion/friendship/patch.lua")
function friendship(names)
friendship_in.install(names)
friendship_in.patch()
end

@ -1,35 +0,0 @@
function friendship_in.install(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod("Friendship")
if mypos then
modpos=mypos
_,modsize=engine.loadobj("dfusion/friendship/friendship.o")
_=nil
else
modpos,modsize=engine.loadmod("dfusion/friendship/friendship.o","Friendship",1024)
print(string.format("Loaded module @:%x",modpos))
end
count=0
for _,v in pairs(names) do
if RaceTable[v] == nil then
--print("Failure, "..v.." not found!")
error("Failure, "..v.." not found!")
--break --maybe some indication of failure? and cleanup?
end
engine.pokew(modpos+modsize+count*2+4+2,RaceTable[v]) -- for some reason it compiled strangely
-- cmp word[ebx+ecx*2],ax -> cmp word[ebx+ecx*2+2],ax
count = count + 1
end
engine.poked(modpos+0x8f,modpos+modsize+4) -- set ptr to creatures
engine.poked(modpos+0x94,count) -- set count of creatures
engine.poked(modpos+0xb9,modpos+modsize) -- set safe location
engine.poked(modpos+0xc3,modpos+modsize) -- set safe location
SetExecute(modpos)
end
function pokeCall(off)
engine.pokeb(off,0xe8)
b=engine.peekb(off+1)
engine.poked(off+1,modpos-off-5)
engine.pokeb(off+5,b)
end

@ -1,57 +0,0 @@
function friendship_in.patch()
UpdateRanges()
pos=GetTextRegion().start
local _,crace=df.sizeof(df.global.ui:_field("race_id"))
hits={}
i=1
repeat
--todo make something better/smarter...
pos1=offsets.find(pos+7,0x0f,0xBF,ANYBYTE,DWORD_,crace) -- movsx
pos2=offsets.find(pos+7,0x66,0xa1,DWORD_,crace) -- mov ax,[ptr]
pos3=offsets.find(pos+7,0xa1,DWORD_,crace) -- mov eax,[ptr]
pos4=offsets.find(pos+7,0x66,0x8b,ANYBYTE,DWORD_,crace) -- mov ANYREG,[ptr]
--pos5=offsets.find(pos+7,0x66,0x8b,0x15,DWORD_,crace) -- mov dx,[ptr]
pos=minEx{pos1,pos2,pos3,pos4}
if pos ~=0 then
hits[i]=pos
i=i+1
print(string.format("Found at %x",pos))
end
until pos==0
print("=======================================")
for _,p in pairs(hits) do
myp=p
repeat
--print(string.format("Analyzing %x...",p))
--TODO read offset from memory.xml
pos1=offsets.find(myp,0x39,ANYBYTE,0x8c,00,00,00) -- compare [reg+08c] (creature race) with any reg
pos2=offsets.find(myp,0x3b,ANYBYTE,0x8c,00,00,00) -- compare any reg with [reg+08c] (creature race)
pos=minEx{pos1,pos2}
if pos ~=0 then
if(pos-p>250) then
--this here does not work yet...
--[[pos =offsets.find(p,CALL)
print(string.format("Distance to call:%x",pos-p))
print(string.format("Call: %x",signDword(engine.peekd(pos+1)+pos)))
pos=analyzeF(signDword(signDword(engine.peekd(pos+1)+pos)))
print(string.format("Cmp @:%x",pos))]]--
print(string.format("skipping %x... Cmp too far away (dist=%i)",p,pos-p))
else
--print(string.format("Found at %x, simple compare",pos))
--print(string.format("Distance =%x",pos-p))
--patch compares
pokeCall(pos)
end
else
break
end
myp=myp+pos+6
if myp-p >250 then break end
until false
end
end

@ -1,18 +0,0 @@
if not(FILE) then
--sanity test
--print("race num:"..engine.peekw(offsets.getEx("CurrentRace")))
--print(string.format("%x vs %x",offsets.getEx("CurrentRace"),VersionInfo.getGroup("Creatures"):getAddress("current_race")))
print("Race num:"..df.global.ui.race_id)
print("Your current race is:"..GetRaceToken(df.global.ui.race_id))
print("If this is wrong please type 'q'")
if(getline()=='q') then
return
end
end
if not(FILE) then
names=ParseNames("dfusion/friendship/races.txt")--io.open("plugins/friendship/races.txt"):lines()
friendship_in.install(names)
friendship_in.patch()
end

@ -1,8 +0,0 @@
DWARF
GOBLIN
ELF
HUMAN
KOBOLD
GREMLIN
TIGERMAN
ANT_MAN

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

@ -1,41 +0,0 @@
.intel_syntax
eaxpart:
push eax
push ecx
jmp compare
ecxpart:
push eax
push ecx
mov eax,ecx
compare:
push ebx
mov ebx,0xDEADBEEF #write a pointer to the list of allowed civs
mov ecx,2000 #write a number of allowed civs
loop1:
cmp [ebx+ecx*4],eax
jnz endok
dec ecx
cmp ecx ,-1
jnz loop1
pop ebx
jmp fail
endok:
pop ebx
cmp eax,eax
jmp endfinal
fail:
xor ecx,ecx
xor eax,eax
inc eax
cmp eax,ebx
endfinal:
pop ecx
pop eax
ret

@ -1,89 +0,0 @@
friendship_civ={}
function friendship_civ.init()
friendship_civ.count=0x0f
friendship_civ.firsttime=true
local mypos=engine.getmod("Friendship_civ")
local modpos=0
if mypos then
modpos=mypos
_,modsize=engine.loadobj("dfusion/friendship_civ/friendship_c.o")
_=nil
friendship_civ.firsttime=false
else
modpos,modsize=engine.loadmod("dfusion/friendship_civ/friendship_c.o","Friendship_civ",1024)
print(string.format("Loaded module @:%x",modpos))
end
friendship_civ.modpos=modpos
friendship_civ.modsize=modsize
end
function friendship_civ.install(civs)
friendship_civ.init()
local count=0
for _,v in pairs(civs) do
engine.poked(friendship_civ.modpos+friendship_civ.modsize+count*4,v) -- for some reason it compiled strangely
-- cmp word[ebx+ecx*2],ax -> cmp word[ebx+ecx*2+2],ax
count = count + 1
end
engine.poked(friendship_civ.modpos+0x0a,friendship_civ.modpos+friendship_civ.modsize) -- set ptr to civ ids
engine.poked(friendship_civ.modpos+friendship_civ.count,count-1) -- set count of civs
SetExecute(friendship_civ.modpos)
if(friendship_civ.firsttime) then
friendship_civ.patch()
end
end
function friendship_civ.getcivs()
if(friendship_civ.firsttime==nil)then
return nil
end
friendship_civ.init()
local count=engine.peekd(friendship_civ.modpos+friendship_civ.count)+1
local ret={}
for i=0, count-1 do
table.insert(ret,engine.peekd(friendship_civ.modpos+friendship_civ.modsize+i*4))
end
return ret
end
function friendship_civ.addciv(civ) --if called with nil add current civ :)
if civ==nil then
local cciv=engine.peekd(VersionInfo.getGroup("Creatures"):getAddress("current_civ"))
friendship_civ.install({cciv})
return
end
local oldcivs=friendship_civ.getcivs()
oldcivs=oldcivs or {}
if type(civ)=="table" then
for k,v in ipairs(civ) do
table.insert(oldcivs,v)
end
else
table.insert(oldcivs,civ)
end
friendship_civ.install(oldcivs)
end
function friendship_civ.patch_call(off,iseax)
local calltrg=friendship_civ.modpos
if not iseax then
calltrg=calltrg+4
end
engine.pokeb(off,0xe8) --this is a call
engine.poked(off+1,calltrg-off-5) --offset to call to (relative)
engine.pokeb(off+5,0x90) --nop
end
function friendship_civ.patch()
--UpdateRanges()
local civloc= VersionInfo.getGroup("Creatures"):getAddress("current_civ")
local pos1=offsets.findall(0,0x3B,0x05,DWORD_,civloc) --eax
for k,v in pairs(pos1) do print(string.format("%d %x",k,v)) end
local pos2=offsets.findall(0,0x3B,0x0D,DWORD_,civloc) --ecx
for k,v in pairs(pos2) do print(string.format("%d %x",k,v)) end
for k,v in pairs(pos1) do
print(string.format("Patching eax compare %d: %x",k,v))
friendship_civ.patch_call(v,true)
end
for k,v in pairs(pos2) do
print(string.format("Patching ecx compare %d: %x",k,v))
friendship_civ.patch_call(v,false)
end
end

@ -1,57 +0,0 @@
fc_ui={}
fc_ui.menu=MakeMenu()
function fc_ui.get()
local mycivs=friendship_civ.getcivs()
if mycivs~= nil then
print(" Currently friendly civs:")
for k,v in pairs(mycivs) do
print(string.format("%d. %d",k,v))
end
else
print(" Plugin no yet activated.")
end
end
function fc_ui.add()
print("Type in civ id to add (leave empty to add current, q cancels):")
local r
while r==nil and r~='q' do
r=io.stdin:read()
if r=="" then
r=nil
break
end
if r~='q' then r=tonumber(r) else
return
end
end
friendship_civ.addciv(r)
end
function fc_ui.remove()
local mycivs=friendship_civ.getcivs()
if mycivs~= nil then
print(" Currently friendly civs:")
for k,v in pairs(mycivs) do
print(string.format("%d. %d",k,v))
end
else
print(" Plugin no yet activated, nothing to remove.")
return
end
print("Type in civ id to remove( q cancels):")
local r
while r==nil and r~='q' do
r=io.stdin:read()
if r~='q' then
r=tonumber(r)
if r>#mycivs then r=nil end
else
return
end
end
table.remove(mycivs,r)
friendship_civ.install(mycivs)
end
fc_ui.menu:add("Add civ",fc_ui.add)
fc_ui.menu:add("Get civs",fc_ui.get)
fc_ui.menu:add("Remove civ",fc_ui.remove)
fc_ui.menu:display()

@ -1,97 +0,0 @@
function err(msg) --make local maybe...
print(msg)
print(debug.traceback())
end
function dofile(filename) --safer dofile, with traceback (very usefull)
f,perr=loadfile(filename)
if f~=nil then
return xpcall(f,err)
else
print(perr)
end
end
function dofile_silent(filename) --safer dofile, with traceback, no file not found error
f,perr=loadfile(filename)
if f~=nil then
return xpcall(f,err)
else
if(string.sub(perr,1,11)~="cannot open") then --ugly hack
print(perr)
end
end
end
function loadall(t1) --loads all non interactive plugin parts, so that later they could be used
for k,v in pairs(t1) do
dofile_silent("dfusion/"..v[1].."/init.lua")
end
end
function mainmenu(t1)
while true do
print("No. Name Desc")
for k,v in pairs(t1) do
print(string.format("%3d %15s %s",k,v[1],v[2]))
end
local q=dfhack.lineedit("Select plugin to run (q to quit):")
if q=='q' then return end
q=tonumber(q)
if q~=nil then
if q>=1 and q<=#t1 then
if t1[q][3]==nil then
dofile("dfusion/"..t1[q][1].."/plugin.lua")
else
t1[q][3]()
end
end
end
end
end
function RunSaved()
print("Locating saves...")
local str=df.global.world.cur_savegame.save_dir
print("Current region:"..str)
str="data/save/"..str.."/dfusion/init.lua"
print("Trying to run:"..str)
dofile_silent(str)
end
dofile("dfusion/common.lua")
dofile("dfusion/utils.lua")
dofile("dfusion/offsets_misc.lua")
dofile("dfusion/editor.lua")
--dofile("dfusion/xml_struct.lua")
unlockDF()
plugins={}
table.insert(plugins,{"simple_embark","A simple embark dwarf count editor"})
table.insert(plugins,{"tools","some misc tools"})
table.insert(plugins,{"embark","Multi race embark"})
table.insert(plugins,{"friendship","Multi race fort enabler"})
--[=[table.insert(plugins,{"items","A collection of item hacking tools"})
table.insert(plugins,{"offsets","Find all offsets"})
table.insert(plugins,{"friendship_civ","Multi civ fort enabler"})
table.insert(plugins,{"triggers","a function calling plug (discontinued...)"})
table.insert(plugins,{"migrants","multi race imigrations"})
--]=]
--table.insert(plugins,{"onfunction","run lua on some df function"})
--table.insert(plugins,{"editor","edit internals of df",EditDF})
table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved})
table.insert(plugins,{"adv_tools","some tools for (mainly) adventurer hacking"})
loadall(plugins)
dofile_silent("dfusion/initcustom.lua")
local args={...}
local f,err=load(table.concat(args,' '))
if f then
f()
else
dfhack.printerr(err)
end
if not INIT then
mainmenu(plugins)
end

@ -1,62 +0,0 @@
--dofile("patterns2.lua") moved to common.lua
ptr_item={}
ptr_item.RTI={off=0,rtype=DWORD}
ptr_item.x={off=4,rtype=WORD}
ptr_item.y={off=6,rtype=WORD}
ptr_item.z={off=8,rtype=WORD}
ptr_item.ref={off=0x28,rtype=ptr_vector}
ptr_item.mat={off=0x78,rtype=WORD}
ptr_item.submat={off=0x7A,rtype=WORD}
ptr_item.submat2={off=0x7C,rtype=DWORD}
ptr_item.legendid={off=0x80,rtype=DWORD} -- i don't remember writing this...
ptr_item.decorations={off=0x90,rtype=ptr_vector}
ptr_item.flags={off=0xC,rtype=ptt_dfflag.new(8)}
ptr_item.ptr_covering={off=0x64,rtype=DWORD}
ptr_item.stack={off=0x58,rtype=WORD}
function ptr_item.getname(self,RTI)
if RTI == nil then
return string.sub(RTTI_GetName(self.RTI),5,-3)
else
return string.sub(RTTI_GetName(RTI),5,-3)
end
end
ptr_subitems={}
ptr_subitems["item_slabst"]={}
ptr_subitems["item_slabst"].msgptr={off=0xA0,rtype=ptt_dfstring}
ptr_subitems["item_slabst"].signtype={off=0xC0,rtype=DWORD}
ptr_subitems["item_fisthst"]={}
ptr_subitems["item_fisthst"].fisthtype={off=0x78,rtype=WORD}
ptr_subitems["item_eggst"]={}
ptr_subitems["item_eggst"].race={off=0x78,rtype=DWORD}
ptr_subitems["item_eggst"].isfertile={off=0xa0,rtype=DWORD} --0 or 1
ptr_subitems["item_eggst"].hatchtime={off=0xa4,rtype=DWORD}
ptr_decoration_gen={}
ptr_decoration_gen.RTI={off=0,rtype=DWORD}
ptr_decoration_gen.material={off=0x04,rtype=WORD} -- same for all?
ptr_decoration_gen.submat={off=0x08,rtype=DWORD}
function ptr_decoration_gen.getname(self,RTI)
if RTI == nil then
return string.sub(RTTI_GetName(self.RTI),21,-5)
else
return string.sub(RTTI_GetName(RTI),21,-5)
end
end
ptr_decoration={}
ptr_decoration["covered"]={}
ptr_decoration["covered"].material={off=0x04,rtype=WORD}
ptr_decoration["covered"].submat={off=0x08,rtype=DWORD}
ptr_decoration["art_image"]={}
ptr_decoration["art_image"].material={off=0x04,rtype=WORD}
ptr_decoration["art_image"].submat={off=0x08,rtype=DWORD}
ptr_decoration["art_image"].image={off=0x24,rtype=DWORD}
ptr_decoration["bands"]={}
ptr_decoration["bands"].material={off=0x04,rtype=WORD}
ptr_decoration["bands"].submat={off=0x08,rtype=DWORD}
ptr_cover={} --covering of various types (blood, water, etc)
ptr_cover.mat={off=0,rtype=WORD}
ptr_cover.submat={off=4,rtype=DWORD}
ptr_cover.state={off=8,rtype=WORD}

@ -1,212 +0,0 @@
items={} --> first lets make a menu table
items.menu=MakeMenu()
function items.dest()
myoff=offsets.getEx("Items") -- first find out where "item vector" is
vector=engine.peek(myoff,ptr_vector) -- get list of items
for i=0,vector:size()-1 do --look at each item
flg=engine.peek(vector:getval(i),ptr_item.flags)
flg:set(17,1)
engine.poke(vector:getval(i),ptr_item.flags,flg)
end
end
function items.eggs()
myoff=offsets.getEx("Items") -- first find out where "item vector" is
vector=engine.peek(myoff,ptr_vector) -- get list of items
for i=0,vector:size()-1 do --look at each item
rti=engine.peek(vector:getval(i),ptr_item.RTI)
if ptr_item.getname(nil,rti)=="item_eggst" then
egg=engine.peek(vector:getval(i),ptr_subitems["item_eggst"])
egg.isfertile=1
egg.hatchtime=0xffffff
--egg.race=123 -- change race for fun times
engine.poke(vector:getval(i),ptr_subitems["item_eggst"],egg)
end
end
end
function editFlags(offset)
while true do
flags=engine.peek(offset,ptr_item.flags)
for i=0,8*8-1 do
if flags:get(i) then
print(i.." is true")
else
print(i.." is false")
end
end
print(" enter number to switch flag or not a number to quit:")
q=tonumber(io.stdin:read())
if q==nil then return end
flags:flip(q)
engine.poke(offset,ptr_item.flags,flags)
end
end
function editCovering(offset)
off=engine.peek(offset,ptr_item.ptr_covering)
if off == 0 then
print("No coverings found.")
end
vec=engine.peek(off,ptr_vector)
print("Covering list:")
for i=0,vec:size()-1 do
cov=engine.peek(vec:getval(i),ptr_cover)
print(string.format("%d. mat=%d submat=%d state=%d",i,cov.mat,cov.submat,cov.state))
end
print("To edit type number:")
q=tonumber(io.stdin:read())
if q==nil then return end
if q>=vec:size() or q<0 then return end
off=vec:getval(q)
cov=engine.peek(off,ptr_cover)
print("Enter mat:")
q=tonumber(io.stdin:read())
if q==nil then q=0xffff end
print("Enter submat:")
v=tonumber(io.stdin:read())
if v==nil then v=0xffff end
print("Enter state:")
y=tonumber(io.stdin:read())
if y==nil then y=0 end
cov.mat=q
cov.submat=v
cov.state=y
engine.poke(off,ptr_cover,cov)
end
function editMaterial(offset)
print("Mat id 0 to 18 is in normal materials (inorganic, amber etc...) after that creature mat (with submat2 being race)")
print("from 219 with submat2=0xffffffff (type not a number) it reads legends id, from 419 submat2 means plant id")
print("Probably submat is not used :? ")
mat=engine.peek(offset,ptr_item.mat)
submat=engine.peek(offset,ptr_item.submat)
submat2=engine.peek(offset,ptr_item.submat2)
lid=engine.peek(offset,ptr_item.legendid)
print(string.format("Now is mat=%d, submat=%d submat2=%d legend id=%d",mat,submat,submat2,lid))
print("Enter mat:")
q=tonumber(io.stdin:read())
if q==nil then return end
print("Enter submat:")
v=tonumber(io.stdin:read())
if v==nil then v=0xffff end
print("Enter submat2:")
z=tonumber(io.stdin:read())
if z==nil then z=0xffffffff end
print("Enter legendid:")
y=tonumber(io.stdin:read())
if y==nil then y=0xffffffff end
engine.poke(offset,ptr_item.mat,q)
engine.poke(offset,ptr_item.submat,v)
engine.poke(offset,ptr_item.legendid,y)
engine.poke(offset,ptr_item.submat2,z)
print("Done")
end
function items.select()
myoff=offsets.getEx("Items")
vector=engine.peek(myoff,ptr_vector)
tx,ty,tz=getxyz()
T={}
for i=0,vector:size()-1 do --this finds all item offsets that are on pointer
itoff=vector:getval(i)
x=engine.peek(itoff,ptr_item.x)
y=engine.peek(itoff,ptr_item.y)
z=engine.peek(itoff,ptr_item.z)
if x==tx and y==ty and z==tz then
table.insert(T,itoff)
end
end
print("Items under cursor:")
i=1
for _,v in pairs(T) do
RTI=engine.peek(v,ptr_item.RTI)
print(i..". "..ptr_item.getname(nil,RTI))
i=i+1
end
print("Type number to edit or 'q' to exit")
while true do
q=io.stdin:read()
if q=='q' then return end
if tonumber(q) ~=nil and tonumber(q)<i then break end
end
return T[tonumber(q)]
end
function items.select_creature(croff)
vector=engine.peek(croff,ptr_Creature.itemlist2)
print("Vector size:"..vector:size())
T={}
for i=0,vector:size()-1 do
table.insert(T,vector:getval(i)) -- item list is itemptr+location?
end
print("Items in inventory:")
i=1
for _,v in pairs(T) do
RTI=engine.peek(engine.peekd(v),ptr_item.RTI)
print(i..". "..ptr_item.getname(nil,RTI).." locations:"..engine.peekw(v+4).." "..engine.peekw(v+6))
i=i+1
end
print("Type number to edit or 'q' to exit")
while true do
q=io.stdin:read()
if q=='q' then return end
if tonumber(q) ~=nil and tonumber(q)<i then break end
end
return engine.peekd(T[tonumber(q)])
end
function items.edit(itoff)
if itoff==nil then
itoff=items.select()
end
print(string.format("Item offset:%x",itoff))
print("Type what to edit:")
print("1. material")
print("2. flags")
print("3. covering")
Z={}
Z[1]=editMaterial
Z[2]=editFlags
Z[3]=editCovering
name=ptr_item.getname(nil,engine.peek(itoff,ptr_item.RTI))
if name~=nil and ptr_subitems[name]~=nil then
print("4. Item specific edit")
--Z[4]=items.fedit[name]
Z[4]="dummy"
end
while true do
q=io.stdin:read()
if q=='q' then return end
if tonumber(q) ~=nil and tonumber(q)<#Z+1 then break end
end
if Z[tonumber(q)]=="dummy" then
ModPattern(itoff,ptr_subitems[name])
else
Z[tonumber(q)](itoff)
end
end
function items.printref()
itoff=items.select()
vec=engine.peek(itoff,ptr_item.ref)
for i=0, vec:size()-1 do
toff=vec:getval(i)
print(RTTI_GetName(engine.peekd(toff)))
end
print("Decorations:")
vec=engine.peek(itoff,ptr_item.decorations)
for i=0, vec:size()-1 do
toff=vec:getval(i)
print(ptr_decoration_gen.getname(nil,engine.peek(toff,ptr_decoration_gen.RTI)))
end
end
function items.edit_adv()
vec=engine.peek(offsets.getEx("CreatureVec"),ptr_vector)
items.edit(items.select_creature(vec:getval(0)))
end
if not(FILE) then -- if not in script mode
items.menu:add("Destroy all",items.dest)
items.menu:add("Hatch eggs",items.eggs)
items.menu:add("Edit item",items.edit)
items.menu:add("Print ref",items.printref)
items.menu:add("Edit adventurer's items",items.edit_adv)
items.menu:display()
end

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

@ -1,62 +0,0 @@
--install part
function migrants(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod("Migrants")
if mypos then
print("Migrant mod is running already @:"..mypos)
modpos=mypos
_,modsize=engine.loadobj("dfusion/migrants/migrants.o")
count=0
for _,v in pairs(names) do
if RaceTable[v] == nil then
print("Failure, "..v.." not found!")
break --maybe some indication of failure? and cleanup?
end
engine.pokew(modpos+modsize+count*2+4,RaceTable[v])
count = count + 1
end
seedpos=modpos+modsize
engine.poked(seedpos,math.random(1234567)) -- seed the generator :)
engine.poked(modpos+0x1c,count) --max size for div
else
modpos,modsize=engine.loadmod("dfusion/migrants/migrants.o","Migrants",400)
print(string.format("Loaded module @:%x",modpos))
count=0
for _,v in pairs(names) do
if RaceTable[v] == nil then
print("Failure, "..v.." not found!")
break --maybe some indication of failure? and cleanup?
end
engine.pokew(modpos+modsize+count*2+4,RaceTable[v])
count = count + 1
end
seedpos=modpos+modsize
engine.poked(modpos+0x04,seedpos)
engine.poked(modpos+0x15,seedpos)
engine.poked(seedpos,math.random(1234567)) -- seed the generator :)
engine.poked(modpos+0x1c,count) --max size for div
engine.poked(modpos+0x26,seedpos+4) --start of array
--patch part
--pos=62873C+DF
-- pattern: A1,DWORD_,"CURRENTRACE",56,89,ANYBYTE,ANYBYTE,34,e8
_,raceoff=df.sizeof(df.global.ui:_field('race_id'))
pos=offsets.find(offsets.base(),0xa1,DWORD_,raceoff,0x56,0x89,ANYBYTE,ANYBYTE,0x34,0xe8)
function pokeCall(off)
engine.pokeb(off,0xe8)
engine.poked(off+1,modpos-off-5)
end
if pos~=0 then
print(string.format("Found @:%x",pos))
pokeCall(pos)
else
print("Not found patch location!!!")
end
end
end

@ -1,20 +0,0 @@
.intel_syntax
pushfd
push ebx
push edx
mov eax,[0xdeadbeef] # get old seed
mov ebx,1103515245
#mul 1103515245
mul ebx
add eax,12345
mov [0xdeadbeef],eax #put seed back...thus generation rnd is complete
xor edx,edx
mov ebx,2000 #put size of array here
div ebx #why oh why there is no div const? compiler prob makes some xor/add magic
movzx eax,word ptr[0xdeadbeef+edx*2]
pop edx
pop ebx
popfd
ret

@ -1,5 +0,0 @@
if not(FILE) then
names=ParseNames("dfusion/migrants/races.txt")--io.open("plugins/migrants/races.txt"):lines()
migrants(names)
end

@ -1,29 +0,0 @@
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
ELF
HUMAN
DWARF
GREMLIN
KOBOLD
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
DWARF
ELF
HUMAN
DWARF
GREMLIN
KOBOLD
DEMON_13

@ -1,2 +0,0 @@
offsets.searchoffsets()
offsets.save()

@ -1,48 +0,0 @@
offsets=offsets or {}
function offsets.find(startoffset,...)
-- [=[
if startoffset== 0 then
local text=GetTextRegion()
--print("searching in:"..text.name)
startoffset=text.start
endadr=text["end"]
else
local reg=GetRegionIn(startoffset)
--print("searching in:"..reg.name)
if reg==nil then
print(string.format("Warning: memory range for search @:%x not found!",startoffset))
return 0
end
endadr=reg["end"]
end
--]=]
--print(string.format("Searching (%x->%x)",startoffset,endadr))
local h=hexsearch(startoffset,endadr,...)
local pos=h:find()
h=nil
return pos
end
function offsets.findall(startoffset,...)
local endadr;
if startoffset== 0 then
local text=GetTextRegion()
--print("searching in:"..text.name)
startoffset=text.start
endadr=text["end"]
else
local reg=GetRegionIn(startoffset)
--print("searching in:"..reg.name)
endadr=reg["end"]
end
local h=hexsearch(startoffset,endadr,...)
local pos=h:findall()
h=nil
return pos
end
function offsets.base()
return Process.getBase()
end
function offsets.getvectors()
return findVectors()
end
ADDRESS=ANYDWORD

@ -1,248 +0,0 @@
ptt_dfstring={}
if WINDOWS then
ptt_dfstring.ptr={off=0,rtype=DWORD}
ptt_dfstring.size={off=16,rtype=DWORD}
ptt_dfstring.alloc={off=20,rtype=DWORD}
function ptt_dfstring:getval()
--print(string.format("GETTING FROM:%x",self.__offset))
if self.size<16 then
--print(string.format("GETTING FROM:%x",self.__offset))
return string.sub(engine.peekstr(self.__offset),1,self.size)
else
--print(string.format("GETTING FROM:%x",self.ptr))
return string.sub(engine.peekstr(self.ptr),1,self.size)
end
end
function ptt_dfstring:setval(newstring)
local offset=self.__offset
local strl=string.len(newstring)
if strl<16 then
--print(string.format("GETTING FROM:%x",self.__offset))
engine.poked(offset+ptt_dfstring.size.off,strl)
engine.poked(offset+ptt_dfstring.alloc.off,15)
engine.pokestr(offset,newstring)
else
local loc
if engine.peekd(offset+ptt_dfstring.alloc.off) > strl then
loc=engine.peekd(offset)
print("Will fit:"..loc.." len:"..strl)
else
loc=Allocate(strl+1)
engine.poked(offset+ptt_dfstring.alloc.off,strl)
print("Will not fit:"..loc.." len:"..strl)
end
--print(string.format("GETTING FROM:%x",self.ptr))
engine.poked(self.__offset+ptt_dfstring.size.off,strl)
engine.pokestr(loc,newstring)
engine.poked(self.__offset,loc)
end
end
else
--ptt_dfstring.ptr={off=0,rtype=DWORD}
function ptt_dfstring:getval()
return engine.peekstr_stl(self.__offset)
end
end
--if(COMPATMODE) then
--ptr_vector={}
--ptr_vector.st={off=4,rtype=DWORD}
--ptr_vector.en={off=8,rtype=DWORD}
--else
ptr_vector={}
ptr_vector.st={off=0,rtype=DWORD}
ptr_vector.en={off=4,rtype=DWORD}
ptr_vector.alloc={off=8,rtype=DWORD}
--end
function ptr_vector:clone(settype)
local ret={}
for k,v in pairs(self) do
ret[k]=v
end
ret.type=settype
return ret
end
function ptr_vector:size()
return (self.en-self.st)/engine.sizeof(self.type)
end
ptr_vector.type=DWORD
function ptr_vector:getval(num)
if self.st==0 then return error("Vector empty.") end
--print("Wants:"..num.." size:"..self:size())
if num>=self:size() then error("Index out of bounds in vector.") end
return engine.peek(self.st+engine.sizeof(self.type)*num,self.type)
end
function ptr_vector:setval(num,val)
return engine.poke(self.st+engine.sizeof(self.type)*num,self.type,val)
end
function ptr_vector:append(val)
if self.alloc - self.en > 0 then
local num=self:size()
self.en=self.en+engine.sizeof(self.type)
self:setval(val,num)
else
error("larger than allocated arrays not implemented yet")
local num=self:size()
local ptr=Allocate(num*2)
end
end
ptt_dfflag={}
function ptt_dfflag.flip(self,num) --flip one bit in flags
local of=math.floor (num/8);
self[of]=bit.bxor(self[of],bit.lshift(1,num%8))
end
function ptt_dfflag.get(self,num) -- get one bit in flags
local of=math.floor (num/8);
if bit.band(self[of],bit.lshift(1,num%8))~=0 then
return true
else
return false
end
end
function ptt_dfflag.set(self,num,val) --set to on or off one bit in flags
if (self:get(num)~=val) then
self:flip(num)
end
end
function ptt_dfflag.new(size) -- create new flag pattern of size(in bytes)
local ret={}
for i=0,size-1 do
ret[i]={off=i,rtype=BYTE};
end
ret.flip=ptt_dfflag.flip --change to metatable stuff...
ret.get=ptt_dfflag.get
ret.set=ptt_dfflag.set
return ret;
end
--[[
Creature:
0 name (df_string) 28
28 nick (df_string) 56
56 surname- namearray(7*dword(4)) 84 ...
140 race (dword) 144
224 flags
264 civ (dword)
252 ID
592 following ID
904 bleed vector? hurt vector or sth...
0x790 legends id?
2128 known names? or knowledge?
flags:
0 Can the dwarf move or are they waiting for their movement timer
1 Dead (might also be set for incoming/leaving critters that are alive)
2 Currently in mood
3 Had a mood
4 "marauder" -- wide class of invader/inside creature attackers
5 Drowning
6 Active merchant
7 "forest" (used for units no longer linked to merchant/diplomacy, they just try to leave mostly)
8 Left (left the map)
9 Rider
10 Incoming
11 Diplomat
12 Zombie
13 Skeleton
14 Can swap tiles during movement (prevents multiple swaps)
15 On the ground (can be conscious)
16 Projectile
17 Active invader (for organized ones)
18 Hidden in ambush
19 Invader origin (could be inactive and fleeing)
20 Will flee if invasion turns around
21 Active marauder/invader moving inward
22 Marauder resident/invader moving in all the way
23 Check against flows next time you get a chance
24 Ridden
25 Caged
26 Tame
27 Chained
28 Royal guard
29 Fortress guard
30 Suppress wield for beatings/etc
31 Is an important historical figure
32 swiming
]]--
ptr_Creature={}
local posoff=0 --VersionInfo.getGroup("Creatures"):getGroup("creature"):getOffset("position")
ptr_Creature.x={off=posoff,rtype=WORD} --ok
ptr_Creature.y={off=posoff+2,rtype=WORD} --ok
ptr_Creature.z={off=posoff+4,rtype=WORD} --ok
ptr_Creature.flags={off=0,rtype=ptt_dfflag.new(10)}
ptr_Creature.name={off=0,rtype=ptt_dfstring}
ptr_Creature.ID={off=252,rtype=DWORD} --ok i guess
ptr_Creature.followID={off=592,rtype=DWORD} --ok
ptr_Creature.race={off=140,rtype=DWORD} --ok
ptr_Creature.civ={off=264,rtype=DWORD}
ptr_Creature.legends={off=344,rtype=ptr_vector} --ok
ptr_Creature.hurt1={off=0x308,rtype=ptr_vector:clone(BYTE)} --byte vector...
ptr_Creature.hurt2={off=0x338,rtype=ptr_vector}
ptr_Creature.wounds={off=0x388,rtype=ptr_vector}
ptr_Creature.itemlist1={off=0x1D0,rtype=ptr_vector}
ptr_Creature.itemlist2={off=0x288,rtype=ptr_vector}
ptr_Creature.bloodlvl={off=0x490,rtype=DWORD}
ptr_Creature.bleedlvl={off=0x494,rtype=DWORD}
ptr_CrGloss={}
ptr_CrGloss.token={off=0,rtype=ptt_dfstring}
ptr_CrGloss.castes={off=296,rtype=ptr_vector}
ptr_CrCaste={}
ptr_CrCaste.name={off=0,rtype=ptt_dfstring}
ptr_CrCaste.flags_ptr={off=0x5A0,rtype=DWORD} --size 17?
--[=[
Flags:
57 - is sentient (allows setting labours)
--]=]
ptr_LEntry={} -- all size 256
ptr_LEntry.name={off=36,rtype=ptt_dfstring}
ptr_LEntry.id1={off=160,rtype=DWORD}
ptr_LEntry.id2={off=164,rtype=DWORD}
ptr_LEntry.somlist={off=220,rtype=DWORD}
ptr_dfname={}
for i=0,6 do
ptr_dfname[i]={off=i*4,rtype=DWORD}
end
--[[
Site docs:
0x38 name struct todo...
0x78 type:
0 - mountain halls (yours)
1 - dark fort
2 - cave
3 - mountain hall (other)
4 - forest
5 - hamlet
6 - imp location
7 - lair
8 - fort
9 - camp
0x7a some sort of id?
0x84 some vec (ids)
0x94 some other vec (ids)
0xa4 some vec (prts)
0x118 ptr to sth
0x14c ptr to mapdata
]]--
ptr_site={}
ptr_site.type={off=0x78,rtype=WORD}
ptr_site.id={off=0x7a,rtype=DWORD}
ptr_site.name={off=0x38,rtype=ptr_dfname}
ptr_site.flagptr={off=0x118,rtype=DWORD}
ptr_legends2={}
ptr_legends2.id={off=0,rtype=DWORD}
ptr_legends2.follow={off=0x18,rtype=DWORD}
ptr_material={}
ptr_material.token={off=0,rtype=ptt_dfstring}

@ -1,7 +0,0 @@
<ld:data-definition xmlns:ld="http://github.com/peterix/dfhack/lowered-data-definition">
<ld:global-type ld:meta="struct-type" type-name="coord">
<ld:field name="x" ld:meta="number" ld:subtype="int16_t" ld:bits="16"/>
<ld:field name="y" ld:meta="number" ld:subtype="int16_t" ld:bits="16"/>
<ld:field name="z" ld:meta="number" ld:subtype="int16_t" ld:bits="16"/>
</ld:global-type>
</ld:data-definition>

@ -1,55 +0,0 @@
function parseargs(s)
local arg = {}
string.gsub(s, "([%w%-]+)=([\"'])(.-)%2", function (w, _, a)
arg[w] = a
end)
return arg
end
function collect(s)
local stack = {}
local top = {}
table.insert(stack, top)
local ni,c,label,xarg, empty
local i, j = 1, 1
while true do
ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:%-]+)(.-)(%/?)>", i)
if not ni then break end
local text = string.sub(s, i, ni-1)
if not string.find(text, "^%s*$") then
table.insert(top, text)
end
if empty == "/" then -- empty element tag
table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
elseif c == "" then -- start tag
top = {label=label, xarg=parseargs(xarg)}
table.insert(stack, top) -- new level
else -- end tag
local toclose = table.remove(stack) -- remove top
top = stack[#stack]
if #stack < 1 then
error("nothing to close with "..label)
end
if toclose.label ~= label then
error("trying to close "..toclose.label.." with "..label)
end
table.insert(top, toclose)
end
i = j+1
end
local text = string.sub(s, i)
if not string.find(text, "^%s*$") then
table.insert(stack[#stack], text)
end
if #stack > 1 then
error("unclosed "..stack[#stack].label)
end
return stack[1]
end
function parseXmlFile(path)
local f, e = io.open(path, "r")
local xml = f:read("*a")
return collect(xml)
end

@ -1,29 +0,0 @@
ptr_COL={} -- complete object locator...
ptr_COL.sig={off=0,rtype=DWORD}
ptr_COL.offset={off=4,rtype=DWORD} --offset of this vtable in the complete class
ptr_COL.cdoffset={off=8,rtype=DWORD} -- constructor displacement
ptr_COL.typePointer={off=12,rtype=DWORD}
ptr_COL.hierarchyPointer={off=16,rtype=DWORD}
ptr_RTTI_Type={}
ptr_RTTI_Type.vftPointer={off=0,rtype=DWORD}
ptr_RTTI_Type.name={off=8,rtype=STD_STRING}
function RTTI_GetName(vtable)
local COLoff=engine.peek(vtable-4,DWORD)
--print(string.format("Look:%x vtable:%x",vtable,engine.peek(vtable-4,DWORD)))
COL=engine.peek(COLoff,ptr_COL)
--print(string.format("COL:%x Typeptr:%x Type:%s",COLoff,COL.typePointer,engine.peek(COL.typePointer,ptr_RTTI_Type.name)))
return engine.peek(COL.typePointer,ptr_RTTI_Type.name)
end
ptr_RTTI_Hierarchy={}
ptr_RTTI_Hierarchy.sig={off=0,rtype=DWORD}
ptr_RTTI_Hierarchy.attributes={off=4,rtype=DWORD}
ptr_RTTI_Hierarchy.numBaseClasses={off=8,rtype=DWORD}
ptr_RTTI_Hierarchy.ptrBases={off=12,rtype=DWORD}
ptr_RTTI_BaseClass={}
ptr_RTTI_BaseClass.typePointer={off=0,rtype=DWORD}
ptr_RTTI_BaseClass.numContained={off=4,rtype=DWORD}
--todo PMD
--todo flags

@ -1,15 +0,0 @@
function simple_embark(num)
stoff=VersionInfo.getAddress('start_dwarf_count')
print("Starting dwarves found:"..engine.peekd(stoff))
engine.poked(stoff,num)
end
if not(FILE) then
print("Type in new ammount:")
repeat
ans=tonumber(io.read())
if ans==nil or not(ans<=15000 and ans>0) then
print("incorrect choice")
end
until ans~=nil and (ans<=15000 and ans>0)
simple_embark(ans)
end

@ -1,497 +0,0 @@
tools={}
tools.menu=MakeMenu()
function tools.setrace(name)
RaceTable=BuildNameTable()
print("Your current race is:"..GetRaceToken(df.global.ui.race_id))
local id
if name == nil then
print("Type new race's token name in full caps (q to quit):")
repeat
entry=getline()
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
tools.menu:add("Set current race",tools.setrace)
function tools.GiveSentience(names)
RaceTable=RaceTable or BuildNameTable() --slow.If loaded don't load again
if names ==nil then
ids={}
print("Type race's token name in full caps to give sentience to:")
repeat
entry=getline()
id=RaceTable[entry]
until id~=nil
table.insert(ids,id)
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.size))
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
tools.menu:add("Give Sentience",tools.GiveSentience)
function tools.embark()
off=offsets.find(0,0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04)
if off~=0 then
engine.pokeb(off+5,0x90)
engine.pokeb(off+6,0x90)
print("Found and patched")
else
print("not found")
end
end
tools.menu:add("Embark anywhere",tools.embark)
function tools.getCreatureId(vector) --redo it to getcreature by name/id or something
tnames={}
rnames={}
--[[print("vector1 size:"..vector:size())
print("vector2 size:"..vector2:size())]]--
for i=0,vector:size()-1 do
--print(string.format("%x",vector:getval(i)))
local name=engine.peek(vector:getval(i),ptt_dfstring):getval()
local lid= tools.getlegendsid(vector:getval(i))
if lid ~=0 then
print(i..")*Creature Name:"..name.." race="..engine.peekw(vector:getval(i)+ptr_Creature.race.off).." legendid="..lid)
else
print(i..") Creature Name:"..name.." race="..engine.peekw(vector:getval(i)+ptr_Creature.race.off))
end
if name ~="" and name~=nil then
tnames[i]=name
rnames[name]=i
end
end
print("=====================================")
print("type in name or number:")
r=getline()
if tonumber(r) ==nil then
indx=rnames[r]
if indx==nil then return end
else
r=tonumber(r)
if r<vector:size() then indx=r else return end
end
return indx
end
function tools.change_adv(unit,nemesis)
if nemesis==nil then
nemesis=true --default value is nemesis switch too.
end
if unit==nil then
unit=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=getNemesis(unit)
local other_nem=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
error("Current unit does not have nemesis record, further working not guaranteed")
end
end
end
tools.menu:add("Change Adventurer",tools.change_adv)
function tools.MakeFollow(unit,trgunit)
if unit == nil then
unit=getCreature()
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=getNemesis(unit)
local t_nem=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
tools.menu:add("Make creature follow",tools.MakeFollow)
function tools.getsite(names)
if words==nil then --do once its slow.
words,rwords=BuildWordTables()
end
if names==nil then
print("Type words that are in the site name, FULLCAPS, no modifiers (lovely->LOVE), q to quit:")
names={}
repeat
w=getline();
if rwords[w]~=nil then
table.insert(names,w)
print("--added--")
end
until w=='q'
end
tnames={}
for _,v in pairs(names) do
if rwords[v] ~=nil then
table.insert(tnames,rwords[v]) --get word numbers
end
end
local offsites=engine.peekd(offsets.getEx("SiteData"))+0x120
snames={" pfort"," dfort"," cave","mohall","forest","hamlet","imploc"," lair"," fort"," camp"}
vector=engine.peek(offsites,ptr_vector)
print("Number of sites:"..vector:size())
print("List of hits:")
for i =0,vector:size()-1 do
off=vector:getval(i)
good=true
r=""
hits=0
sname=engine.peek(off,ptr_site.name)
for k=0,6 do
vnum=sname[k]--engine.peekd(off+0x38+k*4)
tgood=false
if vnum~=0xFFFFFFFF then
--print(string.format("%x",vnum))
if names[vnum]~=nil then
r=r..names[vnum].." "
end
for _,v in pairs(tnames) do
if vnum==v then
tgood=true
--print("Match")
hits=hits+1
break
end
end
if not tgood then
good=false
end
end
end
if(good) and (hits>0)then
--if true then
--print("=====================")
typ=engine.peek(off,ptr_site.type)--engine.peekw(off+0x78)
flg=engine.peekd(engine.peek(off,ptr_site.flagptr))
--flg=engine.peekd(off+224)
--flg2=engine.peekw(off)
--tv=engine.peek(off+0x84,ptr_vector)
--tv2=engine.peek(off+0xA4,ptr_vector)
print(string.format("%d)%s off=%x type=%s\t flags=%x",i,r,off,snames[typ+1],flg))
if i%100==99 then
r=getline()
end
end
end
print("Type which to change (q cancels):")
repeat
r=getline()
n=tonumber(r)
if(r=='q') then return end
until n~=nil
return vector:getval(n)
end
function tools.changesite(names)
off=tools.getsite(names)
snames={"Mountain halls (yours)","Dark fort","Cave","Mountain hall (NPC)","Forest retreat","Hamlet","Important location","Lair","Fort","Camp"}
print("Type in the site type (q cancels):")
for k,v in pairs(snames) do
print((k-1).."->"..v)
end
repeat
r=getline()
n2=tonumber(r)
if(r=='q') then return end
until n2~=nil
--off=vector:getval(n)
print(string.format("%x->%d",off,n2))
engine.poke(off,ptr_site.type,n2)
end
function tools.project(unit,trg)
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.refs:insert(#unit.refs,proj_ref)
unit.flags1.projectile=true
end
function tools.empregnate(unit)
if unit==nil then
unit=getSelectedUnit()
end
if unit==nil then
unit=getCreatureAtPos(getxyz())
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_ptr == nil then
print("creating preg ptr.")
if false then
print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_ptr"))))
return
end
unit.relations.pregnancy_ptr = { new = true, assign = genes }
end
local ngenes = unit.relations.pregnancy_ptr
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_mystery=1
end
tools.menu:add("Empregnate",tools.empregnate)
function tools.changeflags(names)
myflag_pattern=ptt_dfflag.new(3*8)
off=tools.getsite(names)
offflgs=engine.peek(off,ptr_site.flagptr)
q=''
print(string.format("Site offset %x flags offset %x",off,offflgs))
repeat
print("flags:")
--off=vector:getval(n)
flg=engine.peek(offflgs,myflag_pattern)
r=""
for i=0,3*8-1 do
if flg:get(i)==1 then
r=r.."x"
else
r=r.."o"
end
if i%8==7 then
print(i-7 .."->"..r)
r=""
end
end
print("Type number to flip, or 'q' to quit.")
q=getline()
n2=tonumber(q)
if n2~=nil then
flg:flip(n2)
engine.poke(offflgs,myflag_pattern,flg)
end
until q=='q'
end
function tools.hostilate()
vector=engine.peek(offsets.getEx("CreatureVec"),ptr_vector)
id=engine.peekd(offsets.getEx("CreaturePtr"))
print(string.format("Vec:%d cr:%d",vector:size(),id))
off=vector:getval(id)
crciv=engine.peek(off,ptr_Creature.civ)
print("Creatures civ:"..crciv)
curciv=engine.peekd(offsets.getEx("CurrentRace")-12)
print("My civ:"..curciv)
if curciv==crciv then
print("Friendly-making enemy")
engine.poke(off,ptr_Creature.civ,-1)
flg=engine.peek(off,ptr_Creature.flags)
flg:set(17,0)
print("flag 51:"..tostring(flg:get(51)))
engine.poke(off,ptr_Creature.flags,flg)
else
print("Enemy- making friendly")
engine.poke(off,ptr_Creature.civ,curciv)
flg=engine.peek(off,ptr_Creature.flags)
flg:set(17,1)
flg:set(19,0)
engine.poke(off,ptr_Creature.flags,flg)
end
end
function tools.mouseBlock()
local xs,ys,zs
xs,ys,zs=getxyz()
xs=math.floor(xs/16)
ys=math.floor(ys/16)
print("Mouse block is:"..xs.." "..ys.." "..zs)
end
function tools.fixwarp()
local mapoffset=offsets.getEx("WorldData")--0x131C128+offsets.base()
local x=engine.peek(mapoffset+24,DWORD)
local y=engine.peek(mapoffset+28,DWORD)
local z=engine.peek(mapoffset+32,DWORD)
--vec=engine.peek(mapoffset,ptr_vector)
print("Blocks loaded:"..x.." "..y.." "..z)
print("Select type:")
print("1. All (SLOW)")
print("2. range (x0 x1 y0 y1 z0 z1)")
print("3. One block around pointer")
print("anything else- quit")
q=getline()
n2=tonumber(q)
if n2==nil then return end
if n2>3 or n2<1 then return end
local xs,xe,ys,ye,zs,ze
if n2==1 then
xs=0
xe=x-1
ys=0
ye=y-1
zs=0
ze=z-1
elseif n2==2 then
print("enter x0:")
xs=tonumber(getline())
print("enter x1:")
xe=tonumber(getline())
print("enter y0:")
ys=tonumber(getline())
print("enter y1:")
ye=tonumber(getline())
print("enter z0:")
zs=tonumber(getline())
print("enter z1:")
ze=tonumber(getline())
function clamp(t,vmin,vmax)
if t> vmax then return vmax end
if t< vmin then return vmin end
return t
end
xs=clamp(xs,0,x-1)
ys=clamp(ys,0,y-1)
zs=clamp(zs,0,z-1)
xe=clamp(xe,xs,x-1)
ye=clamp(ye,ys,y-1)
ze=clamp(ze,zs,z-1)
else
xs,ys,zs=getxyz()
xs=math.floor(xs/16)
ys=math.floor(ys/16)
xe=xs
ye=ys
ze=zs
end
local xblocks=engine.peek(mapoffset,DWORD)
local flg=bit.bnot(bit.lshift(1,3))
for xx=xs,xe do
local yblocks=engine.peek(xblocks+xx*4,DWORD)
for yy=ys,ye do
local zblocks=engine.peek(yblocks+yy*4,DWORD)
for zz=zs,ze do
local myblock=engine.peek(zblocks+zz*4,DWORD)
if myblock~=0 then
for i=0,255 do
local ff=engine.peek(myblock+0x67c+i*4,DWORD)
ff=bit.band(ff,flg) --set 14 flag to 1
engine.poke(myblock+0x67c+i*4,DWORD,ff)
end
end
end
print("Blocks done:"..xx.." "..yy)
end
end
end

@ -1,8 +0,0 @@
if not(FILE) then
--tools.menu:add("Change site type",tools.changesite)
--tools.menu:add("Change site flags",tools.changeflags)
--tools.menu:add("Hostilate creature",tools.hostilate)
--tools.menu:add("Print current mouse block",tools.mouseBlock)
tools.menu:display()
end

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

@ -1,20 +0,0 @@
function func.Find_Print()
pos=offsets.find(offsets.base(),0x73,0x02,0x8b,0xce,0x53,0x6a,0x01,0x6a,0x06,CALL) -- a hack for now...
return engine.peekd(pos+10)+pos+14-offsets.base()
end
function func.PrintMessage(msg,color1,color2)
func.f_print_pos= func.f_print_pos or func.Find_Print()
print(string.format("Print @:%x",func.f_print_pos))
debuger.suspend()
d=NewCallTable() -- make a call table
t=Allocate(string.len(msg))
engine.pokestr(t,msg)
--print(string.format("Message location:%x",t))
d["ECX"]=t --set ecx to message location
d["STACK5"]=color1 -- push to stack color1
d["STACK4"]=color2 -- push to stack color2
d["STACK3"]=0 -- this is usually 0 maybe a struct pointing to location of this message?
PushFunction(func.f_print_pos+offsets.base(),d) -- prep to call function
-- was 0x27F030
debuger.resume()
end

@ -1,12 +0,0 @@
func={}
dofile("dfusion/triggers/functions.lua")
func.menu=MakeMenu()
function func.PrintMessage_()
print("Type a message:")
msg=io.stdin:read()
func.PrintMessage(msg,6,1)
end
if not(FILE) then -- if not in script mode
func.menu:add("Print message",func.PrintMessage_)
func.menu:display()
end

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