Merge remote-tracking branch 'angavrilov/master'

develop
Kelly Martin 2012-10-12 20:33:15 -05:00
commit 5abecf32e7
12 changed files with 453 additions and 9 deletions

@ -932,6 +932,21 @@ Returns <em>entry, did_create_new</em></p>
and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p>
<p>It is also possible to associate one bit per map tile with an entry,
using these two methods:</p>
<ul>
<li><p class="first"><tt class="docutils literal">entry:getTilemask(block[, create])</tt></p>
<p>Retrieves the tile bitmask associated with this entry in the given map
block. If <tt class="docutils literal">create</tt> is <em>true</em>, an empty mask is created if none exists;
otherwise the function returns <em>nil</em>, which must be assumed to be the same
as an all-zero mask.</p>
</li>
<li><p class="first"><tt class="docutils literal">entry:deleteTilemask(block)</tt></p>
<p>Deletes the associated tile mask from the given map block.</p>
</li>
</ul>
<p>Note that these masks are only saved in fortress mode, and also that deleting
the persistent entry will <strong>NOT</strong> delete the associated masks.</p>
</div>
<div class="section" id="material-info-lookup">
<h3><a class="toc-backref" href="#id17">Material info lookup</a></h3>
@ -1286,6 +1301,18 @@ tools like liquids or tiletypes are used. It also cannot possibly
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.hasTileAssignment(tilemask)</tt></p>
<p>Checks if the tile_bitmask object is not <em>nil</em> and contains any set bits; returns <em>true</em> or <em>false</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getTileAssignment(tilemask,x,y)</tt></p>
<p>Checks if the tile_bitmask object is not <em>nil</em> and has the relevant bit set; returns <em>true</em> or <em>false</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.setTileAssignment(tilemask,x,y,enable)</tt></p>
<p>Sets the relevant bit in the tile_bitmask object to the <em>enable</em> argument.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.maps.resetTileAssignment(tilemask[,enable])</span></tt></p>
<p>Sets all bits in the mask to the <em>enable</em> argument.</p>
</li>
</ul>
</div>
<div class="section" id="burrows-module">

@ -646,6 +646,24 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.
It is also possible to associate one bit per map tile with an entry,
using these two methods:
* ``entry:getTilemask(block[, create])``
Retrieves the tile bitmask associated with this entry in the given map
block. If ``create`` is *true*, an empty mask is created if none exists;
otherwise the function returns *nil*, which must be assumed to be the same
as an all-zero mask.
* ``entry:deleteTilemask(block)``
Deletes the associated tile mask from the given map block.
Note that these masks are only saved in fortress mode, and also that deleting
the persistent entry will **NOT** delete the associated masks.
Material info lookup
--------------------
@ -1079,6 +1097,22 @@ Maps module
take into account anything that depends on the actual units, like
burrows, or the presence of invaders.
* ``dfhack.maps.hasTileAssignment(tilemask)``
Checks if the tile_bitmask object is not *nil* and contains any set bits; returns *true* or *false*.
* ``dfhack.maps.getTileAssignment(tilemask,x,y)``
Checks if the tile_bitmask object is not *nil* and has the relevant bit set; returns *true* or *false*.
* ``dfhack.maps.setTileAssignment(tilemask,x,y,enable)``
Sets the relevant bit in the tile_bitmask object to the *enable* argument.
* ``dfhack.maps.resetTileAssignment(tilemask[,enable])``
Sets all bits in the mask to the *enable* argument.
Burrows module
--------------

@ -72,6 +72,9 @@ keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine
# military weapon auto-select
keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons
# minecart Guide path
keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
############################
# UI and game logic tweaks #
############################

