Skip to content

Commit d1f3016

Browse files
committed
helper functions for creating jobs and checking whether units are ready to take jobs
1 parent 2264abb commit d1f3016

File tree

5 files changed

+134
-0
lines changed

5 files changed

+134
-0
lines changed

docs/changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Template for new versions:
6363
## Documentation
6464

6565
## API
66+
- ``Job``: new functions ``createLinked`` and ``assignToWorkshop``
67+
- ``Units``: new functions ``getFocusPenalty``, ``unbailableSocialActivity``, ``isJobAvailable``
6668

6769
## Lua
6870

library/include/modules/Job.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ distribution.
3131
#include "Types.h"
3232
#include "DataDefs.h"
3333

34+
#include "df/building_workshopst.h"
3435
#include "df/item_type.h"
3536
#include "df/job_item_ref.h"
3637

@@ -94,7 +95,13 @@ namespace DFHack
9495
DFHACK_EXPORT void checkBuildingsNow();
9596
DFHACK_EXPORT void checkDesignationsNow();
9697

98+
// link the job into the global job list, passing ownership to DF
9799
DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
100+
// create a job and immediately link it into the global job list
101+
DFHACK_EXPORT df::job* createLinked();
102+
103+
// assign job to workshop, returns false if workshop already has the maximum of ten jobs
104+
DFHACK_EXPORT bool assignToWorkshop(df::job *job, df::building_workshopst *workshop);
98105

99106
// Flag this job's posting as "dead" and set its posting_index to -1
100107
// If remove_all is true, flag all postings pointing to this job

library/include/modules/Units.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,14 @@ distribution.
3939
#include "df/job_skill.h"
4040
#include "df/mental_attribute_type.h"
4141
#include "df/misc_trait_type.h"
42+
#include "df/need_type.h"
4243
#include "df/physical_attribute_type.h"
4344
#include "df/unit_action.h"
4445
#include "df/unit_action_type_group.h"
4546
#include "df/unit_path_goal.h"
4647

4748
#include <ranges>
49+
#include <bitset>
4850

4951
namespace df {
5052
struct activity_entry;
@@ -339,6 +341,17 @@ DFHACK_EXPORT bool isGoalAchieved(df::unit *unit, size_t goalIndex = 0);
339341
DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit);
340342
DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit);
341343

344+
// get largest (i.e. most negative) focus penalty for a set of needs
345+
using need_types_set = std::bitset<ENUM_LAST_ITEM(need_type)+1UL>;
346+
DFHACK_EXPORT int32_t getFocusPenalty(df::unit* unit, need_types_set need_types);
347+
// get focused penalty for a single need
348+
DFHACK_EXPORT int32_t getFocusPenalty(df::unit* unit, df::need_type need_type);
349+
350+
// unit has an unbailable social activity (e.g. "Socialize!")
351+
DFHACK_EXPORT bool unbailableSocialActivity(df::unit *unit);
352+
// unit can be assigned a job
353+
DFHACK_EXPORT bool isJobAvailable(df::unit *unit, bool interrupt_social);
354+
342355
// Stress categories. 0 is highest stress, 6 is lowest.
343356
DFHACK_EXPORT extern const std::vector<int32_t> stress_cutoffs;
344357
DFHACK_EXPORT int getStressCategory(df::unit *unit);

library/modules/Job.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ distribution.
3838
#include "modules/References.h"
3939

4040
#include "df/building.h"
41+
#include "df/building_workshopst.h"
4142
#include "df/general_ref.h"
4243
#include "df/general_ref_unit_workerst.h"
4344
#include "df/general_ref_building_holderst.h"
@@ -517,6 +518,27 @@ bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
517518
}
518519
}
519520

