Skip to content

Commit b658a12

Browse files
Merge remote-tracking branch 'upstream/develop' into SquidHack
2 parents 45cc899 + d295136 commit b658a12

File tree

19 files changed

+595
-157
lines changed

19 files changed

+595
-157
lines changed

docs/about/Authors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Cameron Ewell Ozzatron
4343
Carter Bray Qartar
4444
Chris Dombroski cdombroski
4545
Chris Parsons chrismdp
46+
Christian Doczkal chdoc
4647
cjhammel cjhammel
4748
Clayton Hughes
4849
Clément Vuchener cvuchener

docs/changelog.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,23 @@ Template for new versions:
5555

5656
## New Features
5757

58+
- `nestboxes`: allow limiting egg protection to nestboxes inside a designated burrow
59+
5860
## Fixes
61+
- ``Units::getReadableName`` will no longer append a comma to the names of histfigs with no profession
5962

6063
## Misc Improvements
6164

6265
## Documentation
6366

6467
## API
68+
- ``Job``: new functions ``createLinked`` and ``assignToWorkshop``
69+
- ``Units``: new functions ``getFocusPenalty``, ``unbailableSocialActivity``, ``isJobAvailable``
6570

6671
## Lua
6772

73+
- New functions: ``dfhack.jobs.createLinked``, ``dfhack.jobs.assignToWorkshop``, ``dfhack.units.getFocusPenalty``, ``dfhack.units.unbailableSocialActivity``, and ``dfhack.units.isJobAvailable``
74+
6875
## Removed
6976

7077
# 52.03-r1.1
@@ -100,6 +107,7 @@ Template for new versions:
100107
## Documentation
101108

102109
## API
110+
- Adjusted the logic inside ``Military::removeFromSquad`` to more closely match the game's own behavior
103111

104112
## Lua
105113

@@ -165,6 +173,7 @@ Template for new versions:
165173
## API
166174

167175
## Lua
176+
- ``widgets.Slider``: new mouse-controlled single-headed slider widget
168177

169178
## Removed
170179

docs/dev/Lua API.rst

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,10 @@ Job module
13131313

13141314
Creates a deep copy of the given job.
13151315

1316+
* ``dfhack.job.createLinked()``
1317+
1318+
Create a job and immediately link it into the global job list.
1319+
13161320
* ``dfhack.job.printJobDetails(job)``
13171321

13181322
Prints info about the job.
@@ -1338,6 +1342,12 @@ Job module
13381342

13391343
Searches for a specific_ref with the given type.
13401344

1345+
* ``dfhack.job.assignToWorkshop(job, workshop)``
1346+
1347+
Assign job to workshop (i.e. establish the bidirectional link between the job
1348+
and the workshop). Does nothing and returns ``false`` if the workshop already
1349+
has the maximum of ten jobs.
1350+
13411351
* ``dfhack.job.getHolder(job)``
13421352

13431353
Returns the building holding the job.
@@ -1628,7 +1638,7 @@ Units module
16281638
Returns true if the unit is within a box defined by the
16291639
specified coordinates.
16301640

1631-
``dfhack.units.getUnitsInBox(pos1, pos2[, filter])``
1641+
* ``dfhack.units.getUnitsInBox(pos1, pos2[, filter])``
16321642
* ``dfhack.units.getUnitsInBox(x1,y1,z1,x2,y2,z2[,filter])``
16331643

16341644
Returns a table of all units within the specified coordinates.
@@ -1894,6 +1904,22 @@ Units module
18941904
Return the ``df.activity_entry`` or ``df.activity_event`` representing the
18951905
unit's current social activity.
18961906

1907+
* ``dfhack.units.hasUnbailableSocialActivity(unit)``
1908+
1909+
Unit has an uninterruptible social activity (e.g. a purple "Socialize!").
1910+
1911+
* ``dfhack.units.isJobAvailable(unit [, interrupt_social])``
1912+
1913+
Check whether a unit can be assigned to (i.e. is looking for) a job. Will
1914+
return ``true`` if the unit is engaged in "green" social activities, unless
1915+
the boolean ``interrupt_social`` is true.
1916+
1917+
* ``dfhack.units.getFocusPenalty(unit, need_type [, need_type, ...])``
1918+
1919+
Get largest (i.e. most negative) focus penalty associated to a collection of
1920+
``df.need_type`` arguments. Returns a number strictly greater than 400 if the
1921+
unit does not have any of the requested needs.
1922+
18971923
* ``dfhack.units.getStressCategory(unit)``
18981924

