WIP - More logical categories and variable windows

develop
Anuradha Dissanayake 2013-03-02 21:58:36 +13:00
parent 701b7b7d9b
commit 63707471f4
3 changed files with 405 additions and 123 deletions

@ -278,12 +278,12 @@ public:
// Category masks // Category masks
auto masks = masks_column.getSelectedElems(); auto masks = masks_column.getSelectedElems();
for_each_(masks, for_each_(masks,
[&] (df::dfhack_material_category *m) { filter->mat_mask.whole |= m->whole; }); [&] (df::dfhack_material_category &m) { filter->mat_mask.whole |= m.whole; });
// Specific materials // Specific materials
auto materials = materials_column.getSelectedElems(); auto materials = materials_column.getSelectedElems();
transform_(materials, filter->materials, transform_(materials, filter->materials,
[] (MaterialInfo *m) { return *m; }); [] (MaterialInfo &m) { return m; });
Screen::dismiss(this); Screen::dismiss(this);
} }
@ -349,7 +349,6 @@ private:
entry.selected = true; entry.selected = true;
masks_column.add(entry); masks_column.add(entry);
mask.whole = 0;
} }
void populateMasks() void populateMasks()
@ -357,12 +356,19 @@ private:
masks_column.clear(); masks_column.clear();
df::dfhack_material_category mask; df::dfhack_material_category mask;
mask.whole = 0;
mask.bits.stone = true; mask.bits.stone = true;
addMaskEntry(mask, "Stone"); addMaskEntry(mask, "Stone");
mask.whole = 0;
mask.bits.wood = true; mask.bits.wood = true;
addMaskEntry(mask, "Wood"); addMaskEntry(mask, "Wood");
mask.whole = 0;
mask.bits.metal = true; mask.bits.metal = true;
addMaskEntry(mask, "Metal"); addMaskEntry(mask, "Metal");
mask.whole = 0;
mask.bits.soap = true; mask.bits.soap = true;
addMaskEntry(mask, "Soap"); addMaskEntry(mask, "Soap");
@ -373,9 +379,9 @@ private:
{ {
materials_column.clear(); materials_column.clear();
df::dfhack_material_category selected_category; df::dfhack_material_category selected_category;
vector<df::dfhack_material_category *> selected_masks = masks_column.getSelectedElems(); vector<df::dfhack_material_category> selected_masks = masks_column.getSelectedElems();
if (selected_masks.size() == 1) if (selected_masks.size() == 1)
selected_category = *selected_masks[0]; selected_category = selected_masks[0];
else if (selected_masks.size() > 1) else if (selected_masks.size() > 1)
return; return;
@ -406,14 +412,6 @@ private:
for (size_t i = 0; i < raws.plants.all.size(); i++) for (size_t i = 0; i < raws.plants.all.size(); i++)
{ {
df::plant_raw *p = raws.plants.all[i]; df::plant_raw *p = raws.plants.all[i];
//string basename = p->name;
/*MaterialInfo material;
material.decode(p->material_defs.type_basic_mat, p->material_defs.idx_basic_mat);*/
//addMaterialEntry(selected_category, material, material.toString());
for (size_t j = 0; p->material.size() > 1 && j < p->material.size(); j++) for (size_t j = 0; p->material.size() > 1 && j < p->material.size(); j++)
{ {
auto t = p->material[j]; auto t = p->material[j];
@ -422,18 +420,15 @@ private:
MaterialInfo material; MaterialInfo material;
material.decode(DFHack::MaterialInfo::PLANT_BASE+j, i); material.decode(DFHack::MaterialInfo::PLANT_BASE+j, i);
//addMaterialEntry(selected_category, material, material.toString());
auto name = material.toString(); auto name = material.toString();
ListEntry<MaterialInfo> entry(pad_string(name, MAX_MATERIAL, false), material); ListEntry<MaterialInfo> entry(pad_string(name, MAX_MATERIAL, false), material);
if (filter->matches(material)) if (filter->matches(material))
entry.selected = true; entry.selected = true;
materials_column.add(entry); materials_column.add(entry);
} }
} }
} }
materials_column.sort(); materials_column.sort();
} }

