implement rating overlay

develop
Myk Taylor 2023-09-07 16:14:20 -07:00
parent f0b0c2093b
commit d0f08dcc0d
No known key found for this signature in database
1 changed files with 228 additions and 91 deletions

@ -9,31 +9,33 @@ local widgets = require('gui.widgets')
local CH_UP = string.char(30)
local CH_DN = string.char(31)
local MELEE_WEAPON_SKILLS = {
df.job_skill.AXE,
df.job_skill.SWORD,
df.job_skill.MACE,
df.job_skill.HAMMER,
df.job_skill.SPEAR,
}
local RANGED_WEAPON_SKILLS = {
df.job_skill.CROSSBOW,
}
local function get_rating(val, max, med, low)
val = math.min(max, val)
local percent = (val * 100) // max
local color = COLOR_GREEN
if percent < (low or 50) then color = COLOR_RED
elseif percent < (med or 75) then color = COLOR_YELLOW
end
return percent, color
end
local function sort_noop(a, b)
-- this function is used as a marker and never actually gets called
error('sort_noop should not be called')
end
local function get_name(unit)
return unit and dfhack.toSearchNormalized(dfhack.TranslateName(dfhack.units.getVisibleName(unit)))
end
local function sort_by_name_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local name1 = dfhack.TranslateName(dfhack.units.getVisibleName(unit1))
local name2 = dfhack.TranslateName(dfhack.units.getVisibleName(unit2))
local name1 = get_name(unit1)
local name2 = get_name(unit2)
return utils.compare_name(name1, name2)
end
@ -43,8 +45,8 @@ local function sort_by_name_asc(unit_id_1, unit_id_2)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local name1 = dfhack.TranslateName(dfhack.units.getVisibleName(unit1))
local name2 = dfhack.TranslateName(dfhack.units.getVisibleName(unit2))
local name1 = get_name(unit1)
local name2 = get_name(unit2)
return utils.compare_name(name2, name1)
end
@ -61,6 +63,11 @@ local function get_active_idx_cache()
return active_idx_cache
end
local function get_migrant_wave_rating(unit)
-- TODO: return green for most recent wave, red for the first wave, yellow for all others
return 1, nil
end
local function sort_by_migrant_wave_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local cache = get_active_idx_cache()
@ -77,17 +84,29 @@ local function sort_by_migrant_wave_asc(unit_id_1, unit_id_2)
return utils.compare(cache[unit_id_1], cache[unit_id_2])
end
local function get_stress(unit)
return unit and
unit.status.current_soul and
unit.status.current_soul.personality.stress
end
local function get_stress_rating(unit)
return get_rating(-get_stress(unit) + 100000, 200000, 50, 25)
end
local function sort_by_stress_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local happiness1 = unit1.status.current_soul.personality.stress
local happiness2 = unit2.status.current_soul.personality.stress
local happiness1 = get_stress(unit1)
local happiness2 = get_stress(unit2)
if happiness1 == happiness2 then
return sort_by_name_desc(unit_id_1, unit_id_2)
end
if not happiness2 then return -1 end
if not happiness1 then return 1 end
return utils.compare(happiness2, happiness1)
end
@ -97,22 +116,37 @@ local function sort_by_stress_asc(unit_id_1, unit_id_2)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local happiness1 = unit1.status.current_soul.personality.stress
local happiness2 = unit2.status.current_soul.personality.stress
local happiness1 = get_stress(unit1)
local happiness2 = get_stress(unit2)
if happiness1 == happiness2 then
return sort_by_name_desc(unit_id_1, unit_id_2)
end
if not happiness2 then return 1 end
if not happiness1 then return -1 end
return utils.compare(happiness1, happiness2)
end
local function get_skill(unit_id, skill, unit)
unit = unit or df.unit.find(unit_id)
local function get_skill(skill, unit)
return unit and
unit.status.current_soul and
utils.binsearch(unit.status.current_soul.skills, skill, 'id')
(utils.binsearch(unit.status.current_soul.skills, skill, 'id'))
end
local function melee_skill_effectiveness(unit, skill_list)
local function get_skill_rating(skill, unit)
local uskill = get_skill(skill, unit)
if not uskill then return nil end
return get_rating(uskill.rating, 100, 5, 0)
end
local MELEE_WEAPON_SKILLS = {
df.job_skill.AXE,
df.job_skill.SWORD,
df.job_skill.MACE,
df.job_skill.HAMMER,
df.job_skill.SPEAR,
}
local function melee_skill_effectiveness(unit)
-- Physical attributes
local strength = dfhack.units.getPhysicalAttrValue(unit, df.physical_attribute_type.STRENGTH)
local agility = dfhack.units.getPhysicalAttrValue(unit, df.physical_attribute_type.AGILITY)
@ -128,7 +162,7 @@ local function melee_skill_effectiveness(unit, skill_list)
-- Skills
-- Finding the highest skill
local skill_rating = 0
for _, skill in ipairs(skill_list) do
for _, skill in ipairs(MELEE_WEAPON_SKILLS) do
local melee_skill = dfhack.units.getNominalSkill(unit, skill, true)
skill_rating = math.max(skill_rating, melee_skill)
end
@ -140,36 +174,44 @@ local function melee_skill_effectiveness(unit, skill_list)
return rating
end
local function make_sort_by_melee_skill_effectiveness_desc(list)
local function get_melee_skill_effectiveness_rating(unit)
return get_rating(melee_skill_effectiveness(unit), 2000000)
end
local function make_sort_by_melee_skill_effectiveness_desc()
return function(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = melee_skill_effectiveness(unit1, list)
local rating2 = melee_skill_effectiveness(unit2, list)
local rating1 = melee_skill_effectiveness(unit1)
local rating2 = melee_skill_effectiveness(unit2)
if rating1 == rating2 then return sort_by_name_desc(unit_id_1, unit_id_2) end
return utils.compare(rating2, rating1)
end
end
local function make_sort_by_melee_skill_effectiveness_asc(list)
local function make_sort_by_melee_skill_effectiveness_asc()
return function(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = melee_skill_effectiveness(unit1, list)
local rating2 = melee_skill_effectiveness(unit2, list)
local rating1 = melee_skill_effectiveness(unit1)
local rating2 = melee_skill_effectiveness(unit2)
if rating1 == rating2 then return sort_by_name_desc(unit_id_1, unit_id_2) end
return utils.compare(rating1, rating2)
end
end
-- FUnction could easily be adapted to different weapon types.
local function ranged_skill_effectiveness(unit, skill_list)
local RANGED_WEAPON_SKILLS = {
df.job_skill.CROSSBOW,
}
-- Function could easily be adapted to different weapon types.
local function ranged_skill_effectiveness(unit)
-- Physical attributes
local agility = dfhack.units.getPhysicalAttrValue(unit, df.physical_attribute_type.AGILITY)
@ -181,7 +223,7 @@ local function ranged_skill_effectiveness(unit, skill_list)
-- Skills
-- Finding the highest skill
local skill_rating = 0
for _, skill in ipairs(skill_list) do
for _, skill in ipairs(RANGED_WEAPON_SKILLS) do
local ranged_skill = dfhack.units.getNominalSkill(unit, skill, true)
skill_rating = math.max(skill_rating, ranged_skill)
end
@ -192,6 +234,10 @@ local function ranged_skill_effectiveness(unit, skill_list)
return rating
end
local function get_ranged_skill_effectiveness_rating(unit)
return get_rating(ranged_skill_effectiveness(unit), 500000)
end
local function make_sort_by_ranged_skill_effectiveness_desc(list)
return function(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
@ -225,8 +271,8 @@ local function make_sort_by_skill_desc(sort_skill)
if unit_id_1 == unit_id_2 then return 0 end
if unit_id_1 == -1 then return -1 end
if unit_id_2 == -1 then return 1 end
local s1 = get_skill(unit_id_1, sort_skill)
local s2 = get_skill(unit_id_2, sort_skill)
local s1 = get_skill(sort_skill, df.unit.find(unit_id_1))
local s2 = get_skill(sort_skill, df.unit.find(unit_id_2))
if s1 == s2 then return sort_by_name_desc(unit_id_1, unit_id_2) end
if not s2 then return -1 end
if not s1 then return 1 end
@ -245,8 +291,8 @@ local function make_sort_by_skill_asc(sort_skill)
if unit_id_1 == unit_id_2 then return 0 end
if unit_id_1 == -1 then return -1 end
if unit_id_2 == -1 then return 1 end
local s1 = get_skill(unit_id_1, sort_skill)
local s2 = get_skill(unit_id_2, sort_skill)
local s1 = get_skill(sort_skill, df.unit.find(unit_id_1))
local s2 = get_skill(sort_skill, df.unit.find(unit_id_2))
if s1 == s2 then return sort_by_name_desc(unit_id_1, unit_id_2) end
if not s2 then return 1 end
if not s1 then return -1 end
@ -261,7 +307,7 @@ local function make_sort_by_skill_asc(sort_skill)
end
-- Statistical rating that is higher for dwarves that are mentally stable
local function mental_stability(unit)
local function get_mental_stability(unit)
local ALTRUISM = unit.status.current_soul.personality.traits.ALTRUISM
local ANXIETY_PROPENSITY = unit.status.current_soul.personality.traits.ANXIETY_PROPENSITY
local BRAVERY = unit.status.current_soul.personality.traits.BRAVERY
@ -301,14 +347,18 @@ local function mental_stability(unit)
return rating
end
local function get_mental_stability_rating(unit)
return get_rating(get_mental_stability(unit), 100, 10, 0)
end
local function sort_by_mental_stability_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = mental_stability(unit1)
local rating2 = mental_stability(unit2)
local rating1 = get_mental_stability(unit1)
local rating2 = get_mental_stability(unit2)
if rating1 == rating2 then
-- sorting by stress is opposite
-- more mental stable dwarves should have less stress
@ -323,8 +373,8 @@ local function sort_by_mental_stability_asc(unit_id_1, unit_id_2)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = mental_stability(unit1)
local rating2 = mental_stability(unit2)
local rating1 = get_mental_stability(unit1)
local rating2 = get_mental_stability(unit2)
if rating1 == rating2 then
return sort_by_stress_desc(unit_id_1, unit_id_2)
end
@ -334,7 +384,7 @@ end
-- Statistical rating that is higher for more potent dwarves in long run melee military training
-- Rating considers fighting melee opponents
-- Wounds are not considered!
local function melee_combat_potential(unit)
local function get_melee_combat_potential(unit)
-- Physical attributes
local strength = unit.body.physical_attrs.STRENGTH.max_value
local agility = unit.body.physical_attrs.AGILITY.max_value
@ -353,14 +403,18 @@ local function melee_combat_potential(unit)
return rating
end
local function get_melee_combat_potential_rating(unit)
return get_rating(get_melee_combat_potential(unit), 2000000)
end
local function sort_by_melee_combat_potential_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = melee_combat_potential(unit1)
local rating2 = melee_combat_potential(unit2)
local rating1 = get_melee_combat_potential(unit1)
local rating2 = get_melee_combat_potential(unit2)
if rating1 == rating2 then
return sort_by_mental_stability_desc(unit_id_1, unit_id_2)
end
@ -373,8 +427,8 @@ local function sort_by_melee_combat_potential_asc(unit_id_1, unit_id_2)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = melee_combat_potential(unit1)
local rating2 = melee_combat_potential(unit2)
local rating1 = get_melee_combat_potential(unit1)
local rating2 = get_melee_combat_potential(unit2)
if rating1 == rating2 then
return sort_by_mental_stability_asc(unit_id_1, unit_id_2)
end
@ -383,7 +437,7 @@ end
-- Statistical rating that is higher for more potent dwarves in long run ranged military training
-- Wounds are not considered!
local function ranged_combat_potential(unit)
local function get_ranged_combat_potential(unit)
-- Physical attributes
local agility = unit.body.physical_attrs.AGILITY.max_value
@ -397,14 +451,18 @@ local function ranged_combat_potential(unit)
return rating
end
local function get_ranged_combat_potential_rating(unit)
return get_rating(get_ranged_combat_potential(unit), 40000)
end
local function sort_by_ranged_combat_potential_desc(unit_id_1, unit_id_2)
if unit_id_1 == unit_id_2 then return 0 end
local unit1 = df.unit.find(unit_id_1)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = ranged_combat_potential(unit1)
local rating2 = ranged_combat_potential(unit2)
local rating1 = get_ranged_combat_potential(unit1)
local rating2 = get_ranged_combat_potential(unit2)
if rating1 == rating2 then
return sort_by_mental_stability_desc(unit_id_1, unit_id_2)
end
@ -417,18 +475,18 @@ local function sort_by_ranged_combat_potential_asc(unit_id_1, unit_id_2)
local unit2 = df.unit.find(unit_id_2)
if not unit1 then return -1 end
if not unit2 then return 1 end
local rating1 = ranged_combat_potential(unit1)
local rating2 = ranged_combat_potential(unit2)
local rating1 = get_ranged_combat_potential(unit1)
local rating2 = get_ranged_combat_potential(unit2)
if rating1 == rating2 then
return sort_by_mental_stability_asc(unit_id_1, unit_id_2)
end
return utils.compare(rating1, rating2)
end
local sort_by_any_melee_desc=make_sort_by_melee_skill_effectiveness_desc(MELEE_WEAPON_SKILLS)
local sort_by_any_melee_asc=make_sort_by_melee_skill_effectiveness_asc(MELEE_WEAPON_SKILLS)
local sort_by_any_ranged_desc=make_sort_by_ranged_skill_effectiveness_desc(RANGED_WEAPON_SKILLS)
local sort_by_any_ranged_asc=make_sort_by_ranged_skill_effectiveness_asc(RANGED_WEAPON_SKILLS)
local sort_by_any_melee_desc=make_sort_by_melee_skill_effectiveness_desc()
local sort_by_any_melee_asc=make_sort_by_melee_skill_effectiveness_asc()
local sort_by_any_ranged_desc=make_sort_by_ranged_skill_effectiveness_desc()
local sort_by_any_ranged_asc=make_sort_by_ranged_skill_effectiveness_asc()
local sort_by_teacher_desc=make_sort_by_skill_desc(df.job_skill.TEACHING)
local sort_by_teacher_asc=make_sort_by_skill_asc(df.job_skill.TEACHING)
local sort_by_tactics_desc=make_sort_by_skill_desc(df.job_skill.MILITARY_TACTICS)
@ -446,13 +504,38 @@ local sort_by_spear_asc=make_sort_by_skill_asc(df.job_skill.SPEAR)
local sort_by_crossbow_desc=make_sort_by_skill_desc(df.job_skill.CROSSBOW)
local sort_by_crossbow_asc=make_sort_by_skill_asc(df.job_skill.CROSSBOW)
local SORT_LIBRARY = {
{label='melee effectiveness', desc_fn=sort_by_any_melee_desc, asc_fn=sort_by_any_melee_asc, rating_fn=get_melee_skill_effectiveness_rating},
{label='ranged effectiveness', desc_fn=sort_by_any_ranged_desc, asc_fn=sort_by_any_ranged_asc, rating_fn=get_ranged_skill_effectiveness_rating},
{label='name', desc_fn=sort_by_name_desc, asc_fn=sort_by_name_asc},
{label='teacher skill', desc_fn=sort_by_teacher_desc, asc_fn=sort_by_teacher_asc, rating_fn=curry(get_skill_rating, df.job_skill.TEACHING)},
{label='tactics skill', desc_fn=sort_by_tactics_desc, asc_fn=sort_by_tactics_asc, rating_fn=curry(get_skill_rating, df.job_skill.MILITARY_TACTICS)},
{label='migrant wave', desc_fn=sort_by_migrant_wave_desc, asc_fn=sort_by_migrant_wave_asc, rating_fn=get_migrant_wave_rating},
{label='stress level', desc_fn=sort_by_stress_desc, asc_fn=sort_by_stress_asc, rating_fn=get_stress_rating},
{label='mental stability', desc_fn=sort_by_mental_stability_desc, asc_fn=sort_by_mental_stability_asc, rating_fn=get_mental_stability_rating},
{label='axe skill', desc_fn=sort_by_axe_desc, asc_fn=sort_by_axe_asc, rating_fn=curry(get_skill_rating, df.job_skill.AXE)},
{label='sword skill', desc_fn=sort_by_sword_desc, asc_fn=sort_by_sword_asc, rating_fn=curry(get_skill_rating, df.job_skill.SWORD)},
{label='mace skill', desc_fn=sort_by_mace_desc, asc_fn=sort_by_mace_asc, rating_fn=curry(get_skill_rating, df.job_skill.MACE)},
{label='hammer skill', desc_fn=sort_by_hammer_desc, asc_fn=sort_by_hammer_asc, rating_fn=curry(get_skill_rating, df.job_skill.HAMMER)},
{label='spear skill', desc_fn=sort_by_spear_desc, asc_fn=sort_by_spear_asc, rating_fn=curry(get_skill_rating, df.job_skill.SPEAR)},
{label='crossbow skill', desc_fn=sort_by_crossbow_desc, asc_fn=sort_by_crossbow_asc, rating_fn=curry(get_skill_rating, df.job_skill.CROSSBOW)},
{label='melee potential', desc_fn=sort_by_melee_combat_potential_desc, asc_fn=sort_by_melee_combat_potential_asc, rating_fn=get_melee_combat_potential_rating},
{label='ranged potential', desc_fn=sort_by_ranged_combat_potential_desc, asc_fn=sort_by_ranged_combat_potential_asc, rating_fn=get_ranged_combat_potential_rating},
}
local RATING_FNS = {}
for _, opt in ipairs(SORT_LIBRARY) do
RATING_FNS[opt.desc_fn] = opt.rating_fn
RATING_FNS[opt.asc_fn] = opt.rating_fn
end
-- ----------------------
-- SquadAssignmentOverlay
--
SquadAssignmentOverlay = defclass(SquadAssignmentOverlay, overlay.OverlayWidget)
SquadAssignmentOverlay.ATTRS{
default_pos={x=23, y=5},
default_pos={x=18, y=5},
default_enabled=true,
viewscreens='dwarfmode/UnitSelector/SQUAD_FILL_POSITION',
frame={w=38, h=25},
@ -472,6 +555,20 @@ end
function SquadAssignmentOverlay:init()
self.dirty = true
local sort_options = {}
for _, opt in ipairs(SORT_LIBRARY) do
table.insert(sort_options, {
label=opt.label..CH_DN,
value=opt.desc_fn,
pen=COLOR_GREEN,
})
table.insert(sort_options, {
label=opt.label..CH_UP,
value=opt.asc_fn,
pen=COLOR_YELLOW,
})
end
self:addviews{
widgets.EditField{
view_id='search',
@ -489,40 +586,7 @@ function SquadAssignmentOverlay:init()
frame={t=0, l=0},
label='Sort by:',
key='CUSTOM_SHIFT_S',
options={
{label='melee effectiveness'..CH_DN, value=sort_by_any_melee_desc, pen=COLOR_GREEN},
{label='melee effectiveness'..CH_UP, value=sort_by_any_melee_asc, pen=COLOR_YELLOW},
{label='ranged effectiveness'..CH_DN, value=sort_by_any_ranged_desc, pen=COLOR_GREEN},
{label='ranged effectiveness'..CH_UP, value=sort_by_any_ranged_asc, pen=COLOR_YELLOW},
{label='name'..CH_DN, value=sort_by_name_desc, pen=COLOR_GREEN},
{label='name'..CH_UP, value=sort_by_name_asc, pen=COLOR_YELLOW},
{label='teacher skill'..CH_DN, value=sort_by_teacher_desc, pen=COLOR_GREEN},
{label='teacher skill'..CH_UP, value=sort_by_teacher_asc, pen=COLOR_YELLOW},
{label='tactics skill'..CH_DN, value=sort_by_tactics_desc, pen=COLOR_GREEN},
{label='tactics skill'..CH_UP, value=sort_by_tactics_asc, pen=COLOR_YELLOW},
{label='migrant wave'..CH_DN, value=sort_by_migrant_wave_desc, pen=COLOR_GREEN},
{label='migrant wave'..CH_UP, value=sort_by_migrant_wave_asc, pen=COLOR_YELLOW},
{label='stress level'..CH_DN, value=sort_by_stress_desc, pen=COLOR_GREEN},
{label='stress level'..CH_UP, value=sort_by_stress_asc, pen=COLOR_YELLOW},
{label='mental stability'..CH_DN, value=sort_by_mental_stability_desc, pen=COLOR_GREEN},
{label='mental stability'..CH_UP, value=sort_by_mental_stability_asc, pen=COLOR_YELLOW},
{label='axe skill'..CH_DN, value=sort_by_axe_desc, pen=COLOR_GREEN},
{label='axe skill'..CH_UP, value=sort_by_axe_asc, pen=COLOR_YELLOW},
{label='sword skill'..CH_DN, value=sort_by_sword_desc, pen=COLOR_GREEN},
{label='sword skill'..CH_UP, value=sort_by_sword_asc, pen=COLOR_YELLOW},
{label='mace skill'..CH_DN, value=sort_by_mace_desc, pen=COLOR_GREEN},
{label='mace skill'..CH_UP, value=sort_by_mace_asc, pen=COLOR_YELLOW},
{label='hammer skill'..CH_DN, value=sort_by_hammer_desc, pen=COLOR_GREEN},
{label='hammer skill'..CH_UP, value=sort_by_hammer_asc, pen=COLOR_YELLOW},
{label='spear skill'..CH_DN, value=sort_by_spear_desc, pen=COLOR_GREEN},
{label='spear skill'..CH_UP, value=sort_by_spear_asc, pen=COLOR_YELLOW},
{label='crossbow skill'..CH_DN, value=sort_by_crossbow_desc, pen=COLOR_GREEN},
{label='crossbow skill'..CH_UP, value=sort_by_crossbow_asc, pen=COLOR_YELLOW},
{label='melee potential'..CH_DN, value=sort_by_melee_combat_potential_desc, pen=COLOR_GREEN},
{label='melee potential'..CH_UP, value=sort_by_melee_combat_potential_asc, pen=COLOR_YELLOW},
{label='ranged potential'..CH_DN, value=sort_by_ranged_combat_potential_desc, pen=COLOR_GREEN},
{label='ranged potential'..CH_UP, value=sort_by_ranged_combat_potential_asc, pen=COLOR_YELLOW},
},
options=sort_options,
initial_option=sort_by_any_melee_desc,
on_change=self:callback('refresh_list', 'sort'),
},
@ -846,6 +910,29 @@ local function filter_vector(filter, prev_filter)
unit_selector.unid:erase(idx)
end
end
-- fix up scroll position if it would be off the end of the list
if unit_selector.scroll_position + 10 > #unit_selector.unid then
unit_selector.scroll_position = math.max(0, #unit_selector.unid - 10)
end
end
local rating_annotations = {}
local function annotate_visible_units(sort_fn)
rating_annotations = {}
rating_fn = RATING_FNS[sort_fn]
local max_idx = math.min(#unit_selector.unid-1, unit_selector.scroll_position+9)
for idx = unit_selector.scroll_position, max_idx do
local annotation_idx = idx - unit_selector.scroll_position + 1
local unit = df.unit.find(unit_selector.unid[idx])
rating_annotations[annotation_idx] = nil
if unit and rating_fn then
local val, color = rating_fn(unit)
if val then
rating_annotations[annotation_idx] = {val=val, color=color}
end
end
end
end
local SORT_WIDGET_NAMES = {
@ -887,6 +974,8 @@ function SquadAssignmentOverlay:refresh_list(sort_widget, sort_fn)
filter_vector(filter, self.prev_filter or {})
self.prev_filter = filter
utils.sort_vector(unit_selector.unid, nil, sort_fn)
annotate_visible_units(sort_fn)
self.saved_scroll_position = unit_selector.scroll_position
end
function SquadAssignmentOverlay:onInput(keys)
@ -904,11 +993,59 @@ function SquadAssignmentOverlay:onRenderFrame(dc, frame_rect)
if self.dirty then
self:refresh_list()
self.dirty = false
elseif self.saved_scroll_position ~= unit_selector.scroll_position then
annotate_visible_units(self.subviews.sort:getOptionValue())
self.saved_scroll_position = unit_selector.scroll_position
end
end
-- ----------------------
-- SquadAnnotationOverlay
--
SquadAnnotationOverlay = defclass(SquadAnnotationOverlay, overlay.OverlayWidget)
SquadAnnotationOverlay.ATTRS{
default_pos={x=56, y=5},
default_enabled=true,
viewscreens='dwarfmode/UnitSelector/SQUAD_FILL_POSITION',
frame={w=5, h=35},
frame_style=gui.FRAME_INTERIOR_MEDIUM,
frame_background=gui.CLEAR_PEN,
}
function get_annotation_text(idx)
local elem = rating_annotations[idx]
if not elem or not tonumber(elem.val) then return ' - ' end
return tostring(math.tointeger(elem.val))
end
function get_annotation_color(idx)
local elem = rating_annotations[idx]
return elem and elem.color or nil
end
function SquadAnnotationOverlay:init()
for idx = 1, 10 do
self:addviews{
widgets.Label{
frame={t=idx*3+1, h=1, w=3},
text={
{
text=curry(get_annotation_text, idx),
pen=curry(get_annotation_color, idx),
width=3,
rjustify=true,
},
},
},
}
end
end
OVERLAY_WIDGETS = {
squad_assignment=SquadAssignmentOverlay,
squad_annotation=SquadAnnotationOverlay,
}
--[[