18991925
Returns a number from 0-6 indicating stress. 0 is most stressed; 6 is least.
@@ -6515,10 +6541,24 @@ change, the ``RangeSlider`` appearance will adjust automatically.
65156541
:get_left_idx_fn: The function used by the RangeSlider to get the notch index on which
65166542
to display the left handle.
65176543
:get_right_idx_fn: The function used by the RangeSlider to get the notch index on which
6518-
to display the right handle.
6544+
to display the right handle.
65196545
:on_left_change: Callback executed when moving the left handle.
65206546
:on_right_change: Callback executed when moving the right handle.
65216547

6548+
Slider class
6549+
-----------------
6550+
6551+
This widget implements a mouse-interactable slider. The player can move the handle to
6552+
set the value of the slider. The parent widget owns the slider value, and can control
6553+
it independently (e.g., with a ``CycleHotkeyLabel``). If the value changes, the ``Slider``
6554+
appearance will adjust automatically.
6555+
6556+
:num_stops: Used to specify the number of "notches" in the slider, the places
6557+
where the handle can stop. (This should match the parents' number of options.)
6558+
:get_idx_fn: The function used by the Slider to get the notch index on which
6559+
to display the handle.
6560+
:on_change: Callback executed when moving the handle.
6561+
65226562
DimensionsTooltip class
65236563
-----------------------
65246564

docs/plugins/nestboxes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@ This plugin will automatically scan for and forbid fertile eggs incubating in a
1010
nestbox so that dwarves won't come to collect them for eating. The eggs will
1111
hatch normally, even when forbidden.
1212

13+
You can control which eggs are collected and which eggs are protected by placing
14+
the nestboxes whose contents should be protected inside a burrow and running
15+
``nestboxes burrow <burrow_name>``. The default behavior of protecting all
16+
fertile eggs in nest boxes can be reestablished by running ``nestboxes all``.
17+
1318
Usage
1419
-----
1520

1621
::
1722

1823
enable nestboxes
24+
nestboxes burrow <burrow_name>
25+
nestbox all

docs/plugins/orders.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ game does not provide sufficient order conditions. Please enable ``automilk``,
151151
types of jobs.
152152

153153
Note that the jugs are specifically made out of wood. This is so, as long as
154-
you don't may any other "Tools" out of wood, you can have a stockpile just for
154+
you don't make any other "Tools" out of wood, you can have a stockpile just for
155155
jugs by restricting a finished goods stockpile to only take wooden tools.
156156

157157
Armok's additional note: "shleggings? Yes,

library/LuaApi.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ distribution.
6969
#include "df/building_civzonest.h"
7070
#include "df/building_stockpilest.h"
7171
#include "df/building_tradedepotst.h"
72+
#include "df/building_workshopst.h"
7273
#include "df/burrow.h"
7374
#include "df/caravan_state.h"
7475
#include "df/construction.h"
@@ -116,6 +117,7 @@ distribution.
116117
#include <string>
117118
#include <vector>
118119
#include <filesystem>
120+
#include <stdexcept>
119121

120122
namespace DFHack {
121123
DBG_DECLARE(core, luaapi, DebugCategory::LINFO);
@@ -629,7 +631,7 @@ void Lua::Push(lua_State *L, const Screen::Pen &info)
629631
return;
630632
}
631633

632-
new (L) Pen(info);
634+
Lua::make_lua_userdata<Pen>(L, info);
633635

634636
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN);
635637
lua_setmetatable(L, -2);
@@ -1128,7 +1130,7 @@ static int dfhack_random_init(lua_State *L)
11281130