@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h"
#include "Core.h"
#include "Error.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
@ -311,12 +312,12 @@ static PersistentDataItem get_persistent(lua_State *state)
if (lua_istable(state, 1))
{
Lua::StackUnwinder frame(state);
if (!lua_getmetatable(state, 1) ||
!lua_rawequal(state, -1, lua_upvalueindex(1)))
luaL_argerror(state, 1, "invalid table type");
lua_settop(state, 1);
return persistent_by_struct(state, 1);
}
else
@ -446,11 +447,38 @@ static int dfhack_persistent_save(lua_State *state)
return 2;
}
static int dfhack_persistent_getTilemask(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 3);
auto ref = get_persistent(state);
auto block = Lua::CheckDFObject<df::map_block>(state, 2);
bool create = lua_toboolean(state, 3);
Lua::PushDFObject(state, World::getPersistentTilemask(ref, block, create));
return 1;
}
static int dfhack_persistent_deleteTilemask(lua_State *state)
{
CoreSuspender suspend;
lua_settop(state, 2);
auto ref = get_persistent(state);
auto block = Lua::CheckDFObject<df::map_block>(state, 2);
lua_pushboolean(state, World::deletePersistentTilemask(ref, block));
return 1;
}
static const luaL_Reg dfhack_persistent_funcs[] = {
{ "get", dfhack_persistent_get },
{ "delete", dfhack_persistent_delete },
{ "get_all", dfhack_persistent_get_all },
{ "save", dfhack_persistent_save },
{ "getTilemask", dfhack_persistent_getTilemask },
{ "deleteTilemask", dfhack_persistent_deleteTilemask },
{ NULL, NULL }
};
@ -937,6 +965,22 @@ static const luaL_Reg dfhack_items_funcs[] = {
/***** Maps module *****/
static bool hasTileAssignment(df::tile_bitmask *bm) {
return bm && bm->has_assignments();
}
static bool getTileAssignment(df::tile_bitmask *bm, int x, int y) {
return bm && bm->getassignment(x,y);
}
static void setTileAssignment(df::tile_bitmask *bm, int x, int y, bool val) {
CHECK_NULL_POINTER(bm);
bm->setassignment(x,y,val);
}
static void resetTileAssignment(df::tile_bitmask *bm, bool val) {
CHECK_NULL_POINTER(bm);
if (val) bm->set_all();
else bm->clear();
}
static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, enableBlockUpdates),
@ -944,6 +988,10 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween),
WRAPM(Maps, spawnFlow),
WRAPN(hasTileAssignment, hasTileAssignment),
WRAPN(getTileAssignment, getTileAssignment),
WRAPN(setTileAssignment, setTileAssignment),
WRAPN(resetTileAssignment, resetTileAssignment),
{ NULL, NULL }
};

@ -281,9 +281,10 @@ VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_
return item;
}
void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr)
bool VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr)
{
auto &children = cur->getChildren();
bool found = false;
for (size_t i = 0; i < children.size(); i++)
{
@ -298,19 +299,34 @@ void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmp
continue;
child_next.insert(base);
found = true;
}
else
else if (child->vtable_ptr)
{
void *cptr = child->get_vmethod_ptr(vmethod_idx);
if (cptr != vmptr)
continue;
child_hosts.insert(child);
found = true;
find_child_hosts(child, vmptr);
}
else
{
// If this vtable is not known, but any of the children
// have the same vmethod, this one definitely does too
if (find_child_hosts(child, vmptr))
{
child_hosts.insert(child);
found = true;
}
}
}
return found;
}
void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from)
{
if (from == host)

@ -159,7 +159,7 @@ namespace DFHack
void on_host_delete(virtual_identity *host);
VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id);
void find_child_hosts(virtual_identity *cur, void *vmptr);
bool find_child_hosts(virtual_identity *cur, void *vmptr);
public:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
~VMethodInterposeLinkBase();

@ -16,7 +16,7 @@ inline bool getassignment( const df::coord2d &xy )
}
inline bool getassignment( int x, int y )
{
return (bits[y] & (1 << x));
return (bits[(y&15)] & (1 << (x&15)));
}
inline void setassignment( const df::coord2d &xy, bool bit )
{
@ -25,9 +25,9 @@ inline void setassignment( const df::coord2d &xy, bool bit )
inline void setassignment( int x, int y, bool bit )
{
if(bit)
bits[y] |= (1 << x);
bits[(y&15)] |= (1 << (x&15));
else
bits[y] &= ~(1 << x);
bits[(y&15)] &= ~(1 << (x&15));
}
bool has_assignments()
{

@ -37,6 +37,12 @@ distribution.
#include "DataDefs.h"
namespace df
{
struct tile_bitmask;
struct map_block;
}
namespace DFHack
{
typedef df::game_mode GameMode;
@ -120,6 +126,9 @@ namespace DFHack
DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item);
DFHACK_EXPORT void ClearPersistentCache();
DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false);
DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block);
}
}
#endif

