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 and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O. 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> 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>
<div class="section" id="material-info-lookup"> <div class="section" id="material-info-lookup">
<h3><a class="toc-backref" href="#id17">Material info lookup</a></h3> <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 take into account anything that depends on the actual units, like
burrows, or the presence of invaders.</p> burrows, or the presence of invaders.</p>
</li> </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> </ul>
</div> </div>
<div class="section" id="burrows-module"> <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. functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead. 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 Material info lookup
-------------------- --------------------
@ -1079,6 +1097,22 @@ Maps module
take into account anything that depends on the actual units, like take into account anything that depends on the actual units, like
burrows, or the presence of invaders. 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 Burrows module
-------------- --------------

@ -72,6 +72,9 @@ keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine
# military weapon auto-select # military weapon auto-select
keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons 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 # # UI and game logic tweaks #
############################ ############################

@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h" #include "MemAccess.h"
#include "Core.h" #include "Core.h"
#include "Error.h"
#include "VersionInfo.h" #include "VersionInfo.h"
#include "tinythread.h" #include "tinythread.h"
// must be last due to MS stupidity // must be last due to MS stupidity
@ -311,12 +312,12 @@ static PersistentDataItem get_persistent(lua_State *state)
if (lua_istable(state, 1)) if (lua_istable(state, 1))
{ {
Lua::StackUnwinder frame(state);
if (!lua_getmetatable(state, 1) || if (!lua_getmetatable(state, 1) ||
!lua_rawequal(state, -1, lua_upvalueindex(1))) !lua_rawequal(state, -1, lua_upvalueindex(1)))
luaL_argerror(state, 1, "invalid table type"); luaL_argerror(state, 1, "invalid table type");
lua_settop(state, 1);
return persistent_by_struct(state, 1); return persistent_by_struct(state, 1);
} }
else else
@ -446,11 +447,38 @@ static int dfhack_persistent_save(lua_State *state)
return 2; 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[] = { static const luaL_Reg dfhack_persistent_funcs[] = {
{ "get", dfhack_persistent_get }, { "get", dfhack_persistent_get },
{ "delete", dfhack_persistent_delete }, { "delete", dfhack_persistent_delete },
{ "get_all", dfhack_persistent_get_all }, { "get_all", dfhack_persistent_get_all },
{ "save", dfhack_persistent_save }, { "save", dfhack_persistent_save },
{ "getTilemask", dfhack_persistent_getTilemask },
{ "deleteTilemask", dfhack_persistent_deleteTilemask },
{ NULL, NULL } { NULL, NULL }
}; };
@ -937,6 +965,22 @@ static const luaL_Reg dfhack_items_funcs[] = {
/***** Maps module *****/ /***** 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[] = { static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, enableBlockUpdates), WRAPM(Maps, enableBlockUpdates),
@ -944,6 +988,10 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getLocalInitFeature), WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween), WRAPM(Maps, canWalkBetween),
WRAPM(Maps, spawnFlow), WRAPM(Maps, spawnFlow),
WRAPN(hasTileAssignment, hasTileAssignment),
WRAPN(getTileAssignment, getTileAssignment),
WRAPN(setTileAssignment, setTileAssignment),
WRAPN(resetTileAssignment, resetTileAssignment),
{ NULL, NULL } { NULL, NULL }
}; };

@ -281,9 +281,10 @@ VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_
return item; 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(); auto &children = cur->getChildren();
bool found = false;
for (size_t i = 0; i < children.size(); i++) for (size_t i = 0; i < children.size(); i++)
{ {
@ -298,17 +299,32 @@ void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmp
continue; continue;
child_next.insert(base); child_next.insert(base);
found = true;
} }
else else if (child->vtable_ptr)
{ {
void *cptr = child->get_vmethod_ptr(vmethod_idx); void *cptr = child->get_vmethod_ptr(vmethod_idx);
if (cptr != vmptr) if (cptr != vmptr)
continue; continue;
child_hosts.insert(child); child_hosts.insert(child);
found = true;
find_child_hosts(child, vmptr); 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) void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from)

@ -159,7 +159,7 @@ namespace DFHack
void on_host_delete(virtual_identity *host); void on_host_delete(virtual_identity *host);
VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id); 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: public:
VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority); VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority);
~VMethodInterposeLinkBase(); ~VMethodInterposeLinkBase();

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

@ -37,6 +37,12 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
namespace df
{
struct tile_bitmask;
struct map_block;
}
namespace DFHack namespace DFHack
{ {
typedef df::game_mode GameMode; typedef df::game_mode GameMode;
@ -120,6 +126,9 @@ namespace DFHack
DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item); DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item);
DFHACK_EXPORT void ClearPersistentCache(); 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 #endif

@ -85,6 +85,8 @@ using namespace DFHack;
#include "df/assign_trade_status.h" #include "df/assign_trade_status.h"
#include "df/announcement_flags.h" #include "df/announcement_flags.h"
#include "df/announcements.h" #include "df/announcements.h"
#include "df/stop_depart_condition.h"
#include "df/route_stockpile_link.h"
using namespace df::enums; using namespace df::enums;
using df::global::gview; using df::global::gview;
@ -303,6 +305,45 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
focus += "/List"; focus += "/List";
break; 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: default:
break; break;
} }

@ -38,13 +38,18 @@ using namespace std;
#include "ModuleFactory.h" #include "ModuleFactory.h"
#include "Core.h" #include "Core.h"
#include "modules/Maps.h"
#include "MiscUtils.h" #include "MiscUtils.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "df/world.h" #include "df/world.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/map_block.h"
#include "df/block_square_event_world_constructionst.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums;
using df::global::world; using df::global::world;
@ -312,3 +317,63 @@ bool World::DeletePersistentData(const PersistentDataItem &item)
return false; 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()