Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Template for new versions:

## Fixes
- `getplants`: will no longer crash when faced with plants with growths that do not drop seeds when processed
- `getplants`: use updated formula for calculating whether plant growths are ripe
- `getplants`: fix logic for determining whether plant growths have been picked
- `gui/teleport`: adapt to new behavior in DF 51.11 to avoid a crash when teleporting items into mid-air
- `script-manager`: fix lua scripts in mods not being reloaded properly upon entering a saved world on Windows

Expand All @@ -66,6 +68,7 @@ Template for new versions:
## Documentation

## API
- ``Random`` module: added ``SplitmixRNG`` class, implements the Splitmix64 RNG used by Dwarf Fortress for "simple" randomness
- ``Items::getDescription``: fixed display of quality levels, now displays ALL item designations (in correct order) and obeys vanilla SHOW_IMP_QUALITY setting

## Lua
Expand Down
31 changes: 31 additions & 0 deletions library/include/modules/Random.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,37 @@ namespace Random
extern template void DFHACK_IMPORT MersenneRNG::unitvector<double>(double *p, int size);
#endif

// Standard Splitmix64 RNG, as used by Dwarf Fortress's "hash_rngst" class
class SplitmixRNG
{
uint64_t state;

public:
SplitmixRNG(uint64_t seed) {
init(seed);
}

void init(uint64_t seed) {
state = seed;
}

uint64_t next() {
state += 0x9e3779b97f4a7c15;
uint64_t z = state;
z ^= z >> 30;
z *= 0xbf58476d1ce4e5b9;
z ^= z >> 27;
z *= 0x94d049bb133111eb;
z ^= z >> 31;
return z;
}

int32_t df_trandom(uint32_t max) {
uint32_t val = next() >> 32;
return (int32_t)(val % max);
}
};

/*
* Classical Perlin noise function in template form.
* http://mrl.nyu.edu/~perlin/doc/oscar.html#noise
Expand Down
44 changes: 22 additions & 22 deletions plugins/getplants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "modules/Designations.h"
#include "modules/Maps.h"
#include "modules/Materials.h"
#include "modules/Random.h"

#include "df/map_block.h"
#include "df/map_block_column.h"
Expand Down Expand Up @@ -211,32 +212,31 @@ selectability selectablePlant(color_ostream& out, const df::plant_raw* plant, bo
}

// Formula for determination of the variance in plant growth maturation time, determined via disassembly.
// The x and y parameters are in tiles relative to the embark.
bool ripe(int32_t x, int32_t y, int32_t start, int32_t end) {
int32_t time = (((435522653 - (((y + 3) * x + 5) * ((y + 7) * y * 400181475 + 289700012))) & 0x3FFFFFFF) % 2000 + *cur_year_tick) % 403200;
// The coordinates are relative to the embark region.
bool ripe(int32_t x, int32_t y, int32_t z, int32_t start, int32_t end) {
DFHack::Random::SplitmixRNG rng((world->map.region_x * 48 + x) + (world->map.region_y * 48 + y) * 10000 + (world->map.region_z + z) * 100000000);
int32_t time = (rng.df_trandom(2000) + *cur_year_tick) % 403200;

return time >= start && (end == -1 || time <= end);
}

// Looks in the picked growths vector to see if a matching growth has been marked as picked.
bool picked(const df::plant* plant, int32_t growth_subtype) {
df::world_data* world_data = world->world_data;
df::world_site* site = df::world_site::find(plotinfo->site_id);
int32_t pos_x = site->global_min_x + plant->pos.x / 48;
int32_t pos_y = site->global_min_y + plant->pos.y / 48;
size_t id = pos_x + pos_y * 16 * world_data->world_width;
df::world_object_data* object_data = df::world_object_data::find(id);
if (!object_data) {
// Looks in the local creation zone's picked growths vector to see if a matching growth has been marked as picked.
bool picked(const df::plant* plant, int32_t growth_subtype, int32_t growth_density) {
int32_t pos_x = plant->pos.x / 48 + world->map.region_x;
int32_t pos_y = plant->pos.y / 48 + world->map.region_y;
size_t cz_id = pos_x + pos_y * 16 * world->world_data->world_width;
auto cz = df::world_object_data::find(cz_id);
if (!cz) {
return false;
}
df::map_block_column* column = world->map.map_block_columns[(plant->pos.x / 16) * world->map.x_count_block + (plant->pos.y / 16)];

for (size_t i = 0; i < object_data->picked_growths.x.size(); i++) {
if (object_data->picked_growths.x[i] == plant->pos.x &&
object_data->picked_growths.y[i] == plant->pos.y &&
object_data->picked_growths.z[i] - column->z_base == plant->pos.z &&
object_data->picked_growths.subtype[i] == growth_subtype &&
object_data->picked_growths.year[i] == *cur_year) {

for (size_t i = 0; i < cz->picked_growths.x.size(); i++) {
if (cz->picked_growths.x[i] == (plant->pos.x % 48) &&
cz->picked_growths.y[i] == (plant->pos.y % 48) &&
cz->picked_growths.z[i] == (plant->pos.z + world->map.region_z) &&
cz->picked_growths.density[i] >= growth_density &&
cz->picked_growths.subtype[i] == growth_subtype &&
cz->picked_growths.year[i] == *cur_year) {
return true;
}
}
Expand Down Expand Up @@ -310,8 +310,8 @@ bool designate(color_ostream& out, const df::plant* plant, bool farming) {
}

if ((!farming || seedSource) &&
ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2) &&
!picked(plant, i))
ripe(plant->pos.x, plant->pos.y, plant->pos.z, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2) &&
!picked(plant, i, plant_raw->growths[i]->density))
return Designations::markPlant(plant);
}

Expand Down