Add a script to reclassify 'individual choice' weapons based on unit skill.

develop
Alexander Gavrilov 2012-09-29 17:21:49 +04:00
parent 48da06ec37
commit 62cde96724
6 changed files with 237 additions and 13 deletions

@ -56,6 +56,7 @@ DFHack v0.34.11-r2 (UNRELEASED)
- gui/rename: renaming stockpiles, workshops and units via an in-game dialog.
- gui/power-meter: front-end for the Power Meter plugin.
- gui/siege-engine: front-end for the Siege Engine plugin.
- gui/choose-weapons: auto-choose matching weapons in the military equip screen.
Autolabor plugin:
- can set nonidle hauler percentage.
- broker excluded from all labors when needed at depot.

@ -1800,19 +1800,19 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit
directly to the main dwarf mode screen.
Liquids
=======
gui/liquids
===========
Implemented by the gui/liquids script. To use, bind to a key and activate in the 'k' mode.
To use, bind to a key and activate in the 'k' mode.
While active, use the suggested keys to switch the usual liquids parameters, and Enter
to select the target area and apply changes.
Mechanisms
==========
gui/mechanisms
==============
Implemented by the gui/mechanims script. To use, bind to a key and activate in the 'q' mode.
To use, bind to a key and activate in the 'q' mode.
Lists mechanisms connected to the building, and their links. Navigating the list centers
the view on the relevant linked buildings.
@ -1822,10 +1822,10 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter
re-entering the mechanisms ui.
Rename
======
gui/rename
==========
Backed by the rename plugin, the gui/rename script allows entering the desired name
Backed by the rename plugin, this script allows entering the desired name
via a simple dialog in the game ui.
* ``gui/rename [building]`` in 'q' mode changes the name of a building.
@ -1840,16 +1840,30 @@ via a simple dialog in the game ui.
The ``building`` or ``unit`` options are automatically assumed when in relevant ui state.
Room List
=========
gui/room-list
=============
Implemented by the gui/room-list script. To use, bind to a key and activate in the 'q' mode,
either immediately or after opening the assign owner page.
To use, bind to a key and activate in the 'q' mode, either immediately or after opening
the assign owner page.
The script lists other rooms owned by the same owner, or by the unit selected in the assign
list, and allows unassigning them.
gui/choose-weapons
==================
Bind to a key, and activate in the Equip->View/Customize page of the military screen.
Depending on the cursor location, it rewrites all 'individual choice weapon' entries
in the selected squad or position to use a specific weapon type matching the assigned
unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites
only that entry, and does it even if it is not 'individual choice'.
Rationale: individual choice seems to be unreliable when there is a weapon shortage,
and may lead to inappropriate weapons being selected.
=============
Behavior Mods
=============

@ -69,6 +69,9 @@ keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter
# siege engine control
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
############################
# UI and game logic tweaks #
############################

@ -361,6 +361,27 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos
end
-- Binary search and erase
function erase_sorted_key(vector,key,field,cmp)
local cur,found,pos = binsearch(vector,key,field,cmp)
if found then
if df.isvalid(vector) then
vector:erase(pos)
else
table.remove(vector, pos)
end
end
return found,cur,pos
end
function erase_sorted(vector,item,field,cmp)
local key = item
if field and item then
key = item[field]
end
return erase_sorted_key(vector,key,field,cmp)
end
-- Calls a method with a string temporary
function call_with_string(obj,methodname,...)
return dfhack.with_temp_object(

@ -347,6 +347,37 @@ DEFINE_GET_FOCUS_STRING_HANDLER(layer_military)
break;
}
case df::viewscreen_layer_militaryst::Equip:
{
focus += "/" + enum_item_key(screen->equip.mode);
switch (screen->equip.mode)
{
case df::viewscreen_layer_militaryst::T_equip::Customize:
{
if (screen->equip.edit_mode < 0)
focus += "/View";
else
focus += "/" + enum_item_key(screen->equip.edit_mode);
break;
}
case df::viewscreen_layer_militaryst::T_equip::Uniform:
break;
case df::viewscreen_layer_militaryst::T_equip::Priority:
{
if (screen->equip.prio_in_move >= 0)
focus += "/Move";
else
focus += "/View";
break;
}
}
static const char *lists[] = { "/Squads", "/Positions", "/Choices" };
focus += lists[cur_list];
break;
}
default:
break;
}