521+
df::job* DFHack::Job::createLinked()
522+
{
523+
auto job = new df::job();
524+
DFHack::Job::linkIntoWorld(job, true);
525+
return job;
526+
}
527+
528+
bool assignToWorkshop(df::job *job, df::building_workshopst *workshop)
529+
{
530+
CHECK_NULL_POINTER(job);
531+
CHECK_NULL_POINTER(workshop);
532+
533+
if (workshop->jobs.size() >= 10) {
534+
return false;
535+
}
536+
job->pos = df::coord(workshop->centerx, workshop->centery, workshop->z);
537+
DFHack::Job::addGeneralRef(job, df::general_ref_type::BUILDING_HOLDER, workshop->id);
538+
workshop->jobs.push_back(job);
539+
return true;
540+
}
541+
520542
bool DFHack::Job::removePostings(df::job *job, bool remove_all)
521543
{
522544
using df::global::world;

library/modules/Units.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,15 @@ distribution.
6464
#include "df/interaction_profilest.h"
6565
#include "df/item.h"
6666
#include "df/job.h"
67+
#include "df/need_type.h"
6768
#include "df/nemesis_record.h"
6869
#include "df/personality_goalst.h"
70+
#include "df/personality_needst.h"
6971
#include "df/plotinfost.h"
7072
#include "df/proj_unitst.h"
7173
#include "df/reputation_profilest.h"
7274
#include "df/syndrome.h"
75+
#include "df/squad.h"
7376
#include "df/tile_occupancy.h"
7477
#include "df/training_assignment.h"
7578
#include "df/unit.h"
@@ -89,6 +92,7 @@ distribution.
8992
#include "df/world_site.h"
9093

9194
#include <algorithm>
95+
#include <bitset>
9296
#include <cstring>
9397
#include <functional>
9498
#include <map>
@@ -2017,6 +2021,92 @@ df::activity_event *Units::getMainSocialEvent(df::unit *unit) {
20172021
return entry->events[entry->events.size() - 1];
20182022
}
20192023

2024+
int32_t Units::getFocusPenalty(df::unit* unit, need_types_set need_types) {
2025+
CHECK_NULL_POINTER(unit);
2026+
2027+
int max_penalty = INT_MAX;
2028+
auto& needs = unit->status.current_soul->personality.needs;
2029+
for (auto const need : needs) {
2030+
if (need_types.test(need->id)) {
2031+
max_penalty = min(max_penalty, need->focus_level);
2032+
}
2033+
}
2034+
return max_penalty;
2035+
}
2036+
2037+
int32_t Units::getFocusPenalty(df::unit* unit, df::need_type need_type) {
2038+
auto need_types = need_types_set().set(need_type);
2039+
return getFocusPenalty(unit, need_types);
2040+
}
2041+
2042+
// reverse engineered from unitst::have_unbailable_sp_activities (partial implementation)
2043+
bool Units::unbailableSocialActivity(df::unit *unit)
2044+
{
2045+
// these can become constexpr with C++23
2046+
static const need_types_set pray_needs = need_types_set()
2047+
.set(df::need_type::PrayOrMeditate);
2048+
2049+
static const need_types_set socialize_needs = need_types_set()
2050+
.set(df::need_type::Socialize)
2051+
.set(df::need_type::BeCreative)
2052+
.set(df::need_type::Excitement)
2053+
.set(df::need_type::AdmireArt);
2054+
2055+
static const need_types_set read_needs = need_types_set()
2056+
.set(df::need_type::ThinkAbstractly)
2057+
.set(df::need_type::LearnSomething);
2058+
2059+
CHECK_NULL_POINTER(unit);
2060+
2061+
if (unit->social_activities.empty()) {
2062+
return false;
2063+
} else if (unit->social_activities.size() > 1) {
2064+
return true; // is this even possible?
2065+
}
2066+
2067+
auto activity = df::activity_entry::find(unit->social_activities[0]);
2068+
if (activity) {
2069+
using df::activity_entry_type;
2070+
switch (activity->type) {
2071+
case activity_entry_type::Socialize:
2072+
return getFocusPenalty(unit, socialize_needs) <= -10000;
2073+
case activity_entry_type::Prayer:
2074+
return getFocusPenalty(unit, pray_needs) <= -10000;
2075+
case activity_entry_type::Read:
2076+
return getFocusPenalty(unit, read_needs) <= -10000;
2077+
default:
2078+
// consider unhandled activities as uninterruptible
2079+
return true;
2080+
}
2081+
}
2082+
// this should never happen
2083+
return false;
2084+
}
2085+
2086+
bool Units::isJobAvailable(df::unit *unit, bool preserve_social = false){
2087+
if (unit->job.current_job)
2088+
return false;
2089+
if (unit->flags1.bits.caged || unit->flags1.bits.chained)
2090+
return false;
2091+
if (unit->individual_drills.size() > 0) {
2092+
if (unit->individual_drills.size() > 1)
2093+
return false; // this is even possible
2094+
auto activity = df::activity_entry::find(unit->individual_drills[0]);
2095+
if (activity && (activity->type == df::activity_entry_type::FillServiceOrder))
2096+
return false;
2097+
}
2098+
if (unbailableSocialActivity(unit))
2099+
return false;
2100+
if (preserve_social && unit->social_activities.size() > 0)
2101+
return false;
2102+
if (unit->military.squad_id != -1) {
2103+
auto squad = df::squad::find(unit->military.squad_id);
2104+
if (squad)
2105+
return squad->orders.size() == 0 && squad->activity == -1;
2106+
}
2107+
return true;
2108+
}
2109+
20202110
// 50000 and up is level 0, 25000 and up is level 1, etc.
20212111
const vector<int32_t> Units::stress_cutoffs {50000, 25000, 10000, -10000, -25000, -50000, -100000};
20222112

0 commit comments

Comments
 (0)