@ -85,6 +85,8 @@ using namespace DFHack;
#include "df/assign_trade_status.h"
#include "df/announcement_flags.h"
#include "df/announcements.h"
#include "df/stop_depart_condition.h"
#include "df/route_stockpile_link.h"
using namespace df::enums;
using df::global::gview;
@ -303,6 +305,45 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
focus += "/List";
break;
case Hauling:
if (ui->hauling.in_assign_vehicle)
{
auto vehicle = vector_get(ui->hauling.vehicles, ui->hauling.cursor_vehicle);
focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None");
}
else
{
int idx = ui->hauling.cursor_top;
auto route = vector_get(ui->hauling.view_routes, idx);
auto stop = vector_get(ui->hauling.view_stops, idx);
std::string tag = stop ? "Stop" : (route ? "Route" : "None");
if (ui->hauling.in_name)
focus += "/Rename/" + tag;
else if (ui->hauling.in_stop)
{
int sidx = ui->hauling.cursor_stop;
auto cond = vector_get(ui->hauling.stop_conditions, sidx);
auto link = vector_get(ui->hauling.stop_links, sidx);
focus += "/DefineStop";
if (cond)
focus += "/Cond/" + enum_item_key(cond->mode);
else if (link)
{
focus += "/Link/";
if (link->mode.bits.give) focus += "Give";
if (link->mode.bits.take) focus += "Take";
}
else
focus += "/None";
}
else
focus += "/Select/" + tag;
}
break;
default:
break;
}

@ -38,13 +38,18 @@ using namespace std;
#include "ModuleFactory.h"
#include "Core.h"
#include "modules/Maps.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/world.h"
#include "df/historical_figure.h"
#include "df/map_block.h"
#include "df/block_square_event_world_constructionst.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
@ -312,3 +317,63 @@ bool World::DeletePersistentData(const PersistentDataItem &item)
return false;
}
df::tile_bitmask *World::getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create)
{
if (!block)
return NULL;
int id = item.raw_id();
if (id > -100)
return NULL;
for (size_t i = 0; i < block->block_events.size(); i++)
{
auto ev = block->block_events[i];
if (ev->getType() != block_square_event_type::world_construction)
continue;
auto wcsev = strict_virtual_cast<df::block_square_event_world_constructionst>(ev);
if (!wcsev || wcsev->construction_id != id)
continue;
return &wcsev->tile_bitmask;
}
if (!create)
return NULL;
auto ev = df::allocate<df::block_square_event_world_constructionst>();
if (!ev)
return NULL;
ev->construction_id = id;
ev->tile_bitmask.clear();
vector_insert_at(block->block_events, 0, (df::block_square_event*)ev);
return &ev->tile_bitmask;
}
bool World::deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block)
{
if (!block)
return false;
int id = item.raw_id();
if (id > -100)
return false;
bool found = false;
for (int i = block->block_events.size()-1; i >= 0; i--)
{
auto ev = block->block_events[i];
if (ev->getType() != block_square_event_type::world_construction)
continue;
auto wcsev = strict_virtual_cast<df::block_square_event_world_constructionst>(ev);
if (!wcsev || wcsev->construction_id != id)
continue;
delete wcsev;
vector_erase_at(block->block_events, i);
found = true;
}
return found;
}

@ -1 +1 @@
Subproject commit a5a8fd68947b60fcb2d1c03b4f05bdf48cfcf7a5
Subproject commit aec106cdc32083bbcc6d6dd27d9e069f5525ea92

