Autolabor: Instead of guessing who the nobles are based on noble skills, actually find out who they are and behave appropriately. Certain responsibilities are assigned various penalties to increase their holders' availability to perform noble functions. Also, holders of the "manage health" responsibility are always assigned the diagnose labor.

The responsibility penalties are first-approximation and will likely need tuning.
develop
Kelly Martin 2012-04-26 09:38:25 -05:00
parent c463597e09
commit 5f5152f449
1 changed files with 78 additions and 46 deletions

@ -23,6 +23,15 @@
#include <df/building.h>
#include <df/workshop_type.h>
#include <df/unit_misc_trait.h>
#include <df/entity_position_responsibility.h>
#include <df/historical_figure.h>
#include <df/historical_entity.h>
#include <df/histfig_entity_link.h>
#include <df/histfig_entity_link_positionst.h>
#include <df/entity_position_assignment.h>
#include <df/entity_position.h>
#include <MiscUtils.h>
using std::string;
using std::endl;
@ -446,21 +455,44 @@ static const struct labor_default default_labor_infos[] = {
/* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0},
};
static const df::job_skill noble_skills[] = {
df::enums::job_skill::APPRAISAL,
df::enums::job_skill::ORGANIZATION,
df::enums::job_skill::RECORD_KEEPING,
static const int responsibility_penalties[] = {
0, /* LAW_MAKING */
0, /* LAW_ENFORCEMENT */
3000, /* RECEIVE_DIPLOMATS */
0, /* MEET_WORKERS */
1000, /* MANAGE_PRODUCTION */
3000, /* TRADE */
1000, /* ACCOUNTING */
0, /* ESTABLISH_COLONY_TRADE_AGREEMENTS */
0, /* MAKE_INTRODUCTIONS */
0, /* MAKE_PEACE_AGREEMENTS */
0, /* MAKE_TOPIC_AGREEMENTS */
0, /* COLLECT_TAXES */
0, /* ESCORT_TAX_COLLECTOR */
0, /* EXECUTIONS */
0, /* TAME_EXOTICS */
0, /* RELIGION */
0, /* ATTACK_ENEMIES */
0, /* PATROL_TERRITORY */
0, /* MILITARY_GOALS */
0, /* MILITARY_STRATEGY */
0, /* UPGRADE_SQUAD_EQUIPMENT */
0, /* EQUIPMENT_MANIFESTS */
0, /* SORT_AMMUNITION */
0, /* BUILD_MORALE */
5000 /* HEALTH_MANAGEMENT */
};
struct dwarf_info_t
{
int highest_skill;
int total_skill;
bool is_best_noble;
int mastery_penalty;
int assigned_jobs;
dwarf_state state;
bool has_exclusive_labor;
int noble_penalty; // penalty for assignment due to noble status
bool medical; // this dwarf has medical responsibility
};
static bool isOptionEnabled(unsigned flag)
@ -714,15 +746,45 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
std::vector<dwarf_info_t> dwarf_info(n_dwarfs);
std::vector<int> best_noble(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_skill(ARRAY_COUNT(noble_skills));
std::vector<int> highest_noble_experience(ARRAY_COUNT(noble_skills));
// Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
assert(dwarfs[dwarf]->status.souls.size() > 0);
// assert(dwarfs[dwarf]->status.souls.size() > 0);
// assert fails can cause DF to crash, so don't do that
if (dwarfs[dwarf]->status.souls.size() <= 0)
continue;
// compute noble penalty
int noble_penalty = 0;
df::historical_figure* hf = df::historical_figure::find(dwarfs[dwarf]->hist_figure_id);
for (int i = 0; i < hf->entity_links.size(); i++) {
df::histfig_entity_link* hfelink = hf->entity_links.at(i);
if (hfelink->getType() == df::histfig_entity_link_type::POSITION) {
df::histfig_entity_link_positionst *epos =
(df::histfig_entity_link_positionst*) hfelink;
df::historical_entity* entity = df::historical_entity::find(epos->entity_id);
if (!entity)
continue;
df::entity_position_assignment* assignment = binsearch_in_vector(entity->positions.assignments, epos->assignment_id);
if (!assignment)
continue;
df::entity_position* position = binsearch_in_vector(entity->positions.own, assignment->position_id);
if (!position)
continue;
for (int n = 0; n < 25; n++)
if (position->responsibilities[n])
noble_penalty += responsibility_penalties[n];
if (position->responsibilities[df::entity_position_responsibility::HEALTH_MANAGEMENT])
dwarf_info[dwarf].medical = true;
}
}
for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++)
{
@ -733,30 +795,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
int skill_level = (*s)->rating;
int skill_experience = (*s)->experience;
// Track the dwarfs with the best Appraisal, Organization, and Record Keeping skills.
// They are likely to have appointed noble positions, so should be kept free where possible.
int noble_skill_id = -1;
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
if (skill == noble_skills[i])
noble_skill_id = i;
}
if (noble_skill_id >= 0)
{
assert(noble_skill_id < ARRAY_COUNT(noble_skills));
if (highest_noble_skill[noble_skill_id] < skill_level ||
(highest_noble_skill[noble_skill_id] == skill_level &&
highest_noble_experience[noble_skill_id] < skill_experience))
{
highest_noble_skill[noble_skill_id] = skill_level;
highest_noble_experience[noble_skill_id] = skill_experience;
best_noble[noble_skill_id] = dwarf;
}
}
// Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.)
if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical)
@ -768,24 +806,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
}
}
// Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.)
for (int i = 0; i < ARRAY_COUNT(noble_skills); i++)
{
assert(best_noble[i] >= 0);
assert(best_noble[i] < n_dwarfs);
dwarf_info[best_noble[i]].is_best_noble = true;
}
// Calculate a base penalty for using each dwarf for a task he isn't good at.
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{
dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill;
dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill;
if (dwarf_info[dwarf].is_best_noble)
dwarf_info[dwarf].mastery_penalty -= 250;
dwarf_info[dwarf].mastery_penalty -= dwarf_info[dwarf].noble_penalty;
for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++)
{
@ -1032,6 +1059,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
* Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs
* (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted.
* Military and children/nobles will not have labors assigned.
* Dwarfs with the "health management" responsibility are always assigned DIAGNOSIS.
*/
for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++)
{
@ -1040,6 +1068,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
assert(dwarf >= 0);
assert(dwarf < n_dwarfs);
bool preferred_dwarf = false;
if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE)
preferred_dwarf = true;
@ -1047,6 +1077,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
preferred_dwarf = true;
if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive)
preferred_dwarf = true;
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
preferred_dwarf = true;
if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf)
continue;