@ -0,0 +1,154 @@
-- Rewrite individual choice weapons into specific types.
local utils = require 'utils'
local dlg = require 'gui.dialogs'
local defs = df.global.world.raws.itemdefs
local entity = df.global.ui.main.fortress_entity
local tasks = df.global.ui.tasks
local equipment = df.global.ui.equipment
function find_best_weapon(unit,mode)
local best = nil
local skill = nil
local skill_level = nil
local count = 0
local function try(id,iskill)
local slevel = dfhack.units.getNominalSkill(unit,iskill)
-- Choose most skill
if (skill ~= nil and slevel > skill_level)
or (skill == nil and slevel > 0) then
best,skill,skill_level,count = id,iskill,slevel,0
end
-- Then most produced within same skill
if skill == iskill then
local cnt = tasks.created_weapons[id]
if cnt > count then
best,count = id,cnt
end
end
end
for _,id in ipairs(entity.resources.weapon_type) do
local def = defs.weapons[id]
if def.skill_ranged >= 0 then
if mode == nil or mode == 'ranged' then
try(id, def.skill_ranged)
end
else
if mode == nil or mode == 'melee' then
try(id, def.skill_melee)
end
end
end
return best
end
function unassign_wrong_items(unit,position,spec,subtype)
for i=#spec.assigned-1,0,-1 do
local id = spec.assigned[i]
local item = df.item.find(id)
if item.subtype.subtype ~= subtype then
spec.assigned:erase(i)
-- TODO: somewhat unexplored area; maybe missing some steps
utils.erase_sorted(position.assigned_items,id)
if utils.erase_sorted(equipment.items_assigned.WEAPON,item,'id') then
utils.insert_sorted(equipment.items_unassigned.WEAPON,item,'id')
end
equipment.update.weapon = true
unit.military.pickup_flags.update = true
end
end
end
local count = 0
function adjust_uniform_spec(unit,position,spec,force)
if not unit then return end
local best
if spec.indiv_choice.melee then
best = find_best_weapon(unit, 'melee')
elseif spec.indiv_choice.ranged then
best = find_best_weapon(unit, 'ranged')
elseif spec.indiv_choice.any or force then
best = find_best_weapon(unit, nil)
end
if best then
count = count + 1
spec.item_filter.item_subtype = best
spec.indiv_choice.any = false
spec.indiv_choice.melee = false
spec.indiv_choice.ranged = false
unassign_wrong_items(unit, position, spec, best)
end
end
function adjust_position(unit,position,force)
if not unit then
local fig = df.historical_figure.find(position.occupant)
if not fig then return end
unit = df.unit.find(fig.unit_id)
end
for _,v in ipairs(position.uniform.weapon) do
adjust_uniform_spec(unit, position, v, force)
end
end
function adjust_squad(squad, force)
for _,pos in ipairs(squad.positions) do
adjust_position(nil, pos, force)
end
end
local args = {...}
local vs = dfhack.gui.getCurViewscreen()
local vstype = df.viewscreen_layer_militaryst
if not vstype:is_instance(vs) then
qerror('Call this from the military screen')
end
if vs.page == vstype.T_page.Equip
and vs.equip.mode == vstype.T_equip.T_mode.Customize then
local slist = vs.layer_objects[0]
local squad = vs.equip.squads[slist:getListCursor()]
if slist.active then
print('Adjusting squad.')
adjust_squad(squad)
else
local plist = vs.layer_objects[1]
local pidx = plist:getListCursor()
local pos = squad.positions[pidx]
local unit = vs.equip.units[pidx]
if plist.active then
print('Adjusting position.')
adjust_position(unit, pos)
elseif unit and vs.equip.edit_mode < 0 then
local wlist = vs.layer_objects[2]
local idx = wlist:getListCursor()
local cat = vs.equip.assigned.category[idx]
if wlist.active and cat == df.uniform_category.weapon then
print('Adjusting spec.')
adjust_uniform_spec(unit, pos, vs.equip.assigned.spec[idx], true)
end
end
end
else
qerror('Call this from the Equip page of military screen')
end
if count > 1 then
dlg.showMessage(
'Choose Weapons',
'Updated '..count..' uniform entries.', COLOR_GREEN
)
elseif count == 0 then
dlg.showMessage(
'Choose Weapons',
'Did not find any entries to update.', COLOR_YELLOW
)
end