|
|
@ -109,6 +109,9 @@ enum dwarf_state {
|
|
|
|
// Busy with a useful task
|
|
|
|
// Busy with a useful task
|
|
|
|
BUSY,
|
|
|
|
BUSY,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Busy with a useful task that requires a tool
|
|
|
|
|
|
|
|
EXCLUSIVE,
|
|
|
|
|
|
|
|
|
|
|
|
// In the military, can't work
|
|
|
|
// In the military, can't work
|
|
|
|
MILITARY,
|
|
|
|
MILITARY,
|
|
|
|
|
|
|
|
|
|
|
@ -119,11 +122,12 @@ enum dwarf_state {
|
|
|
|
OTHER
|
|
|
|
OTHER
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const int NUM_STATE = 5;
|
|
|
|
const int NUM_STATE = 6;
|
|
|
|
|
|
|
|
|
|
|
|
static const char *state_names[] = {
|
|
|
|
static const char *state_names[] = {
|
|
|
|
"IDLE",
|
|
|
|
"IDLE",
|
|
|
|
"BUSY",
|
|
|
|
"BUSY",
|
|
|
|
|
|
|
|
"EXCLUSIVE",
|
|
|
|
"MILITARY",
|
|
|
|
"MILITARY",
|
|
|
|
"CHILD",
|
|
|
|
"CHILD",
|
|
|
|
"OTHER",
|
|
|
|
"OTHER",
|
|
|
@ -133,13 +137,13 @@ static const dwarf_state dwarf_states[] = {
|
|
|
|
BUSY /* CarveFortification */,
|
|
|
|
BUSY /* CarveFortification */,
|
|
|
|
BUSY /* DetailWall */,
|
|
|
|
BUSY /* DetailWall */,
|
|
|
|
BUSY /* DetailFloor */,
|
|
|
|
BUSY /* DetailFloor */,
|
|
|
|
BUSY /* Dig */,
|
|
|
|
EXCLUSIVE /* Dig */,
|
|
|
|
BUSY /* CarveUpwardStaircase */,
|
|
|
|
EXCLUSIVE /* CarveUpwardStaircase */,
|
|
|
|
BUSY /* CarveDownwardStaircase */,
|
|
|
|
EXCLUSIVE /* CarveDownwardStaircase */,
|
|
|
|
BUSY /* CarveUpDownStaircase */,
|
|
|
|
EXCLUSIVE /* CarveUpDownStaircase */,
|
|
|
|
BUSY /* CarveRamp */,
|
|
|
|
EXCLUSIVE /* CarveRamp */,
|
|
|
|
BUSY /* DigChannel */,
|
|
|
|
EXCLUSIVE /* DigChannel */,
|
|
|
|
BUSY /* FellTree */,
|
|
|
|
EXCLUSIVE /* FellTree */,
|
|
|
|
BUSY /* GatherPlants */,
|
|
|
|
BUSY /* GatherPlants */,
|
|
|
|
BUSY /* RemoveConstruction */,
|
|
|
|
BUSY /* RemoveConstruction */,
|
|
|
|
BUSY /* CollectWebs */,
|
|
|
|
BUSY /* CollectWebs */,
|
|
|
@ -154,7 +158,7 @@ static const dwarf_state dwarf_states[] = {
|
|
|
|
OTHER /* Sleep */,
|
|
|
|
OTHER /* Sleep */,
|
|
|
|
BUSY /* CollectSand */,
|
|
|
|
BUSY /* CollectSand */,
|
|
|
|
BUSY /* Fish */,
|
|
|
|
BUSY /* Fish */,
|
|
|
|
BUSY /* Hunt */,
|
|
|
|
EXCLUSIVE /* Hunt */,
|
|
|
|
OTHER /* HuntVermin */,
|
|
|
|
OTHER /* HuntVermin */,
|
|
|
|
BUSY /* Kidnap */,
|
|
|
|
BUSY /* Kidnap */,
|
|
|
|
BUSY /* BeatCriminal */,
|
|
|
|
BUSY /* BeatCriminal */,
|
|
|
@ -183,7 +187,7 @@ static const dwarf_state dwarf_states[] = {
|
|
|
|
OTHER /* GoShopping2 */,
|
|
|
|
OTHER /* GoShopping2 */,
|
|
|
|
BUSY /* Clean */,
|
|
|
|
BUSY /* Clean */,
|
|
|
|
OTHER /* Rest */,
|
|
|
|
OTHER /* Rest */,
|
|
|
|
BUSY /* PickupEquipment */,
|
|
|
|
EXCLUSIVE /* PickupEquipment */,
|
|
|
|
BUSY /* DumpItem */,
|
|
|
|
BUSY /* DumpItem */,
|
|
|
|
OTHER /* StrangeMoodCrafter */,
|
|
|
|
OTHER /* StrangeMoodCrafter */,
|
|
|
|
OTHER /* StrangeMoodJeweller */,
|
|
|
|
OTHER /* StrangeMoodJeweller */,
|
|
|
@ -393,8 +397,15 @@ struct labor_default
|
|
|
|
int active_dwarfs;
|
|
|
|
int active_dwarfs;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The percentage of the dwarves assigned as haulers at any one time.
|
|
|
|
static int hauler_pct = 33;
|
|
|
|
static int hauler_pct = 33;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The maximum percentage of dwarves who will be allowed to be idle.
|
|
|
|
|
|
|
|
// Decreasing this will encourage autolabor to keep dwarves busy,
|
|
|
|
|
|
|
|
// at the expense of making it harder for dwarves to specialize in
|
|
|
|
|
|
|
|
// specific skills.
|
|
|
|
|
|
|
|
static int idler_pct = 10;
|
|
|
|
|
|
|
|
|
|
|
|
static std::vector<struct labor_info> labor_infos;
|
|
|
|
static std::vector<struct labor_info> labor_infos;
|
|
|
|
|
|
|
|
|
|
|
|
static const struct labor_default default_labor_infos[] = {
|
|
|
|
static const struct labor_default default_labor_infos[] = {
|
|
|
@ -521,7 +532,6 @@ struct dwarf_info_t
|
|
|
|
bool medical; // this dwarf has medical responsibility
|
|
|
|
bool medical; // this dwarf has medical responsibility
|
|
|
|
bool trader; // this dwarf has trade responsibility
|
|
|
|
bool trader; // this dwarf has trade responsibility
|
|
|
|
bool diplomacy; // this dwarf meets with diplomats
|
|
|
|
bool diplomacy; // this dwarf meets with diplomats
|
|
|
|
int single_labor; // this dwarf will be exclusively assigned to one labor (-1/NONE for none)
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static bool isOptionEnabled(unsigned flag)
|
|
|
|
static bool isOptionEnabled(unsigned flag)
|
|
|
@ -738,7 +748,13 @@ struct laborinfo_sorter
|
|
|
|
{
|
|
|
|
{
|
|
|
|
bool operator() (int i,int j)
|
|
|
|
bool operator() (int i,int j)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (labor_infos[i].mode() != labor_infos[j].mode())
|
|
|
|
return labor_infos[i].mode() < labor_infos[j].mode();
|
|
|
|
return labor_infos[i].mode() < labor_infos[j].mode();
|
|
|
|
|
|
|
|
if (labor_infos[i].is_exclusive != labor_infos[j].is_exclusive)
|
|
|
|
|
|
|
|
return labor_infos[i].is_exclusive;
|
|
|
|
|
|
|
|
if (labor_infos[i].maximum_dwarfs() != labor_infos[j].maximum_dwarfs())
|
|
|
|
|
|
|
|
return labor_infos[i].maximum_dwarfs() < labor_infos[j].maximum_dwarfs();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -769,6 +785,7 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
|
|
|
|
|
|
|
|
int best_dwarf = 0;
|
|
|
|
int best_dwarf = 0;
|
|
|
|
int best_value = -10000;
|
|
|
|
int best_value = -10000;
|
|
|
|
|
|
|
|
int best_skill = 0;
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<int> values(n_dwarfs);
|
|
|
|
std::vector<int> values(n_dwarfs);
|
|
|
|
std::vector<int> candidates;
|
|
|
|
std::vector<int> candidates;
|
|
|
@ -813,6 +830,9 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
dwarf_skill[dwarf] = skill_level;
|
|
|
|
dwarf_skill[dwarf] = skill_level;
|
|
|
|
dwarf_skillxp[dwarf] = skill_experience;
|
|
|
|
dwarf_skillxp[dwarf] = skill_experience;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (best_skill < skill_level)
|
|
|
|
|
|
|
|
best_skill = skill_level;
|
|
|
|
|
|
|
|
|
|
|
|
value += skill_level * 100;
|
|
|
|
value += skill_level * 100;
|
|
|
|
value += skill_experience / 20;
|
|
|
|
value += skill_experience / 20;
|
|
|
|
if (skill_level > 0 || skill_experience > 0)
|
|
|
|
if (skill_level > 0 || skill_experience > 0)
|
|
|
@ -832,6 +852,9 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
value += 350;
|
|
|
|
value += 350;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].has_exclusive_labor)
|
|
|
|
|
|
|
|
value -= 500;
|
|
|
|
|
|
|
|
|
|
|
|
// bias by happiness
|
|
|
|
// bias by happiness
|
|
|
|
|
|
|
|
|
|
|
|
//value += dwarfs[dwarf]->status.happiness;
|
|
|
|
//value += dwarfs[dwarf]->status.happiness;
|
|
|
@ -897,15 +920,28 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
if (unit_labor::FISH == labor && !has_fishery)
|
|
|
|
if (unit_labor::FISH == labor && !has_fishery)
|
|
|
|
min_dwarfs = max_dwarfs = 0;
|
|
|
|
min_dwarfs = max_dwarfs = 0;
|
|
|
|
|
|
|
|
|
|
|
|
bool want_idle_dwarf = true;
|
|
|
|
// If there are enough idle dwarves to choose from, enter an aggressive assignment
|
|
|
|
if (state_count[IDLE] < 2)
|
|
|
|
// mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves,
|
|
|
|
want_idle_dwarf = false;
|
|
|
|
// whichever is higher.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// In aggressive mode, we will always pick at least one idle dwarf for each skill,
|
|
|
|
|
|
|
|
// in order to try to get the idle dwarves to start doing something. We also pick
|
|
|
|
|
|
|
|
// any dwarf more preferable to the idle dwarf, since we'd rather have a more
|
|
|
|
|
|
|
|
// preferable dwarf do a new job if one becomes available (probably because that
|
|
|
|
|
|
|
|
// dwarf just finished a job).
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// In non-aggressive mode, only dwarves that are good at a labor will be assigned
|
|
|
|
|
|
|
|
// to it. Dwarves good at nothing, or nothing that needs doing, will tend to get
|
|
|
|
|
|
|
|
// assigned to hauling by the hauler code. If there are no hauling jobs to do,
|
|
|
|
|
|
|
|
// they will sit around idle and when enough build up they will trigger aggressive
|
|
|
|
|
|
|
|
// mode again.
|
|
|
|
|
|
|
|
bool aggressive_mode = state_count[IDLE] >= 2 && state_count[IDLE] >= n_dwarfs * idler_pct / 100;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in
|
|
|
|
* Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in
|
|
|
|
* order of preference, and then assign additional dwarfs that meet any of these conditions:
|
|
|
|
* order of preference, and then assign additional dwarfs that meet any of these conditions:
|
|
|
|
* - The dwarf is idle and there are no idle dwarves assigned to this labor
|
|
|
|
* - We are in aggressive mode and have not yet assigned an idle dwarf
|
|
|
|
* - The dwarf has nonzero skill associated with the labor
|
|
|
|
* - The dwarf is good at this skill
|
|
|
|
* - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled.
|
|
|
|
* - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled.
|
|
|
|
* We stop assigning dwarfs when we reach the maximum allowed.
|
|
|
|
* We stop assigning dwarfs when we reach the maximum allowed.
|
|
|
|
* Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs
|
|
|
|
* Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs
|
|
|
@ -917,24 +953,23 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int dwarf = candidates[i];
|
|
|
|
int dwarf = candidates[i];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].trader && trader_requested)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].diplomacy)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
assert(dwarf >= 0);
|
|
|
|
assert(dwarf >= 0);
|
|
|
|
assert(dwarf < n_dwarfs);
|
|
|
|
assert(dwarf < n_dwarfs);
|
|
|
|
|
|
|
|
|
|
|
|
bool preferred_dwarf = false;
|
|
|
|
bool preferred_dwarf = false;
|
|
|
|
if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE)
|
|
|
|
if (dwarf_skillxp[dwarf] > 0 && dwarf_skill[dwarf] >= best_skill / 2)
|
|
|
|
preferred_dwarf = true;
|
|
|
|
|
|
|
|
if (dwarf_skill[dwarf] > 0)
|
|
|
|
|
|
|
|
preferred_dwarf = true;
|
|
|
|
preferred_dwarf = true;
|
|
|
|
if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive)
|
|
|
|
if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive && dwarf_info[dwarf].state == EXCLUSIVE)
|
|
|
|
preferred_dwarf = true;
|
|
|
|
preferred_dwarf = true;
|
|
|
|
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
|
|
|
|
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
|
|
|
|
preferred_dwarf = true;
|
|
|
|
preferred_dwarf = true;
|
|
|
|
if (dwarf_info[dwarf].trader && trader_requested)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].diplomacy)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf)
|
|
|
|
if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf && !aggressive_mode)
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (!dwarfs[dwarf]->status.labors[labor])
|
|
|
|
if (!dwarfs[dwarf]->status.labors[labor])
|
|
|
@ -952,11 +987,11 @@ static void assign_labor(unit_labor::unit_labor labor,
|
|
|
|
if (print_debug)
|
|
|
|
if (print_debug)
|
|
|
|
out.print("Dwarf %i \"%s\" assigned %s: value %i %s %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf], dwarf_info[dwarf].trader ? "(trader)" : "", dwarf_info[dwarf].diplomacy ? "(diplomacy)" : "");
|
|
|
|
out.print("Dwarf %i \"%s\" assigned %s: value %i %s %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf], dwarf_info[dwarf].trader ? "(trader)" : "", dwarf_info[dwarf].diplomacy ? "(diplomacy)" : "");
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY)
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
|
|
|
|
labor_infos[labor].active_dwarfs++;
|
|
|
|
labor_infos[labor].active_dwarfs++;
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].state == IDLE)
|
|
|
|
if (dwarf_info[dwarf].state == IDLE)
|
|
|
|
want_idle_dwarf = false;
|
|
|
|
aggressive_mode = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1049,8 +1084,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
|
|
|
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
dwarf_info[dwarf].single_labor = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarfs[dwarf]->status.souls.size() <= 0)
|
|
|
|
if (dwarfs[dwarf]->status.souls.size() <= 0)
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
@ -1214,7 +1247,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
laborinfo_sorter lasorter;
|
|
|
|
laborinfo_sorter lasorter;
|
|
|
|
std::sort(labors.begin(), labors.end(), lasorter);
|
|
|
|
std::sort(labors.begin(), labors.end(), lasorter);
|
|
|
|
|
|
|
|
|
|
|
|
// Handle DISABLED skills (just bookkeeping)
|
|
|
|
// Handle DISABLED skills (just bookkeeping).
|
|
|
|
|
|
|
|
// Note that autolabor should *NEVER* enable or disable a skill that has been marked as DISABLED, for any reason.
|
|
|
|
|
|
|
|
// The user has told us that they want manage this skill manually, and we must respect that.
|
|
|
|
for (auto lp = labors.begin(); lp != labors.end(); ++lp)
|
|
|
|
for (auto lp = labors.begin(); lp != labors.end(); ++lp)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto labor = *lp;
|
|
|
|
auto labor = *lp;
|
|
|
@ -1224,12 +1259,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
|
|
|
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if ((dwarf_info[dwarf].trader && trader_requested) ||
|
|
|
|
|
|
|
|
dwarf_info[dwarf].diplomacy)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
dwarfs[dwarf]->status.labors[labor] = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarfs[dwarf]->status.labors[labor])
|
|
|
|
if (dwarfs[dwarf]->status.labors[labor])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (labor_infos[labor].is_exclusive)
|
|
|
|
if (labor_infos[labor].is_exclusive)
|
|
|
@ -1252,7 +1281,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
// Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps
|
|
|
|
// Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps
|
|
|
|
// make sure that hauling jobs are handled quickly rather than building up.
|
|
|
|
// make sure that hauling jobs are handled quickly rather than building up.
|
|
|
|
|
|
|
|
|
|
|
|
int num_haulers = state_count[IDLE] + state_count[BUSY] * hauler_pct / 100;
|
|
|
|
int num_haulers = state_count[IDLE] + (state_count[BUSY] + state_count[EXCLUSIVE]) * hauler_pct / 100;
|
|
|
|
|
|
|
|
|
|
|
|
if (num_haulers < 1)
|
|
|
|
if (num_haulers < 1)
|
|
|
|
num_haulers = 1;
|
|
|
|
num_haulers = 1;
|
|
|
@ -1274,7 +1303,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY)
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
|
|
|
|
hauler_ids.push_back(dwarf);
|
|
|
|
hauler_ids.push_back(dwarf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dwarfinfo_sorter sorter(dwarf_info);
|
|
|
|
dwarfinfo_sorter sorter(dwarf_info);
|
|
|
@ -1305,7 +1334,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
dwarfs[dwarf]->status.labors[labor] = true;
|
|
|
|
dwarfs[dwarf]->status.labors[labor] = true;
|
|
|
|
dwarf_info[dwarf].assigned_jobs++;
|
|
|
|
dwarf_info[dwarf].assigned_jobs++;
|
|
|
|
|
|
|
|
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY)
|
|
|
|
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
|
|
|
|
labor_infos[labor].active_dwarfs++;
|
|
|
|
labor_infos[labor].active_dwarfs++;
|
|
|
|
|
|
|
|
|
|
|
|
if (print_debug)
|
|
|
|
if (print_debug)
|
|
|
|