@ -15,6 +15,8 @@
#include "modules/Translation.h" #include "modules/Translation.h"
#include "modules/World.h" #include "modules/World.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "df/activity_event.h"
#include "df/activity_entry.h"
using std::deque; using std::deque;
@ -83,67 +85,37 @@ static void move_cursor(df::coord &pos)
static void open_stats_srceen(); static void open_stats_srceen();
#define JOB_UNKNOWN -2
#define JOB_IDLE -1 #define JOB_IDLE -1
#define JOB_UNKNOWN -2
#define JOB_MILITARY -3 #define JOB_MILITARY -3
#define JOB_LEISURE -4 #define JOB_LEISURE -4
#define JOB_OTHER -5 #define JOB_UNPRODUCTIVE -5
#define JOB_DESIG_SLOPE -6 #define JOB_DESIGNATE -6
#define JOB_STORE_ITEM -7 #define JOB_STORE_ITEM -7
#define JOB_CONSTRUCT_FURNITURE -8 #define JOB_MANUFACTURE -8
#define JOB_DETAILING -9 #define JOB_DETAILING -9
#define JOB_HUNTING -10 #define JOB_HUNTING -10
#define JOB_CLOTING -11
#define JOB_ARMOUR -12
#define JOB_WEAPONS -13
#define JOB_MEDICAL -14 #define JOB_MEDICAL -14
#define JOB_COLLECT -15
#define JOB_CONSTRUCTION -16
#define JOB_AGRICULTURE -17
#define JOB_FOOD_PROD -18
#define JOB_MECHANICAL -19
#define JOB_ANIMALS -20
#define JOB_PRODUCTIVE -21
static map<activity_type, string> activity_labels;
static string getActivityLabel(const activity_type & activity) static string getActivityLabel(const activity_type activity)
{ {
string label; string label;
switch (activity)
{
case JOB_IDLE:
label = "Idle";
break;
case JOB_MILITARY:
label = "Military Duty";
break;
case JOB_LEISURE:
label = "Leisure";
break;
case JOB_OTHER:
label = "Other";
break;
case JOB_DESIG_SLOPE:
label = "Designate Stair/Slope";
break;
case JOB_STORE_ITEM:
label = "Store Item";
break;
case JOB_CONSTRUCT_FURNITURE:
label = "Construct Furniture";
break;
case JOB_DETAILING:
label = "Detailing";
break;
case JOB_HUNTING:
label = "Hunting";
break;
case JOB_CLOTING:
label = "Make Clothing";
break;
case JOB_ARMOUR:
label = "Make Armor";
break;
case JOB_WEAPONS:
label = "Make Weapons";
break;
case JOB_MEDICAL:
label = "Medical";
break;
default: if (activity_labels.find(activity) != activity_labels.end())
{
label = activity_labels[activity];
}
else
{
string raw_label = enum_item_key_str(static_cast<df::job_type>(activity)); string raw_label = enum_item_key_str(static_cast<df::job_type>(activity));
for (auto c = raw_label.begin(); c != raw_label.end(); c++) for (auto c = raw_label.begin(); c != raw_label.end(); c++)
{ {
@ -152,8 +124,6 @@ static string getActivityLabel(const activity_type & activity)
label += *c; label += *c;
} }
break;
} }
return label; return label;
@ -172,7 +142,20 @@ public:
dwarf_activity_column.auto_select = true; dwarf_activity_column.auto_select = true;
dwarf_activity_column.setTitle("Dwarf Activity"); dwarf_activity_column.setTitle("Dwarf Activity");
for (auto it = work_history.begin(); it != work_history.end();) window_days = min_window;
populateDwarfColumn(starting_selection);
}
void populateDwarfColumn(df::unit *starting_selection = NULL)
{
selected_column = 0;
auto last_selected_index = dwarf_activity_column.highlighted_index;
dwarf_activity_column.clear();
dwarf_activity_values.clear();
for (auto it = work_history.begin(); it != work_history.end(); it++)
{ {
auto unit = it->first; auto unit = it->first;
if (Units::isDead(unit)) if (Units::isDead(unit))
@ -186,7 +169,8 @@ public:
size_t dwarf_total = 0; size_t dwarf_total = 0;
dwarf_activity_values[unit] = map<activity_type, size_t>(); dwarf_activity_values[unit] = map<activity_type, size_t>();
for (auto entry = work_list->begin(); entry != work_list->end(); entry++) size_t count = window_days * ticks_per_day;
for (auto entry = work_list->rbegin(); entry != work_list->rend() && count > 0; entry++, count--)
{ {
if (*entry == JOB_UNKNOWN || *entry == job_type::DrinkBlood) if (*entry == JOB_UNKNOWN || *entry == job_type::DrinkBlood)
continue; continue;
@ -197,23 +181,19 @@ public:
for_each_(dwarf_activity_values[unit], for_each_(dwarf_activity_values[unit],
[&] (const pair<activity_type, size_t> &x) [&] (const pair<activity_type, size_t> &x)
{ dwarf_activity_values[unit][x.first] = getPercentage(x.second, dwarf_total); } ); { dwarf_activity_values[unit][x.first] = getPercentage(x.second, dwarf_total); } );
dwarves_column.add(getUnitName(unit), *unit); dwarves_column.add(getUnitName(unit), unit);
} }
dwarf_activity_column.left_margin = dwarves_column.fixWidth() + 2; dwarf_activity_column.left_margin = dwarves_column.fixWidth() + 2;
dwarves_column.filterDisplay(); dwarves_column.filterDisplay();
dwarves_column.selectItem(starting_selection); if (starting_selection)
populateActivityColumn(); dwarves_column.selectItem(starting_selection);
} else
dwarves_column.setHighlight(last_selected_index);
void addDwarfActivity(df::unit *unit, const activity_type &activity)
{
if (dwarf_activity_values[unit].find(activity) == dwarf_activity_values[unit].end())
dwarf_activity_values[unit][activity] = 0;
dwarf_activity_values[unit][activity]++; populateActivityColumn();
} }
void populateActivityColumn() void populateActivityColumn()
@ -242,6 +222,14 @@ public:
dwarf_activity_column.setHighlight(0); dwarf_activity_column.setHighlight(0);
} }
void addDwarfActivity(df::unit *unit, const activity_type &activity)
{
if (dwarf_activity_values[unit].find(activity) == dwarf_activity_values[unit].end())
dwarf_activity_values[unit][activity] = 0;
dwarf_activity_values[unit][activity]++;
}
string getActivityItem(activity_type activity, size_t value) string getActivityItem(activity_type activity, size_t value)
{ {
return pad_string(int_to_string(value), 3) + " " + getActivityLabel(activity); return pad_string(int_to_string(value), 3) + " " + getActivityLabel(activity);
@ -291,6 +279,14 @@ public:
move_cursor(selected_unit->pos); move_cursor(selected_unit->pos);
} }
} }
else if (input->count(interface_key::SECONDSCROLL_PAGEDOWN))
{
window_days += min_window;
if (window_days > max_history_days)
window_days = min_window;
populateDwarfColumn();
}
else if (input->count(interface_key::CURSOR_LEFT)) else if (input->count(interface_key::CURSOR_LEFT))
{ {
--selected_column; --selected_column;
@ -328,11 +324,18 @@ public:
dwarves_column.display(selected_column == 0); dwarves_column.display(selected_column == 0);
dwarf_activity_column.display(selected_column == 1); dwarf_activity_column.display(selected_column == 1);
int32_t y = gps->dimy - 3; int32_t y = gps->dimy - 4;
int32_t x = 2; int32_t x = 2;
OutputHotkeyString(x, y, "Leave", "Esc"); OutputHotkeyString(x, y, "Leave", "Esc");
x += 3;
x += 13;
string window_label = "Window Months: " + int_to_string(window_days / min_window);
OutputHotkeyString(x, y, window_label.c_str(), "*");
++y;
x = 2;
OutputHotkeyString(x, y, "Fort Stats", "Shift-D"); OutputHotkeyString(x, y, "Fort Stats", "Shift-D");
x += 3; x += 3;
OutputHotkeyString(x, y, "Zoom Unit", "Shift-Z"); OutputHotkeyString(x, y, "Zoom Unit", "Shift-Z");
} }
@ -340,9 +343,10 @@ public:
std::string getFocusString() { return "dwarfmonitor_dwarfstats"; } std::string getFocusString() { return "dwarfmonitor_dwarfstats"; }
private: private:
ListColumn<df::unit> dwarves_column; ListColumn<df::unit *> dwarves_column;
ListColumn<activity_type> dwarf_activity_column; ListColumn<activity_type> dwarf_activity_column;
int selected_column; int selected_column;
size_t window_days;
map<df::unit *, map<activity_type, size_t>> dwarf_activity_values; map<df::unit *, map<activity_type, size_t>> dwarf_activity_values;
@ -374,6 +378,10 @@ public:
dwarf_activity_column.auto_select = true; dwarf_activity_column.auto_select = true;
dwarf_activity_column.setTitle("Units on Activity"); dwarf_activity_column.setTitle("Units on Activity");
dwarf_activity_column.bottom_margin = 4; dwarf_activity_column.bottom_margin = 4;
dwarf_activity_column.text_clip_at = 25;
category_breakdown_column.setTitle("Category Breakdown");
category_breakdown_column.bottom_margin = 4;
window_days = min_window; window_days = min_window;
@ -389,6 +397,7 @@ public:
fort_activity_column.clear(); fort_activity_column.clear();
fort_activity_totals.clear(); fort_activity_totals.clear();
dwarf_activity_values.clear(); dwarf_activity_values.clear();
category_breakdown.clear();
for (auto it = work_history.begin(); it != work_history.end();) for (auto it = work_history.begin(); it != work_history.end();)
{ {
@ -442,7 +451,8 @@ public:
case job_type::CauseTrouble: case job_type::CauseTrouble:
case job_type::ReportCrime: case job_type::ReportCrime:
case job_type::BeatCriminal: case job_type::BeatCriminal:
real_activity = JOB_OTHER; case job_type::ExecuteCriminal:
real_activity = JOB_UNPRODUCTIVE;
break; break;
case job_type::CarveUpwardStaircase: case job_type::CarveUpwardStaircase:
@ -450,7 +460,10 @@ public:
case job_type::CarveUpDownStaircase: case job_type::CarveUpDownStaircase:
case job_type::CarveRamp: case job_type::CarveRamp:
case job_type::DigChannel: case job_type::DigChannel:
real_activity = JOB_DESIG_SLOPE; case job_type::Dig:
case job_type::CarveTrack:
case job_type::CarveFortification:
real_activity = JOB_DESIGNATE;
break; break;
case job_type::StoreOwnedItem: case job_type::StoreOwnedItem:
@ -464,6 +477,17 @@ public:
case job_type::StoreArmor: case job_type::StoreArmor:
case job_type::StoreItemInBarrel: case job_type::StoreItemInBarrel:
case job_type::StoreItemInBin: case job_type::StoreItemInBin:
case job_type::BringItemToDepot:
case job_type::BringItemToShop:
case job_type::GetProvisions:
case job_type::FillWaterskin:
case job_type::FillWaterskin2:
case job_type::CheckChest:
case job_type::PickupEquipment:
case job_type::DumpItem:
case job_type::PushTrackVehicle:
case job_type::PlaceTrackVehicle:
case job_type::StoreItemInVehicle:
real_activity = JOB_STORE_ITEM; real_activity = JOB_STORE_ITEM;
break; break;
@ -479,7 +503,76 @@ public:
case job_type::ConstructWeaponRack: case job_type::ConstructWeaponRack:
case job_type::ConstructCabinet: case job_type::ConstructCabinet:
case job_type::ConstructStatue: case job_type::ConstructStatue:
real_activity = JOB_CONSTRUCT_FURNITURE; case job_type::ConstructBlocks:
case job_type::MakeRawGlass:
case job_type::MakeCrafts:
case job_type::MintCoins:
case job_type::CutGems:
case job_type::CutGlass:
case job_type::EncrustWithGems:
case job_type::EncrustWithGlass:
case job_type::SmeltOre:
case job_type::MeltMetalObject:
case job_type::ExtractMetalStrands:
case job_type::MakeWeapon:
case job_type::ForgeAnvil:
case job_type::ConstructCatapultParts:
case job_type::ConstructBallistaParts:
case job_type::MakeArmor:
case job_type::MakeHelm:
case job_type::MakePants:
case job_type::StudWith:
case job_type::ProcessPlantsBag:
case job_type::ProcessPlantsVial:
case job_type::ProcessPlantsBarrel:
case job_type::WeaveCloth:
case job_type::MakeGloves:
case job_type::MakeShoes:
case job_type::MakeShield:
case job_type::MakeCage:
case job_type::MakeChain:
case job_type::MakeFlask:
case job_type::MakeGoblet:
case job_type::MakeInstrument:
case job_type::MakeToy:
case job_type::MakeAnimalTrap:
case job_type::MakeBarrel:
case job_type::MakeBucket:
case job_type::MakeWindow:
case job_type::MakeTotem:
case job_type::MakeAmmo:
case job_type::DecorateWith:
case job_type::MakeBackpack:
case job_type::MakeQuiver:
case job_type::MakeBallistaArrowHead:
case job_type::AssembleSiegeAmmo:
case job_type::ConstructMechanisms:
case job_type::MakeTrapComponent:
case job_type::ExtractFromPlants:
case job_type::ExtractFromRawFish:
case job_type::ExtractFromLandAnimal:
case job_type::MakeCharcoal:
case job_type::MakeAsh:
case job_type::MakeLye:
case job_type::MakePotashFromLye:
case job_type::MakePotashFromAsh:
case job_type::DyeThread:
case job_type::DyeCloth:
case job_type::SewImage:
case job_type::MakePipeSection:
case job_type::ConstructHatchCover:
case job_type::ConstructGrate:
case job_type::ConstructQuern:
case job_type::ConstructMillstone:
case job_type::ConstructSplint:
case job_type::ConstructCrutch:
case job_type::ConstructTractionBench:
case job_type::CustomReaction:
case job_type::ConstructSlab:
case job_type::EngraveSlab:
case job_type::SpinThread:
case job_type::MakeTool:
real_activity = JOB_MANUFACTURE;
break; break;
case job_type::DetailFloor: case job_type::DetailFloor:
@ -490,14 +583,121 @@ public:
case job_type::Hunt: case job_type::Hunt:
case job_type::ReturnKill: case job_type::ReturnKill:
case job_type::HuntVermin: case job_type::HuntVermin:
case job_type::GatherPlants:
case job_type::Fish:
case job_type::CatchLiveFish:
case job_type::BaitTrap:
case job_type::InstallColonyInHive:
real_activity = JOB_HUNTING; real_activity = JOB_HUNTING;
break; break;
case job_type::RemoveConstruction:
case job_type::DestroyBuilding:
case job_type::RemoveStairs:
case job_type::ConstructBuilding:
real_activity = JOB_CONSTRUCTION;
break;
case job_type::FellTree:
case job_type::CollectWebs:
case job_type::CollectSand:
case job_type::DrainAquarium:
case job_type::FillAquarium:
case job_type::FillPond:
case job_type::CollectClay:
real_activity = JOB_COLLECT;
break;
case job_type::TrainHuntingAnimal:
case job_type::TrainWarAnimal:
case job_type::CatchLiveLandAnimal:
case job_type::TameVermin:
case job_type::TameAnimal:
case job_type::ChainAnimal:
case job_type::UnchainAnimal:
case job_type::UnchainPet:
case job_type::ReleaseLargeCreature:
case job_type::ReleasePet:
case job_type::ReleaseSmallCreature:
case job_type::HandleSmallCreature:
case job_type::HandleLargeCreature:
case job_type::CageLargeCreature:
case job_type::CageSmallCreature:
case job_type::PitLargeAnimal:
case job_type::PitSmallAnimal:
case job_type::SlaughterAnimal:
case job_type::ShearCreature:
case job_type::PenLargeAnimal:
case job_type::PenSmallAnimal:
case job_type::TrainAnimal:
real_activity = JOB_ANIMALS;
break;
case job_type::PlantSeeds:
case job_type::HarvestPlants:
case job_type::FertilizeField:
real_activity = JOB_AGRICULTURE;
break;
case job_type::ButcherAnimal:
case job_type::PrepareRawFish:
case job_type::MillPlants:
case job_type::MilkCreature:
case job_type::MakeCheese:
case job_type::PrepareMeal:
case job_type::ProcessPlants:
case job_type::BrewDrink:
case job_type::CollectHiveProducts:
real_activity = JOB_FOOD_PROD;
break;
case job_type::LoadCatapult:
case job_type::LoadBallista:
case job_type::FireCatapult:
case job_type::FireBallista:
real_activity = JOB_MILITARY;
break;
case job_type::LoadCageTrap:
case job_type::LoadStoneTrap:
case job_type::LoadWeaponTrap:
case job_type::CleanTrap:
case job_type::LinkBuildingToTrigger:
case job_type::PullLever:
real_activity = JOB_MECHANICAL;
break;
case job_type::RecoverWounded:
case job_type::DiagnosePatient:
case job_type::ImmobilizeBreak:
case job_type::DressWound:
case job_type::CleanPatient:
case job_type::Surgery:
case job_type::Suture:
case job_type::SetBone:
case job_type::PlaceInTraction:
case job_type::GiveWater:
case job_type::GiveFood:
case job_type::GiveWater2:
case job_type::GiveFood2:
case job_type::BringCrutch:
case job_type::ApplyCast:
real_activity = JOB_MEDICAL;
break;
case job_type::OperatePump:
case job_type::ManageWorkOrders:
case job_type::UpdateStockpileRecords:
case job_type::TradeAtDepot:
real_activity = JOB_PRODUCTIVE;
break;
default: default:
break; break;
} }
addFortActivity(real_activity); addFortActivity(real_activity);
addCategoryActivity(real_activity, *entry);
} }
if (dwarf_activity_values.find(real_activity) == dwarf_activity_values.end()) if (dwarf_activity_values.find(real_activity) == dwarf_activity_values.end())
@ -526,38 +726,72 @@ public:
} }
} }
for (auto cat_it = category_breakdown.begin(); cat_it != category_breakdown.end(); cat_it++)
{
auto cat_total = fort_activity_totals[cat_it->first];
for (auto val_it = cat_it->second.begin(); val_it != cat_it->second.end(); val_it++)
{
category_breakdown[cat_it->first][val_it->first] = getPercentage(val_it->second, cat_total);
}
}
dwarf_activity_column.left_margin = fort_activity_column.fixWidth() + 2; dwarf_activity_column.left_margin = fort_activity_column.fixWidth() + 2;
fort_activity_column.filterDisplay(); fort_activity_column.filterDisplay();
fort_activity_column.setHighlight(last_selected_index); fort_activity_column.setHighlight(last_selected_index);
populateDwarfColumn(); populateDwarfColumn();
populateCategoryBreakdownColumn();
} }
void populateDwarfColumn() void populateDwarfColumn()
{ {
dwarf_activity_column.clear(); dwarf_activity_column.clear();
if (fort_activity_column.getDisplayListSize() > 0)
{
activity_type selected_activity = fort_activity_column.getFirstSelectedElem();
auto dwarf_activities = &dwarf_activity_values[selected_activity];
if (dwarf_activities)
{
vector<pair<df::unit *, size_t>> rev_vec(dwarf_activities->begin(), dwarf_activities->end());
sort(rev_vec.begin(), rev_vec.end(), less_second<df::unit *, size_t>());
for_each_(rev_vec,
[&] (pair<df::unit *, size_t> x)
{ dwarf_activity_column.add(getDwarfAverage(x.first, x.second), x.first); });
}
}
category_breakdown_column.left_margin = dwarf_activity_column.fixWidth() + 2;
dwarf_activity_column.clearSearch();
dwarf_activity_column.setHighlight(0);
}
void populateCategoryBreakdownColumn()
{
category_breakdown_column.clear();
if (fort_activity_column.getDisplayListSize() == 0) if (fort_activity_column.getDisplayListSize() == 0)
return; return;
auto dwarf_activities = fort_activity_column.getFirstSelectedElem(); auto selected_activity = fort_activity_column.getFirstSelectedElem();
if (dwarf_activities) auto category_activities = &category_breakdown[selected_activity];
if (category_activities)
{ {
vector<pair<df::unit *, size_t>> rev_vec(dwarf_activities->begin(), dwarf_activities->end()); vector<pair<activity_type, size_t>> rev_vec(category_activities->begin(), category_activities->end());
sort(rev_vec.begin(), rev_vec.end(), less_second<df::unit *, size_t>()); sort(rev_vec.begin(), rev_vec.end(), less_second<activity_type, size_t>());
for_each_(rev_vec, for_each_(rev_vec,
[&] (pair<df::unit *, size_t> x) [&] (pair<activity_type, size_t> x)
{ dwarf_activity_column.add(getDwarfAverage(x.first, x.second), *x.first); }); { category_breakdown_column.add(getBreakdownAverage(x.first, x.second), x.first); });
} }
dwarf_activity_column.fixWidth(); category_breakdown_column.fixWidth();
dwarf_activity_column.clearSearch(); category_breakdown_column.clearSearch();
dwarf_activity_column.setHighlight(0); category_breakdown_column.setHighlight(0);
} }
void addToFortAverageColumn(activity_type type) void addToFortAverageColumn(activity_type &type)
{ {
if (getFortActivityCount(type)) if (getFortActivityCount(type))
fort_activity_column.add(getFortAverage(type), dwarf_activity_values[type]); fort_activity_column.add(getFortAverage(type), type);
} }
string getFortAverage(const activity_type &activity) string getFortAverage(const activity_type &activity)
@ -577,7 +811,15 @@ public:
return result; return result;
} }
size_t getFortActivityCount(const activity_type &activity) string getBreakdownAverage(activity_type activity, const size_t value)
{
auto label = getActivityLabel(activity);
auto result = pad_string(int_to_string(value), 3) + " " + label;
return result;
}
size_t getFortActivityCount(const activity_type activity)
{ {
if (fort_activity_totals.find(activity) == fort_activity_totals.end()) if (fort_activity_totals.find(activity) == fort_activity_totals.end())
return 0; return 0;
@ -585,7 +827,7 @@ public:
return fort_activity_totals[activity]; return fort_activity_totals[activity];
} }
void addFortActivity(const activity_type &activity) void addFortActivity(const activity_type activity)
{ {
if (fort_activity_totals.find(activity) == fort_activity_totals.end()) if (fort_activity_totals.find(activity) == fort_activity_totals.end())
fort_activity_totals[activity] = 0; fort_activity_totals[activity] = 0;
@ -593,6 +835,17 @@ public:
fort_activity_totals[activity]++; fort_activity_totals[activity]++;
} }
void addCategoryActivity(const int category, const activity_type activity)
{
if (category_breakdown.find(category) == category_breakdown.end())
category_breakdown[category] = map<activity_type, size_t>();
if (category_breakdown[category].find(activity) == category_breakdown[category].end())
category_breakdown[category][activity] = 0;
category_breakdown[category][activity]++;
}
void feed(set<df::interface_key> *input) void feed(set<df::interface_key> *input)
{ {
bool key_processed = false; bool key_processed = false;
@ -609,7 +862,10 @@ public:
if (key_processed) if (key_processed)
{ {
if (selected_column == 0 && fort_activity_column.feed_changed_highlight) if (selected_column == 0 && fort_activity_column.feed_changed_highlight)
{
populateDwarfColumn(); populateDwarfColumn();
populateCategoryBreakdownColumn();
}
return; return;
} }
@ -682,6 +938,7 @@ public:
fort_activity_column.display(selected_column == 0); fort_activity_column.display(selected_column == 0);
dwarf_activity_column.display(selected_column == 1); dwarf_activity_column.display(selected_column == 1);
category_breakdown_column.display(false);
int32_t y = gps->dimy - 4; int32_t y = gps->dimy - 4;
int32_t x = 2; int32_t x = 2;
@ -702,14 +959,17 @@ public:
std::string getFocusString() { return "dwarfmonitor_fortstats"; } std::string getFocusString() { return "dwarfmonitor_fortstats"; }
private: private:
ListColumn<map<df::unit *, size_t>> fort_activity_column; ListColumn<activity_type> fort_activity_column, category_breakdown_column;
ListColumn<df::unit> dwarf_activity_column; ListColumn<df::unit *> dwarf_activity_column;
int selected_column; int selected_column;
map<activity_type, size_t> fort_activity_totals; map<activity_type, size_t> fort_activity_totals;
map<int, map<activity_type, size_t>> category_breakdown;
map<activity_type, map<df::unit *, size_t>> dwarf_activity_values; map<activity_type, map<df::unit *, size_t>> dwarf_activity_values;
size_t fort_activity_count; size_t fort_activity_count;
size_t window_days; size_t window_days;
vector<activity_type> listed_activities;
void validateColumn() void validateColumn()
{ {
@ -817,7 +1077,10 @@ static void update_dwarf_stats(bool is_paused)
continue; continue;
if (ENUM_ATTR(profession, military, unit->profession)) if (ENUM_ATTR(profession, military, unit->profession))
{
add_work_history(unit, JOB_MILITARY); add_work_history(unit, JOB_MILITARY);
continue;
}
if (!unit->job.current_job) if (!unit->job.current_job)
{ {
@ -986,6 +1249,24 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
if (!gps || !INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply() || !INTERPOSE_HOOK(dwarf_monitor_hook, render).apply()) if (!gps || !INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply() || !INTERPOSE_HOOK(dwarf_monitor_hook, render).apply())
out.printerr("Could not insert dwarfmonitor hooks!\n"); out.printerr("Could not insert dwarfmonitor hooks!\n");
activity_labels[JOB_IDLE] = "Idle";
activity_labels[JOB_MILITARY] = "Military Duty";
activity_labels[JOB_LEISURE] = "Leisure";
activity_labels[JOB_UNPRODUCTIVE] = "Unproductive";
activity_labels[JOB_DESIGNATE] = "Mining";
activity_labels[JOB_STORE_ITEM] = "Store/Fetch Item";
activity_labels[JOB_MANUFACTURE] = "Manufacturing";
activity_labels[JOB_DETAILING] = "Detailing";
activity_labels[JOB_HUNTING] = "Hunting/Gathering";
activity_labels[JOB_MEDICAL] = "Medical";
activity_labels[JOB_COLLECT] = "Collect Materials";
activity_labels[JOB_CONSTRUCTION] = "Construction";
activity_labels[JOB_AGRICULTURE] = "Agriculture";
activity_labels[JOB_FOOD_PROD] = "Food/Drink Production";
activity_labels[JOB_MECHANICAL] = "Mechanics";
activity_labels[JOB_ANIMALS] = "Animal Handling";
activity_labels[JOB_PRODUCTIVE] = "Other Productive";
commands.push_back( commands.push_back(
PluginCommand( PluginCommand(
"dwarfmonitor", "Records dwarf activity to measure fort efficiency", "dwarfmonitor", "Records dwarf activity to measure fort efficiency",

@ -143,11 +143,11 @@ template <typename T>
class ListEntry class ListEntry
{ {
public: public:
T *elem; T elem;
string text; string text;
bool selected; bool selected;
ListEntry(string text, T &elem) : elem(&elem), text(text), selected(false) ListEntry(const string text, const T elem) : elem(elem), text(text), selected(false)
{ {
} }
}; };
@ -158,6 +158,7 @@ class ListColumn
public: public:
int highlighted_index; int highlighted_index;
int display_start_offset; int display_start_offset;
unsigned short text_clip_at;
int32_t bottom_margin, search_margin, left_margin; int32_t bottom_margin, search_margin, left_margin;
bool multiselect; bool multiselect;
bool allow_null; bool allow_null;
@ -173,6 +174,7 @@ public:
left_margin = 2; left_margin = 2;
search_margin = 63; search_margin = 63;
highlighted_index = 0; highlighted_index = 0;
text_clip_at = 0;
multiselect = false; multiselect = false;
allow_null = true; allow_null = true;
auto_select = false; auto_select = false;
@ -202,7 +204,7 @@ public:
max_item_width = entry.text.length(); max_item_width = entry.text.length();
} }
void add(const string &text, T &elem) void add(const string &text, const T &elem)
{ {
list.push_back(ListEntry<T>(text, elem)); list.push_back(ListEntry<T>(text, elem));
if (text.length() > max_item_width) if (text.length() > max_item_width)
@ -211,6 +213,9 @@ public:
int fixWidth() int fixWidth()
{ {
if (text_clip_at > 0 && max_item_width > text_clip_at)
max_item_width = text_clip_at;
for (auto it = list.begin(); it != list.end(); it++) for (auto it = list.begin(); it != list.end(); it++)
{ {
it->text = pad_string(it->text, max_item_width, false); it->text = pad_string(it->text, max_item_width, false);
@ -232,9 +237,14 @@ public:
++y; ++y;
UIColor fg_color = (display_list[i]->selected) ? COLOR_SELECTED : COLOR_UNSELECTED; UIColor fg_color = (display_list[i]->selected) ? COLOR_SELECTED : COLOR_UNSELECTED;
UIColor bg_color = (is_selected_column && i == highlighted_index) ? COLOR_HIGHLIGHTED : COLOR_BLACK; UIColor bg_color = (is_selected_column && i == highlighted_index) ? COLOR_HIGHLIGHTED : COLOR_BLACK;
paint_text(fg_color, left_margin, y, display_list[i]->text, bg_color);
string item_label = display_list[i]->text;
if (text_clip_at > 0 && item_label.length() > text_clip_at)
item_label.resize(text_clip_at);
paint_text(fg_color, left_margin, y, item_label, bg_color);
int x = left_margin + display_list[i]->text.length() + 1; int x = left_margin + display_list[i]->text.length() + 1;
display_extras(*display_list[i]->elem, x, y); display_extras(display_list[i]->elem, x, y);
} }
if (is_selected_column && allow_search) if (is_selected_column && allow_search)
@ -355,9 +365,9 @@ public:
entry->selected = !entry->selected; entry->selected = !entry->selected;
} }
vector<T*> getSelectedElems(bool only_one = false) vector<T> getSelectedElems(bool only_one = false)
{ {
vector<T*> results; vector<T> results;
for (auto it = list.begin(); it != list.end(); it++) for (auto it = list.begin(); it != list.end(); it++)
{ {
if ((*it).selected) if ((*it).selected)
@ -371,17 +381,22 @@ public:
return results; return results;
} }
T getFirstSelectedElem()
{
vector<T> results = getSelectedElems(true);
if (results.size() == 0)
return nullptr;
else
return results[0];
}
void clearSelection() void clearSelection()
{ {
for_each_(list, [] (ListEntry<T> &e) { e.selected = false; }); for_each_(list, [] (ListEntry<T> &e) { e.selected = false; });
} }
void selectItem(const T* elem) void selectItem(const T elem)
{ {
if (!elem)
return;
//clearSelection();
int i = 0; int i = 0;
for (; i < display_list.size(); i++) for (; i < display_list.size(); i++)
{ {
@ -399,15 +414,6 @@ public:
filterDisplay(); filterDisplay();
} }
T* getFirstSelectedElem()
{
vector<T*> results = getSelectedElems(true);
if (results.size() == 0)
return nullptr;
else
return results[0];
}
size_t getDisplayListSize() size_t getDisplayListSize()
{ {
return display_list.size(); return display_list.size();