From 91ebb56b6bd53dc5cc52c1e0e1544e0d5a25e784 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 5 Aug 2021 09:40:50 -0700 Subject: [PATCH 1/7] Adds spectate plugin --- plugins/CMakeLists.txt | 1 + plugins/spectate.cpp | 119 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 plugins/spectate.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 25ea7d150e..29b908b8d3 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -163,6 +163,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(siege-engine siege-engine.cpp LINK_LIBRARIES lua) dfhack_plugin(sort sort.cpp LINK_LIBRARIES lua) dfhack_plugin(steam-engine steam-engine.cpp) + dfhack_plugin(spectate spectate.cpp) dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua) add_subdirectory(stockpiles) dfhack_plugin(stocks stocks.cpp) diff --git a/plugins/spectate.cpp b/plugins/spectate.cpp new file mode 100644 index 0000000000..529d35c0b9 --- /dev/null +++ b/plugins/spectate.cpp @@ -0,0 +1,119 @@ +// +// Created by josh on 7/28/21. +// + +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +std::map freq; +std::set job_tracker; +std::default_random_engine RNG; + +//#include "df/world.h" + +using namespace DFHack; +using namespace df::enums; + +DFHACK_PLUGIN("spectate"); +DFHACK_PLUGIN_IS_ENABLED(enabled); +bool watching_job = false; +void* job_watched = nullptr; +//REQUIRE_GLOBAL(world); + +command_result spectate (color_ostream &out, std::vector & parameters); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { + commands.push_back(PluginCommand("spectate", + "Automated spectator mode.", + spectate, + false, + "" + " spectate\n" + " toggles spectator mode\n" + "\n")); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + return CR_OK; +} + +void onJobStart(color_ostream &out, void* job); +void onJobCompletion(color_ostream &out, void* job); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + namespace EM = EventManager; + if (enable && !enabled) { + using namespace EM::EventType; + EM::EventHandler start(onJobStart, 0); + EM::EventHandler complete(onJobCompletion, 0); + EM::registerListener(EventType::JOB_INITIATED, start, plugin_self); + EM::registerListener(EventType::JOB_COMPLETED, complete, plugin_self); + //out.print("spectator mode enabled!\n"); + } else if (!enable && enabled) { + EM::unregisterAll(plugin_self); + job_tracker.clear(); + freq.clear(); + //out.print("spectator mode disabled!\n"); + } else { + return CR_FAILURE; + } + enabled = enable; + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if(enabled && watching_job) { + int32_t id = Job::getWorker((df::job*) job_watched)->id; + if (df::global::ui->follow_unit != id) { + df::global::ui->follow_unit = id; + } + } + return CR_OK; +} + + +command_result spectate (color_ostream &out, std::vector & parameters) { + return plugin_enable(out, !enabled); +} + +void onJobStart(color_ostream& out, void* job_ptr) { + df::job* job = (df::job*)job_ptr; + int zcount = freq[job->pos.z]++; + job_tracker.emplace(job->id); + if (!watching_job) { + double p = 0.99 * ((double)zcount / job_tracker.size()); + std::bernoulli_distribution follow_job(p); + if(follow_job(RNG)){ + df::unit* unit = Job::getWorker(job); + df::global::ui->follow_unit = unit->id; + job_watched = job_ptr; + } else { + // choose a dwarf that isn't working, and follow them until + } + } +} + +void onJobCompletion(color_ostream &out, void* job_ptr) { + df::job* job = (df::job*)job_ptr; + freq[job->pos.z]--; + job_tracker.erase(job->id); + if (watching_job && job_ptr == job_watched) { + watching_job = false; + job_watched = nullptr; + } +} \ No newline at end of file From 20ebce004f02ede231620dae18f795494bba97b9 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 6 Aug 2021 08:02:05 -0700 Subject: [PATCH 2/7] Updates spectate.cpp --- plugins/spectate.cpp | 84 ++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/plugins/spectate.cpp b/plugins/spectate.cpp index 529d35c0b9..f90878eb71 100644 --- a/plugins/spectate.cpp +++ b/plugins/spectate.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -30,9 +31,11 @@ using namespace df::enums; DFHACK_PLUGIN("spectate"); DFHACK_PLUGIN_IS_ENABLED(enabled); -bool watching_job = false; +bool following_dwarf = false; void* job_watched = nullptr; -//REQUIRE_GLOBAL(world); +int32_t timestamp = -1; +REQUIRE_GLOBAL(world); +REQUIRE_GLOBAL(ui); command_result spectate (color_ostream &out, std::vector & parameters); @@ -52,19 +55,24 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { return CR_OK; } +void onTick(color_ostream& out, void* tick); void onJobStart(color_ostream &out, void* job); void onJobCompletion(color_ostream &out, void* job); DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { namespace EM = EventManager; if (enable && !enabled) { + out.print("Spectator mode enabled!\n"); using namespace EM::EventType; + EM::EventHandler ticking(onTick, 15); EM::EventHandler start(onJobStart, 0); EM::EventHandler complete(onJobCompletion, 0); - EM::registerListener(EventType::JOB_INITIATED, start, plugin_self); + EM::registerListener(EventType::TICK, ticking, plugin_self); + EM::registerListener(EventType::JOB_STARTED, start, plugin_self); EM::registerListener(EventType::JOB_COMPLETED, complete, plugin_self); //out.print("spectator mode enabled!\n"); } else if (!enable && enabled) { + out.print("Spectator mode disabled!\n"); EM::unregisterAll(plugin_self); job_tracker.clear(); freq.clear(); @@ -76,34 +84,63 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { return CR_OK; } -DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if(enabled && watching_job) { - int32_t id = Job::getWorker((df::job*) job_watched)->id; - if (df::global::ui->follow_unit != id) { - df::global::ui->follow_unit = id; - } - } - return CR_OK; -} - - command_result spectate (color_ostream &out, std::vector & parameters) { return plugin_enable(out, !enabled); } +void onTick(color_ostream& out, void* ptr){ + int32_t tick = df::global::world->frame_counter; + if(!following_dwarf || (job_watched == nullptr && (tick - timestamp) > 50)) { + std::vector dwarves; + for (size_t i = 0; i < df::global::world->units.active.size(); i++) { + df::unit* unit = world->units.active[i]; + if (!Units::isCitizen(unit)) { + continue; + } + dwarves.push_back(unit); + } + std::uniform_int_distribution<> follow_any(0,dwarves.size()-1); + if (df::global::ui) { + df::unit* unit = dwarves[follow_any(RNG)]; + df::global::ui->follow_unit = unit->id; + job_watched = unit->job.current_job; + following_dwarf = true; + if(!job_watched){ + timestamp = tick; + } + } + } +} + void onJobStart(color_ostream& out, void* job_ptr) { - df::job* job = (df::job*)job_ptr; + int32_t tick = df::global::world->frame_counter; + df::job* job = (df::job*) job_ptr; int zcount = freq[job->pos.z]++; job_tracker.emplace(job->id); - if (!watching_job) { - double p = 0.99 * ((double)zcount / job_tracker.size()); + if (!following_dwarf || (job_watched == nullptr && (tick - timestamp) > 50)) { + following_dwarf = true; + double p = 0.99 * ((double) zcount / job_tracker.size()); std::bernoulli_distribution follow_job(p); - if(follow_job(RNG)){ - df::unit* unit = Job::getWorker(job); - df::global::ui->follow_unit = unit->id; + if (job && !job->flags.bits.special && follow_job(RNG)) { job_watched = job_ptr; + df::unit* unit = Job::getWorker(job); + if (df::global::ui && unit) { + df::global::ui->follow_unit = unit->id; + } } else { - // choose a dwarf that isn't working, and follow them until + timestamp = tick; + std::vector nonworkers; + for (size_t i = 0; i < df::global::world->units.active.size(); i++) { + df::unit* unit = world->units.active[i]; + if (!Units::isCitizen(unit) || unit->job.current_job) { + continue; + } + nonworkers.push_back(unit); + } + std::uniform_int_distribution<> follow_drunk(0,nonworkers.size()-1); + if (df::global::ui) { + df::global::ui->follow_unit = nonworkers[follow_drunk(RNG)]->id; + } } } } @@ -111,9 +148,10 @@ void onJobStart(color_ostream& out, void* job_ptr) { void onJobCompletion(color_ostream &out, void* job_ptr) { df::job* job = (df::job*)job_ptr; freq[job->pos.z]--; + freq[job->pos.z] = freq[job->pos.z] < 0 ? 0 : freq[job->pos.z]; job_tracker.erase(job->id); - if (watching_job && job_ptr == job_watched) { - watching_job = false; + if (following_dwarf && job_ptr == job_watched) { + following_dwarf = false; job_watched = nullptr; } } \ No newline at end of file From 23fe85b155ab5337c0ab3b0eb3fb04506488e310 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 6 Aug 2021 08:12:56 -0700 Subject: [PATCH 3/7] Changes spectate toggle message --- plugins/spectate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/spectate.cpp b/plugins/spectate.cpp index f90878eb71..6c398ddd09 100644 --- a/plugins/spectate.cpp +++ b/plugins/spectate.cpp @@ -62,7 +62,7 @@ void onJobCompletion(color_ostream &out, void* job); DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { namespace EM = EventManager; if (enable && !enabled) { - out.print("Spectator mode enabled!\n"); + out.print("Spectate mode enabled!\n"); using namespace EM::EventType; EM::EventHandler ticking(onTick, 15); EM::EventHandler start(onJobStart, 0); @@ -72,7 +72,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { EM::registerListener(EventType::JOB_COMPLETED, complete, plugin_self); //out.print("spectator mode enabled!\n"); } else if (!enable && enabled) { - out.print("Spectator mode disabled!\n"); + out.print("Spectate mode disabled!\n"); EM::unregisterAll(plugin_self); job_tracker.clear(); freq.clear(); From fad7ddd08fd60605fdb41cbbfcdb04619b4b5eda Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 6 Aug 2021 08:15:15 -0700 Subject: [PATCH 4/7] Updates changelog.txt --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index fab594d6f3..a4df3715e3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins - `dig-now`: instantly completes dig designations (including smoothing and carving tracks) +- `spectate`: automates the following of dwarves more often than not based on job zlevel activity levels, sometimes randomly though. ## Misc Improvements - `tiletypes-here`, `tiletypes-here-point`: add --cursor and --quiet options to support non-interactive use cases From 04f421530e8d96e337ff7d05008a20524806bdc0 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 6 Aug 2021 08:24:50 -0700 Subject: [PATCH 5/7] Adds spectate to Plugins.rst --- docs/Plugins.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/Plugins.rst b/docs/Plugins.rst index eb1ca6f1b6..b9f65eaa0c 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -250,6 +250,12 @@ showmood ======== Shows all items needed for the currently active strange mood. +spectate +======== +Simple plugin to automate following random dwarves. Most of the time things will +be weighted towards z-levels with the highest job activity. Simply enter the +``spectate`` command to toggle the plugin's state. + ======== Bugfixes From 7fd1e8d65c5c06ef5e7d3d1fbf9e8a8cdafa70b8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 14 Mar 2022 14:15:47 -0700 Subject: [PATCH 6/7] Adds requested changes - foreach syntax replaces active units loops - removes CR_FAILURE return on double enable/disable usage - removes disabled code - implements a few clion clang tidy suggestions (auto declare when casting) - Updates zcount to have accurate count for performing RNG - adds eof newline - adds todo comment about a redundant if condition --- plugins/spectate.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/plugins/spectate.cpp b/plugins/spectate.cpp index 6c398ddd09..aae48cf5a0 100644 --- a/plugins/spectate.cpp +++ b/plugins/spectate.cpp @@ -70,15 +70,11 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { EM::registerListener(EventType::TICK, ticking, plugin_self); EM::registerListener(EventType::JOB_STARTED, start, plugin_self); EM::registerListener(EventType::JOB_COMPLETED, complete, plugin_self); - //out.print("spectator mode enabled!\n"); } else if (!enable && enabled) { out.print("Spectate mode disabled!\n"); EM::unregisterAll(plugin_self); job_tracker.clear(); freq.clear(); - //out.print("spectator mode disabled!\n"); - } else { - return CR_FAILURE; } enabled = enable; return CR_OK; @@ -88,24 +84,24 @@ command_result spectate (color_ostream &out, std::vector & paramet return plugin_enable(out, !enabled); } -void onTick(color_ostream& out, void* ptr){ +void onTick(color_ostream& out, void* ptr) { int32_t tick = df::global::world->frame_counter; - if(!following_dwarf || (job_watched == nullptr && (tick - timestamp) > 50)) { + // || seems to be redundant as the first always evaluates true when job_watched is nullptr.. todo: figure what is supposed to happen + if (!following_dwarf || (job_watched == nullptr && (tick - timestamp) > 50)) { std::vector dwarves; - for (size_t i = 0; i < df::global::world->units.active.size(); i++) { - df::unit* unit = world->units.active[i]; + for (auto unit: df::global::world->units.active) { if (!Units::isCitizen(unit)) { continue; } dwarves.push_back(unit); } - std::uniform_int_distribution<> follow_any(0,dwarves.size()-1); + std::uniform_int_distribution follow_any(0, dwarves.size() - 1); if (df::global::ui) { df::unit* unit = dwarves[follow_any(RNG)]; df::global::ui->follow_unit = unit->id; job_watched = unit->job.current_job; following_dwarf = true; - if(!job_watched){ + if (!job_watched) { timestamp = tick; } } @@ -114,14 +110,14 @@ void onTick(color_ostream& out, void* ptr){ void onJobStart(color_ostream& out, void* job_ptr) { int32_t tick = df::global::world->frame_counter; - df::job* job = (df::job*) job_ptr; - int zcount = freq[job->pos.z]++; + auto job = (df::job*) job_ptr; + int zcount = ++freq[job->pos.z]; job_tracker.emplace(job->id); if (!following_dwarf || (job_watched == nullptr && (tick - timestamp) > 50)) { following_dwarf = true; double p = 0.99 * ((double) zcount / job_tracker.size()); std::bernoulli_distribution follow_job(p); - if (job && !job->flags.bits.special && follow_job(RNG)) { + if (!job->flags.bits.special && follow_job(RNG)) { job_watched = job_ptr; df::unit* unit = Job::getWorker(job); if (df::global::ui && unit) { @@ -130,14 +126,13 @@ void onJobStart(color_ostream& out, void* job_ptr) { } else { timestamp = tick; std::vector nonworkers; - for (size_t i = 0; i < df::global::world->units.active.size(); i++) { - df::unit* unit = world->units.active[i]; + for (auto unit: df::global::world->units.active) { if (!Units::isCitizen(unit) || unit->job.current_job) { continue; } nonworkers.push_back(unit); } - std::uniform_int_distribution<> follow_drunk(0,nonworkers.size()-1); + std::uniform_int_distribution<> follow_drunk(0, nonworkers.size() - 1); if (df::global::ui) { df::global::ui->follow_unit = nonworkers[follow_drunk(RNG)]->id; } @@ -146,7 +141,7 @@ void onJobStart(color_ostream& out, void* job_ptr) { } void onJobCompletion(color_ostream &out, void* job_ptr) { - df::job* job = (df::job*)job_ptr; + auto job = (df::job*)job_ptr; freq[job->pos.z]--; freq[job->pos.z] = freq[job->pos.z] < 0 ? 0 : freq[job->pos.z]; job_tracker.erase(job->id); @@ -154,4 +149,4 @@ void onJobCompletion(color_ostream &out, void* job_ptr) { following_dwarf = false; job_watched = nullptr; } -} \ No newline at end of file +} From 21c8ccc92c12927d1230d9610a9f75490f7fcbe8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 14 Mar 2022 14:16:22 -0700 Subject: [PATCH 7/7] Declares spectate section in Plugins.rst --- docs/Plugins.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 522a406919..54a26ed01d 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -289,6 +289,8 @@ showmood ======== Shows all items needed for the currently active strange mood. +.. _spectate: + spectate ======== Simple plugin to automate following random dwarves. Most of the time things will