@ -0,0 +1,201 @@
-- Show and manipulate the path used by Guide Cart orders.
local utils = require 'utils'
local gui = require 'gui'
local guidm = require 'gui.dwarfmode'
local dlg = require 'gui.dialogs'
local tile_attrs = df.tiletype.attrs
function same_xyz(a,b)
return a and b and a.x == b.x and a.y == b.y and a.z == b.z
end
GuidePathUI = defclass(GuidePathUI, guidm.MenuOverlay)
GuidePathUI.focus_path = 'guide-path'
GuidePathUI.ATTRS {
route = DEFAULT_NIL,
stop = DEFAULT_NIL,
order = DEFAULT_NIL,
}
function GuidePathUI:init()
self.saved_mode = df.global.ui.main.mode
for i=0,#self.route.stops-1 do
if self.route.stops[i] == self.stop then
self.stop_idx = i
break
end
end
if not self.stop_idx then
error('Could not find stop index')
end
self.next_stop = self.route.stops[(self.stop_idx+1)%#self.route.stops]
end
function GuidePathUI:onShow()
-- with cursor, but without those ugly lines from native hauling mode
df.global.ui.main.mode = df.ui_sidebar_mode.Stockpiles
end
function GuidePathUI:onDestroy()
df.global.ui.main.mode = self.saved_mode
end
local function getTileType(cursor)
local block = dfhack.maps.getTileBlock(cursor)
if block then
return block.tiletype[cursor.x%16][cursor.y%16]
else
return 0
end
end
local function isTrackTile(tile)
return tile_attrs[tile].special == df.tiletype_special.TRACK
end
local function paintMapTile(dc, vp, cursor, pos, ...)
if not same_xyz(cursor, pos) then
local stile = vp:tileToScreen(pos)
if stile.z == 0 then
dc:seek(stile.x,stile.y):char(...)
end
end
end
local function get_path_point(gpath,i)
return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i])
end
local function blink_visible(delay)
return math.floor(dfhack.getTickCount()/delay) % 2 == 0
end
function GuidePathUI:renderPath(cursor)
local gpath = self.order.guide_path
local startp = self.stop.pos
local endp = self.next_stop.pos
local vp = self:getViewport()
local dc = gui.Painter.new(self.df_layout.map)
local visible = blink_visible(500)
if visible then
paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN)
end
local ok = nil
local pcnt = #gpath.x
if pcnt > 0 then
ok = true
for i = 0,pcnt-1 do
local pt = get_path_point(gpath, i)
if i == 0 and not same_xyz(startp,pt) then
ok = false
end
if i == pcnt-1 and not same_xyz(endp,pt) then
ok = false
end
local tile = getTileType(pt)
if not isTrackTile(tile) then
ok = false
end
if visible then
local char = '+'
if i < pcnt-1 then
local npt = get_path_point(gpath, i+1)
if npt.x == pt.x+1 then
char = 26
elseif npt.x == pt.x-1 then
char = 27
elseif npt.y == pt.y+1 then
char = 25
elseif npt.y == pt.y-1 then
char = 24
end
end
local color = COLOR_LIGHTGREEN
if not ok then color = COLOR_LIGHTRED end
paintMapTile(dc, vp, cursor, pt, char, color)
end
end
end
if blink_visible(120) then
paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN)
end
return ok
end
function GuidePathUI:onRenderBody(dc)
dc:clear():seek(1,1):pen(COLOR_WHITE):string("Guide Path")
dc:seek(2,3)
local cursor = guidm.getCursorPos()
local path_ok = self:renderPath(cursor)
if path_ok == nil then
dc:string('No saved path', COLOR_DARKGREY)
elseif path_ok then
dc:string('Valid path', COLOR_GREEN)
else
dc:string('Invalid path', COLOR_RED)
end
dc:newline():newline(1)
dc:key('CUSTOM_Z'):string(": Reset path, ",COLOR_GREY,nil,path_ok~=nil)
dc:key('CUSTOM_P'):string(": Find path",COLOR_GREY,nil,false)
dc:newline(1)
dc:key('CUSTOM_C'):string(": Zoom cur, ")
dc:key('CUSTOM_N'):string(": Zoom next")
dc:newline():newline(1):string('At cursor:')
dc:newline():newline(2)
local tile = getTileType(cursor)
if isTrackTile(tile) then
dc:string('Track '..tile_attrs[tile].direction, COLOR_GREEN)
else
dc:string('No track', COLOR_DARKGREY)
end
dc:newline():newline(1)
dc:key('LEAVESCREEN'):string(": Back")
end
function GuidePathUI:onInput(keys)
if keys.CUSTOM_C then
self:moveCursorTo(copyall(self.stop.pos))
elseif keys.CUSTOM_N then
self:moveCursorTo(copyall(self.next_stop.pos))
elseif keys.CUSTOM_Z then
local gpath = self.order.guide_path
gpath.x:resize(0)
gpath.y:resize(0)
gpath.z:resize(0)
elseif keys.LEAVESCREEN then
self:dismiss()
elseif self:propagateMoveKeys(keys) then
return
end
end
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/Hauling/DefineStop/Cond/Guide') then
qerror("This script requires the main dwarfmode view in 'h' mode over a Guide order")
end
local hauling = df.global.ui.hauling
local route = hauling.view_routes[hauling.cursor_top]
local stop = hauling.view_stops[hauling.cursor_top]
local order = hauling.stop_conditions[hauling.cursor_stop]
local list = GuidePathUI{ route = route, stop = stop, order = order }
list:show()