From b99e948b8aac74772f6963261d63e4cdafabf651 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 9 Nov 2022 21:10:18 -0800 Subject: [PATCH] Implements plugin: spectate v1.0.1a --- docs/plugins/spectate.rst | 3 +- plugins/spectate/spectate.cpp | 122 ++++++++++++++++++++-------------- 2 files changed, 74 insertions(+), 51 deletions(-) diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 8bfff4f03..f54d68142 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -46,5 +46,4 @@ Features Settings -------- -:tick-threshold: Set the plugin's tick interval for changing the followed dwarf. - Acts as a maximum follow time when used with focus-jobs enabled. (default: 1000) +:tick-threshold: Set the plugin's tick interval for changing the followed dwarf. (default: 1000) diff --git a/plugins/spectate/spectate.cpp b/plugins/spectate/spectate.cpp index e308ddd19..9ad4a1f4e 100644 --- a/plugins/spectate/spectate.cpp +++ b/plugins/spectate/spectate.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -78,7 +79,7 @@ static PersistentDataItem pconfig; DFhackCExport command_result plugin_enable(color_ostream &out, bool enable); command_result spectate (color_ostream &out, std::vector & parameters); -#define COORDARGS(id) id.x, (id).y, id.z +#define COORDARGS(id) id.x, id.y, id.z namespace SP { bool following_dwarf = false; @@ -209,68 +210,91 @@ namespace SP { } return true; }; + static auto calc_extra_weight = [](size_t idx, double r1, double r2) { + switch(idx) { + case 0: + return r2; + case 1: + return (r2-r1)/1.3; + case 2: + return (r2-r1)/2; + default: + return 0.0; + } + }; + /// Collecting our choice pool + /////////////////////////////// + std::array ranges{}; + std::array range_exists{}; + static auto build_range = [&](size_t idx){ + size_t first = idx * 2; + size_t second = idx * 2 + 1; + size_t previous = first - 1; + // first we get the end of the range + ranges[second] = units.size() - 1; + // then we calculate whether this indicates there is a range or not + if (first != 0) { + range_exists[idx] = ranges[second] > ranges[previous]; + } else { + range_exists[idx] = ranges[second] >= 0; + } + // lastly we set the start of the range + ranges[first] = ranges[previous] + (range_exists[idx] ? 1 : 0); + }; + + /// RANGE 0 (in view + working) + // grab valid working units + add_if([](df::unit* unit) { + return valid(unit) && Units::isCitizen(unit, true) && unit->job.current_job; + }); + // keep only those in the box + Units::getUnitsInBox(units, COORDARGS(viewMin), COORDARGS(viewMax)); + build_range(0); + /// RANGE 1 (in view) - // grab all valid units add_if(valid); - // keep only those in the box Units::getUnitsInBox(units, COORDARGS(viewMin), COORDARGS(viewMax)); - int32_t inview_idx2 = units.size()-1; - bool range1_exists = inview_idx2 >= 0; - int32_t inview_idx1 = range1_exists ? 0 : -1; + build_range(1); - /// RANGE 2 (citizens) + /// RANGE 2 (working citizens) + add_if([](df::unit* unit) { + return valid(unit) && Units::isCitizen(unit, true) && unit->job.current_job; + }); + build_range(2); + + /// RANGE 3 (citizens) add_if([](df::unit* unit) { return valid(unit) && Units::isCitizen(unit, true); }); - int32_t cit_idx2 = units.size()-1; - bool range2_exists = cit_idx2 > inview_idx2; - int32_t cit_idx1 = range2_exists ? inview_idx2+1 : cit_idx2; + build_range(3); - /// RANGE 3 (any valid) + /// RANGE 4 (any valid) add_if(valid); - int32_t all_idx2 = units.size()-1; - bool range3_exists = all_idx2 > cit_idx2; - int32_t all_idx1 = range3_exists ? cit_idx2+1 : all_idx2; - + build_range(4); + // selecting from our choice pool if (!units.empty()) { + std::array bw{23,17,13,7,1}; // probability weights for each range std::vector i; std::vector w; - if (!range1_exists && !range2_exists && !range3_exists) { - return false; - } - if (range1_exists) { - if (inview_idx1 == inview_idx2) { - i.push_back(0); - w.push_back(17); - } else { - i.push_back(inview_idx1); - i.push_back(inview_idx2); - w.push_back(inview_idx2 + 1); - w.push_back(inview_idx2 + 1); + bool at_least_one = false; + // in one word, elegance + for(size_t idx = 0; idx < range_exists.size(); ++idx) { + if (range_exists[idx]) { + at_least_one = true; + const auto &r1 = ranges[idx*2]; + const auto &r2 = ranges[idx*2+1]; + double extra = calc_extra_weight(idx, r1, r2); + i.push_back(r1); + w.push_back(bw[idx] + extra); + if (r1 != r2) { + i.push_back(r2); + w.push_back(bw[idx] + extra); + } } } - if (range2_exists) { - if (cit_idx1 == cit_idx2) { - i.push_back(cit_idx1); - w.push_back(7); - } else { - i.push_back(cit_idx1); - i.push_back(cit_idx2); - w.push_back(7); - w.push_back(7); - } - } - if (range3_exists) { - if (all_idx1 == all_idx2) { - i.push_back(all_idx1); - w.push_back(1); - } else { - i.push_back(all_idx1); - i.push_back(all_idx2); - w.push_back(1); - w.push_back(1); - } + if (!at_least_one) { + return false; } std::piecewise_linear_distribution<> follow_any(i.begin(), i.end(), w.begin()); // if you're looking at a warning about a local address escaping, it means the unit* from units (which aren't local) @@ -327,7 +351,7 @@ namespace SP { if (!World::ReadPauseState() && tick - last_tick >= 1) { last_tick = tick; // validate follow state - if (!following_dwarf || !our_dorf || df::global::ui->follow_unit < 0) { + if (!following_dwarf || !our_dorf || df::global::ui->follow_unit < 0 || tick - timestamp >= config.tick_threshold) { // we're not following anyone following_dwarf = false; if (!config.disengage) {