From 8359e80b233675ee347ff1ac8ec7d37430d52a7a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 24 Aug 2012 18:26:18 +0400 Subject: [PATCH] Expose a few API functions to lua, and implement a room browser overlay. --- LUA_API.rst | 14 ++ Lua API.html | 11 ++ dfhack.init-example | 3 + library/LuaApi.cpp | 3 + library/include/modules/Buildings.h | 5 + library/modules/Buildings.cpp | 39 +++++ scripts/gui/room-list.lua | 246 ++++++++++++++++++++++++++++ 7 files changed, 321 insertions(+) create mode 100644 scripts/gui/room-list.lua diff --git a/LUA_API.rst b/LUA_API.rst index 49e205235..a7dab21b0 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -880,6 +880,15 @@ Units module Retrieves the profession name for the given race/caste using raws. +* ``dfhack.units.getProfessionColor(unit[,ignore_noble])`` + + Retrieves the color associated with the profession, using noble assignments + or raws. The ``ignore_noble`` boolean disables the use of noble positions. + +* ``dfhack.units.getCasteProfessionColor(race,caste,prof_id)`` + + Retrieves the profession color for the given race/caste using raws. + Items module ------------ @@ -1032,6 +1041,11 @@ Burrows module Buildings module ---------------- +* ``dfhack.buildings.setOwner(item,unit)`` + + Replaces the owner of the building. If unit is *nil*, removes ownership. + Returns *false* in case of error. + * ``dfhack.buildings.getSize(building)`` Returns *width, height, centerx, centery*. diff --git a/Lua API.html b/Lua API.html index 15f1d89cb..b9f09cf96 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1113,6 +1113,13 @@ or raws. The ignore_noble boolean disables the
  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

    Retrieves the profession name for the given race/caste using raws.

  • +
  • dfhack.units.getProfessionColor(unit[,ignore_noble])

    +

    Retrieves the color associated with the profession, using noble assignments +or raws. The ignore_noble boolean disables the use of noble positions.

    +
  • +
  • dfhack.units.getCasteProfessionColor(race,caste,prof_id)

    +

    Retrieves the profession color for the given race/caste using raws.

    +
  • @@ -1237,6 +1244,10 @@ burrows, or the presence of invaders.

    Buildings module

      +
    • dfhack.buildings.setOwner(item,unit)

      +

      Replaces the owner of the building. If unit is nil, removes ownership. +Returns false in case of error.

      +
    • dfhack.buildings.getSize(building)

      Returns width, height, centerx, centery.

    • diff --git a/dfhack.init-example b/dfhack.init-example index f5f40196c..c9408e375 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -45,3 +45,6 @@ keybinding add Shift-G "job-material GLASS_GREEN" # browse linked mechanisms keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms + +# browse rooms of same owner +keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 35cf1435b..00d4c517d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -815,6 +815,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getAge), WRAPM(Units, getProfessionName), WRAPM(Units, getCasteProfessionName), + WRAPM(Units, getProfessionColor), + WRAPM(Units, getCasteProfessionColor), { NULL, NULL } }; @@ -985,6 +987,7 @@ static bool buildings_containsTile(df::building *bld, int x, int y, bool room) { } static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { + WRAPM(Buildings, setOwner), WRAPM(Buildings, allocInstance), WRAPM(Buildings, checkFreeTiles), WRAPM(Buildings, countExtentTiles), diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 6e0a22052..639df6865 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -92,6 +92,11 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); +/** + * Sets the owner unit for the building. + */ +DFHACK_EXPORT bool setOwner(df::building *building, df::unit *owner); + /** * Find the building located at the specified tile. * Does not work on civzones. diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 8ec60e55b..d1aed8979 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -49,6 +49,7 @@ using namespace DFHack; #include "df/ui_look_list.h" #include "df/d_init.h" #include "df/item.h" +#include "df/unit.h" #include "df/job.h" #include "df/job_item.h" #include "df/general_ref_building_holderst.h" @@ -177,6 +178,44 @@ bool Buildings::ReadCustomWorkshopTypes(map & btypes) return true; } +bool Buildings::setOwner(df::building *bld, df::unit *unit) +{ + CHECK_NULL_POINTER(bld); + + if (!bld->is_room) + return false; + if (bld->owner == unit) + return true; + + if (bld->owner) + { + auto &blist = bld->owner->owned_buildings; + vector_erase_at(blist, linear_index(blist, bld)); + + if (auto spouse = df::unit::find(bld->owner->relations.spouse_id)) + { + auto &blist = spouse->owned_buildings; + vector_erase_at(blist, linear_index(blist, bld)); + } + } + + bld->owner = unit; + + if (unit) + { + unit->owned_buildings.push_back(bld); + + if (auto spouse = df::unit::find(unit->relations.spouse_id)) + { + auto &blist = spouse->owned_buildings; + if (bld->canUseSpouseRoom() && linear_index(blist, bld) < 0) + blist.push_back(bld); + } + } + + return true; +} + df::building *Buildings::findAtTile(df::coord pos) { auto occ = Maps::getTileOccupancy(pos); diff --git a/scripts/gui/room-list.lua b/scripts/gui/room-list.lua new file mode 100644 index 000000000..a4507466f --- /dev/null +++ b/scripts/gui/room-list.lua @@ -0,0 +1,246 @@ +-- Browses rooms owned by a unit. + +local utils = require 'utils' +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' + +local room_type_table = { + [df.building_bedst] = { token = 'bed', qidx = 2, tile = 233 }, + [df.building_tablest] = { token = 'table', qidx = 3, tile = 209 }, + [df.building_chairst] = { token = 'chair', qidx = 4, tile = 210 }, + [df.building_coffinst] = { token = 'coffin', qidx = 5, tile = 48 }, +} + +local room_quality_table = { + { 1, 'Meager Quarters', 'Meager Dining Room', 'Meager Office', 'Grave' }, + { 100, 'Modest Quarters', 'Modest Dining Room', 'Modest Office', "Servant's Burial Chamber" }, + { 250, 'Quarters', 'Dining Room', 'Office', 'Burial Chamber' }, + { 500, 'Decent Quarters', 'Decent Dining Room', 'Decent Office', 'Tomb' }, + { 1000, 'Fine Quarters', 'Fine Dining Room', 'Splendid Office', 'Fine Tomb' }, + { 1500, 'Great Bedroom', 'Great Dining Room', 'Throne Room', 'Mausoleum' }, + { 2500, 'Grand Bedroom', 'Grand Dining Room', 'Opulent Throne Room', 'Grand Mausoleum' }, + { 10000, 'Royal Bedroom', 'Royal Dining Room', 'Royal Throne Room', 'Royal Mausoleum' } +} + +function getRoomName(building, unit) + local info = room_type_table[building._type] + if not info or not building.is_room then + return utils.getBuildingName(building) + end + + local quality = building:getRoomValue(unit) + local row = room_quality_table[1] + for _,v in ipairs(room_quality_table) do + if v[1] <= quality then + row = v + else + break + end + end + return row[info.qidx] +end + +function makeRoomEntry(bld, unit, is_spouse) + local info = room_type_table[bld._type] or {} + + return { + obj = bld, + token = info.token or '?', + tile = info.tile or '?', + caption = getRoomName(bld, unit), + can_use = (not is_spouse or bld:canUseSpouseRoom()), + owner = unit + } +end + +function listRooms(unit, spouse) + local rv = {} + for _,v in pairs(unit.owned_buildings) do + if v.owner == unit then + rv[#rv+1] = makeRoomEntry(v, unit, spouse) + end + end + return rv +end + +function concat_lists(...) + local rv = {} + for i = 1,select('#',...) do + local v = select(i,...) + if v then + for _,x in ipairs(v) do rv[#rv+1] = x end + end + end + return rv +end + +RoomList = defclass(RoomList, guidm.MenuOverlay) + +RoomList.focus_path = 'room-list' + +function RoomList:init(unit) + local base_bld = df.global.world.selected_building + + self:init_fields{ + unit = unit, base_building = base_bld, + items = {}, selected = 1, + own_rooms = {}, spouse_rooms = {} + } + guidm.MenuOverlay.init(self) + + self.old_viewport = self:getViewport() + self.old_cursor = guidm.getCursorPos() + + if unit then + self.own_rooms = listRooms(unit) + self.spouse = df.unit.find(unit.relations.spouse_id) + if self.spouse then + self.spouse_rooms = listRooms(self.spouse, unit) + end + self.items = concat_lists(self.own_rooms, self.spouse_rooms) + end + + if base_bld then + for i,v in ipairs(self.items) do + if v.obj == base_bld then + self.selected = i + v.tile = 26 + goto found + end + end + self.base_item = makeRoomEntry(base_bld, unit) + self.base_item.owner = unit + self.base_item.old_owner = base_bld.owner + self.base_item.tile = 26 + self.items = concat_lists({self.base_item}, self.items) + ::found:: + end + + return self +end + +local sex_char = { [0] = 12, [1] = 11 } + +function drawUnitName(dc, unit) + dc:pen(COLOR_GREY) + if unit then + local color = dfhack.units.getProfessionColor(unit) + dc:char(sex_char[unit.sex] or '?'):advance(1):pen(color) + + local vname = dfhack.units.getVisibleName(unit) + if vname and vname.has_name then + dc:string(dfhack.TranslateName(vname)..', ') + end + dc:string(dfhack.units.getProfessionName(unit)) + else + dc:string("No Owner Assigned") + end +end + +function drawRoomEntry(dc, entry, selected) + local color = COLOR_GREEN + if not entry.can_use then + color = COLOR_RED + elseif entry.obj.owner ~= entry.owner or not entry.owner then + color = COLOR_CYAN + end + dc:pen{fg = color, bold = (selected == entry)} + dc:char(entry.tile):advance(1):string(entry.caption) +end + +function can_modify(sel_item) + return sel_item and sel_item.owner + and sel_item.can_use and not sel_item.owner.flags1.dead +end + +function RoomList:onRenderBody(dc) + local sel_item = self.items[self.selected] + + dc:clear():seek(1,1) + drawUnitName(dc, self.unit) + + if self.base_item then + dc:newline():newline(2) + drawRoomEntry(dc, self.base_item, sel_item) + end + if #self.own_rooms > 0 then + dc:newline() + for _,v in ipairs(self.own_rooms) do + dc:newline(2) + drawRoomEntry(dc, v, sel_item) + end + end + if #self.spouse_rooms > 0 then + dc:newline():newline(1) + drawUnitName(dc, self.spouse) + + dc:newline() + for _,v in ipairs(self.spouse_rooms) do + dc:newline(2) + drawRoomEntry(dc, v, sel_item) + end + end + if self.unit and #self.own_rooms == 0 and #self.spouse_rooms == 0 then + dc:newline():newline(2):string("No already assigned rooms.", COLOR_LIGHTRED) + end + + dc:newline():newline(1):pen(COLOR_WHITE) + dc:string("Esc", COLOR_LIGHTGREEN):string(": Back") + + if can_modify(sel_item) then + dc:string(", "):string("Enter", COLOR_LIGHTGREEN) + if sel_item.obj.owner == sel_item.owner then + dc:string(": Unassign") + else + dc:string(": Assign") + end + end +end + +function RoomList:changeSelected(delta) + if #self.items <= 1 then return end + self.selected = 1 + (self.selected + delta - 1) % #self.items + self:selectBuilding(self.items[self.selected].obj) +end + +function RoomList:onInput(keys) + local sel_item = self.items[self.selected] + + if keys.SECONDSCROLL_UP then + self:changeSelected(-1) + elseif keys.SECONDSCROLL_DOWN then + self:changeSelected(1) + elseif keys.LEAVESCREEN then + self:dismiss() + + if self.base_building then + if not sel_item or self.base_building ~= sel_item.obj then + self:selectBuilding(self.base_building, self.old_cursor, self.old_view) + end + if self.unit and self.base_building.owner == self.unit then + df.global.ui_building_in_assign = false + end + end + elseif keys.SELECT then + if can_modify(sel_item) then + local owner = sel_item.owner + if sel_item.obj.owner == owner then + owner = sel_item.old_owner + end + dfhack.buildings.setOwner(sel_item.obj, owner) + end + elseif self:simulateViewScroll(keys) then + return + end +end + +local focus = dfhack.gui.getCurFocus() +if focus == 'dwarfmode/QueryBuilding/Some' then + local base = df.global.world.selected_building + mkinstance(RoomList):init(base.owner):show() +elseif focus == 'dwarfmode/QueryBuilding/Some/Assign/Unit' then + local unit = df.global.ui_building_assign_units[df.global.ui_building_item_cursor] + mkinstance(RoomList):init(unit):show() +else + qerror("This script requires the main dwarfmode view in 'q' mode") +end