11291131
static int dfhack_random_new(lua_State *L)
11301132
{
1131-
new (L) MersenneRNG();
1133+
Lua::make_lua_userdata<MersenneRNG>(L);
11321134

11331135
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN);
11341136
lua_setmetatable(L, -2);
@@ -1212,19 +1214,19 @@ static int dfhack_random_perlin(lua_State *L)
12121214
switch (size)
12131215
{
12141216
case 1: {
1215-
auto pdata = new (L) PerlinNoise1D<float>();
1217+
auto pdata = Lua::make_lua_userdata<PerlinNoise1D<float>>(L);
12161218
pdata->init(*prng);
12171219
lua_pushcclosure(L, eval_perlin_1, 1);
12181220
break;
12191221
}
12201222
case 2: {
1221-
auto pdata = new (L) PerlinNoise2D<float>();
1223+
auto pdata = Lua::make_lua_userdata<PerlinNoise2D<float>>(L);
12221224
pdata->init(*prng);
12231225
lua_pushcclosure(L, eval_perlin_2, 1);
12241226
break;
12251227
}
12261228
case 3: {
1227-
auto pdata = new (L) PerlinNoise3D<float>();
1229+
auto pdata = Lua::make_lua_userdata<PerlinNoise3D<float>>(L);
12281230
pdata->init(*prng);
12291231
lua_pushcclosure(L, eval_perlin_3, 1);
12301232
break;
@@ -1903,6 +1905,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
19031905
WRAPM(Job,disconnectJobItem),
19041906
WRAPM(Job,disconnectJobGeneralRef),
19051907
WRAPM(Job,removeJob),
1908+
WRAPM(Job,createLinked),
1909+
WRAPM(Job,assignToWorkshop),
19061910
WRAPN(is_equal, jobEqual),
19071911
WRAPN(is_item_equal, jobItemEqual),
19081912
{ NULL, NULL }
@@ -2129,6 +2133,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
21292133
WRAPM(Units, setGroupActionTimers),
21302134
WRAPM(Units, getUnitByNobleRole),
21312135
WRAPM(Units, unassignTrainer),
2136+
WRAPM(Units, hasUnbailableSocialActivity),
2137+
WRAPM(Units, isJobAvailable),
21322138
{ NULL, NULL }
21332139
};
21342140

@@ -2322,6 +2328,25 @@ static int units_getProfessionName(lua_State *L) {
23222328
return 1;
23232329
}
23242330

2331+
int32_t units_getFocusPenalty(lua_State *L) {
2332+
auto unit = Lua::GetDFObject<df::unit>(L, 1);
2333+
Units::need_type_set needs;
2334+
auto top = lua_gettop(L);
2335+
if (top < 2) {
2336+
luaL_argerror(L, 2, "Expected at least one need type");
2337+
} else {
2338+
for (int i = 2; i <= top; ++i) {
2339+
try {
2340+
needs.set(luaL_checkint(L, i));
2341+
} catch ([[maybe_unused]] const std::out_of_range &e) {
2342+
luaL_argerror(L, i, "Expected a need type");
2343+
}
2344+
}
2345+
Lua::Push(L, Units::getFocusPenalty(unit, needs));
2346+
}
2347+
return 1;
2348+
}
2349+
23252350
static const luaL_Reg dfhack_units_funcs[] = {
23262351
{ "getPosition", units_getPosition },
23272352
{ "getOuterContainerRef", units_getOuterContainerRef },
@@ -2336,6 +2361,7 @@ static const luaL_Reg dfhack_units_funcs[] = {
23362361
{ "getReadableName", units_getReadablename },
23372362
{ "getVisibleName", units_getVisibleName },
23382363
{ "getProfessionName", units_getProfessionName },
2364+
{ "getFocusPenalty", units_getFocusPenalty },
23392365
{ NULL, NULL }
23402366
};
23412367

library/LuaWrapper.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,15 @@ static void BuildTypeMetatable(lua_State *state, const type_identity *type);
168168
void LuaWrapper::push_object_ref(lua_State *state, void *ptr)
169169
{
170170
// stack: [metatable]
171-
auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader));
172-
ref->ptr = ptr;
173-
ref->field_info = NULL;
174-
ref->tag_ptr = NULL;
175-
ref->tag_identity = NULL;
176-
ref->tag_attr = NULL;
171+
void* stg = lua_newuserdata(state, sizeof(DFRefHeader));
172+
new (stg) DFRefHeader
173+
{
174+
.ptr = ptr,
175+
.field_info = NULL,
176+
.tag_ptr = NULL,
177+
.tag_identity = NULL,
178+
.tag_attr = NULL,
179+
};
177180

178181
lua_swap(state);
179182
lua_setmetatable(state, -2);

library/include/LuaTools.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@ distribution.
4444
#include <lua.h>
4545
#include <lauxlib.h>
4646

47-
/// Allocate a new user data object and push it on the stack
48-
inline void *operator new (std::size_t size, lua_State *L) {
49-
return lua_newuserdata(L, size);
50-
}
51-
5247
namespace DFHack {
5348
class function_identity_base;
5449
struct MaterialInfo;
@@ -67,6 +62,20 @@ namespace DFHack::Lua {
6762
*/
6863
DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL);
6964

65+
/**
66+
* Allocate a lua userdata and construct a C++ object in that userdata's storage space
67+
* The C++ object must be trivially destructible as lua GC will _not_ call the object's destructor
68+
* be aware that the created userdata is left on the Lua stack as well as returned to the caller
69+
*/
70+
template <typename T, typename... Args>
71+
requires (std::is_trivially_destructible_v<T>)
72+
T* make_lua_userdata(lua_State* L, Args&&... args)
73+
{
74+
void* stg = lua_newuserdata(L, sizeof(T));
75+
T * obj = ::new (stg) T(std::forward<Args>(args)...);
76+
return obj;
77+
}
78+
7079
DFHACK_EXPORT void PushDFHack(lua_State *state);
7180
DFHACK_EXPORT void PushBaseGlobals(lua_State *state);
7281

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_type_set = std::bitset<ENUM_LAST_ITEM(need_type)+1UL>;
346+
DFHACK_EXPORT int32_t getFocusPenalty(df::unit* unit, need_type_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 hasUnbailableSocialActivity(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);

0 commit comments

Comments
 (0)