Skip to content

Commit

Permalink
Better leaves grow
Browse files Browse the repository at this point in the history
  • Loading branch information
proller committed Sep 10, 2023
1 parent abc4309 commit d6473e2
Showing 1 changed file with 134 additions and 89 deletions.
223 changes: 134 additions & 89 deletions src/content_abm_grow_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,6 @@ inline auto getLight(const auto &ndef, const auto &n)
n.getLight(LIGHTBANK_NIGHT, lightingFlags));
}

const v3pos_t leaves_grow_dirs[] = {
// +right, +top, +back
v3pos_t{0, 1, 0}, // 1 top
v3pos_t{0, 0, 1}, // 2 back
v3pos_t{0, 0, -1}, // 3 front
v3pos_t{1, 0, 0}, // 4 right
v3pos_t{-1, 0, 0}, // 5 left
v3pos_t{0, -1, 0}, // 6 bottom
};

const v3pos_t leaves_look_dirs[] = {
// +right, +top, +back
v3pos_t{0, 0, 0}, // 0 self
Expand All @@ -78,13 +68,13 @@ const v3pos_t leaves_look_dirs[] = {
v3pos_t{0, -1, 0}, // 6 bottom
};

constexpr auto D_BOTTOM = 6;
constexpr auto D_TOP = 1;
constexpr auto D_SELF = 0;
constexpr auto D_TOP = 1;
constexpr auto D_BACK = 2;
constexpr auto D_FRONT = 3;
constexpr auto D_RIGHT = 4;
constexpr auto D_LEFT = 5;
constexpr auto D_BOTTOM = 6;

struct GrowParams
{
Expand All @@ -98,9 +88,9 @@ struct GrowParams
int tree_grow_chance = 10;
int leaves_water_max = 20; // todo: depend on humidity 2-20
int leaves_grow_light_min = 8;
int leaves_grow_water_min_top = 3;
int leaves_grow_water_min_bottom = 4;
int leaves_grow_water_min_side = 2;
int leaves_grow_water_min_top = 4;
int leaves_grow_water_min_bottom = 5;
int leaves_grow_water_min_side = 3;
int leaves_grow_heat_max = 40;
int leaves_grow_heat_min = 3;
int leaves_grow_prefer_top = 0;
Expand Down Expand Up @@ -518,7 +508,7 @@ class GrowTree : public ActiveBlockModifier
return false;

if (!decrease(self_water_level)) {
// if (grow_debug) DUMP("pumpfail", n_water_level, n_water_level_orig, wl_dir, top, bottom, c_dir, c);
// if (grow_debug) DUMP("pumpfail", n_water_level, n_water_level_orig, wl_dir, top, bottom, nb.content, c);
return true;
}
//if (grow_debug)DUMP("tr pump", pos.Y, self_water_level,self_water_level_orig, water_level, nb.top,nb.bottom, nb.content, content);
Expand Down Expand Up @@ -749,52 +739,111 @@ class GrowLeaves : public ActiveBlockModifier
bool getSimpleCatchUp() override { return true; }
virtual pos_t getMinY() override { return -MAX_MAP_GENERATION_LIMIT; };
virtual pos_t getMaxY() override { return MAX_MAP_GENERATION_LIMIT; };
virtual void trigger(ServerEnvironment *env, v3pos_t p, MapNode n,
virtual void trigger(ServerEnvironment *env, v3pos_t pos, MapNode n,
u32 active_object_count, u32 active_object_count_wider, v3pos_t,
bool activate) override
{
ServerMap *map = &env->getServerMap();
const auto *ndef = env->getGameDef()->ndef();
float heat = map->updateBlockHeat(env, p);
const auto c = n.getContent();
const auto params = type_params.at(c);

int n_water_level = get_leaves_water_level(n);
const auto n_water_level_orig = n_water_level;

const auto light = getLight(ndef, n);

uint8_t i = 0;
float heat = map->updateBlockHeat(env, pos);

content_t c_fruit{};
bool top_is_full_liquid = false;
bool have_tree_or_soil = false;
bool have_air = false;
bool allow_grow_fruit = leaves_to_fruit.contains(c);
const content_t c_fruit =
allow_grow_fruit ? leaves_to_fruit.at(c) : CONTENT_IGNORE;
// TODO: choose pump and grow direction by leaves type
for (const auto &dir : leaves_grow_dirs) {
const auto p_dir = p + dir;
auto n_dir = map->getNodeTry(p_dir);
if (!n_dir) {
have_tree_or_soil = true; // dont remove when map busy
allow_grow_fruit = false;
have_air = false;
continue;
bool allow_grow_fruit = false;

struct Neighbor
{
MapNode node{};
content_t content{CONTENT_IGNORE};
bool is_liquid{false};
bool is_my_leaves{false};
bool is_any_leaves{false};
bool is_tree{false};
bool top{false};
bool bottom{false};
uint8_t light{0};
ContentFeatures *cf{nullptr};
int16_t water_level{0};
v3pos_t pos{};
};

Neighbor nbh[7]{};
{
size_t i = 0;
for (const auto &dir : leaves_look_dirs) {
auto &nb = nbh[i];
const bool is_self = i == D_SELF;

nb.top = i == D_TOP;
nb.bottom = i == D_BOTTOM;
nb.pos = pos + dir;
nb.node = is_self ? n : map->getNodeTry(nb.pos);
if (!nb.node) {
have_tree_or_soil = true; // dont remove when map busy
allow_grow_fruit = false;
have_air = false;
goto NEXT;
}

nb.light = getLight(ndef, nb.node);
nb.content = nb.node.getContent();
nb.is_my_leaves = is_self || nb.content == nbh[D_SELF].content;
nb.cf = (ContentFeatures *)&ndef->get(nb.content);
nb.is_tree = nb.cf->groups.contains("tree");
nb.is_any_leaves = nb.cf->groups.contains("leaves");
nb.is_liquid = nb.cf->groups.contains("liquid");
nb.water_level = nb.is_my_leaves ? get_leaves_water_level(nb.node)
: nb.is_tree ? get_tree_water_level(nb.node)
: 0;
top_is_full_liquid = nb.top && nb.is_liquid &&
nb.node.getMaxLevel(ndef) == nb.node.getLevel(ndef);

if (is_self) {
allow_grow_fruit = leaves_to_fruit.contains(nbh[D_SELF].content);

c_fruit = allow_grow_fruit ? leaves_to_fruit.at(nbh[D_SELF].content)
: CONTENT_IGNORE;
} else {
if ((nb.content == c_fruit) ||
(!nb.top && !nb.bottom && !nb.is_any_leaves))
allow_grow_fruit = false;

if (nb.is_tree) // no fruit near tree
allow_grow_fruit = false;

if (!have_tree_or_soil)
have_tree_or_soil = nb.is_tree || nb.is_any_leaves ||
nb.cf->groups.contains("soil") ||
nb.is_liquid;
if (!have_air)
have_air = nb.content == CONTENT_AIR;
}

NEXT:
++i;
}
const auto light_dir = getLight(ndef, n_dir);
}

auto c_dir = n_dir.getContent();
const auto params = type_params.at(nbh[D_SELF].content);

const auto &cf = ndef->get(c_dir);
bool is_tree = cf.groups.contains("tree");
bool is_leaves = cf.groups.contains("leaves");
bool is_liquid = cf.groups.contains("liquid");
bool top = !i;
bool bottom = i + 1 == sizeof(leaves_grow_dirs) / sizeof(leaves_grow_dirs[0]);
auto &n_water_level = nbh[D_SELF].water_level;
const auto n_water_level_orig = n_water_level;

// TODO: choose pump and grow direction by leaves type

top_is_full_liquid =
top && is_liquid && n_dir.getMaxLevel(ndef) == n_dir.getLevel(ndef);
std::array<int8_t, 6> grow_order{
D_TOP, D_BACK, D_FRONT, D_RIGHT, D_LEFT, D_BOTTOM};

std::sort(grow_order.begin(), grow_order.end(),
[&](auto &a, auto &b) { return nbh[a].light > nbh[b].light; });

for (auto &i : grow_order) {
auto &nb = nbh[i];

if (!nb.node)
continue;

/*todo: shapes:
o sphere
Expand All @@ -803,72 +852,68 @@ class GrowLeaves : public ActiveBlockModifier
\ /
*/

if ((c_dir == c_fruit) || (!top && !bottom && !is_leaves))
allow_grow_fruit = false;
if (is_tree)
allow_grow_fruit = false;

if (!have_tree_or_soil)
have_tree_or_soil =
is_tree || is_leaves || cf.groups.contains("soil") || is_liquid;
if (!have_air)
have_air = c_dir == CONTENT_AIR;

//DUMP("lv?", i, n_water_level, params.leaves_grow_heat_min, heat, params.leaves_grow_heat_min, light_dir, is_liquid, cf.buildable_to);
//DUMP("lv?", (int)i, n_water_level, params.leaves_grow_heat_min, heat, params.leaves_grow_heat_min, nb.light, nb.is_liquid, nb.cf->buildable_to);

if ((!params.leaves_grow_heat_min || heat >= params.leaves_grow_heat_min) &&
(!params.leaves_grow_heat_max ||
heat <= params.leaves_grow_heat_max) &&
can_grow_leaves(params, n_water_level, top, bottom) &&
light_dir >= params.leaves_grow_light_min && cf.buildable_to &&
!is_liquid) {
//if (grow_debug)DUMP("lv->lv ", p.X, p.Y, p.Z, c_dir, c, l, n_water_level,n_water_level_orig, l, ndef->get(c_dir).name);
map->setNode(p_dir, {c, n_dir.getParam1(), 1});
--n_water_level;
can_grow_leaves(params, n_water_level, nb.top, nb.bottom) &&
nb.light >= params.leaves_grow_light_min && nb.cf->buildable_to &&
!nb.is_liquid) {
//if (grow_debug)DUMP("lv->lv ", p.X, p.Y, p.Z, nb.content, c, l, n_water_level,n_water_level_orig, l, ndef->get(nb.content).name);
map->setNode(nb.pos, {nbh[D_SELF].content, nb.node.getParam1(), 2});
n_water_level -= 2;

if (!myrand_range(0, 10))
if (const auto block = map->getBlock(getNodeBlockPos(p_dir)); block) {
if (const auto block = map->getBlock(getNodeBlockPos(nb.pos));
block) {
block->setLightingComplete(0);
}

} else if (c_dir == c) {
const auto l_dir = getLight(ndef, n_dir);

auto wl_dir = get_leaves_water_level(n_dir);
if (n_water_level > 1 && wl_dir < params.leaves_water_max &&
l_dir >= light &&
break;
} else if (nb.content == nbh[D_SELF].content) {
if (n_water_level > 1 && nb.water_level < params.leaves_water_max &&
nb.light >= nbh[D_SELF].light &&
// todo: all up by type?
wl_dir < n_water_level - 1 //(top ? -1 :
// bottom ? 1 : -2)
nb.water_level < n_water_level - 1 //(top ? -1 :
// bottom ? 1 : -2)
) {
--n_water_level;
set_leaves_water_level(n_dir, ++wl_dir);
map->setNode(p_dir, n_dir);
set_leaves_water_level(nb.node, ++nb.water_level);
map->setNode(nb.pos, nb.node);

//if (grow_debug)DUMP("lv pumpup2", p.Y, n_water_level, n_water_level_orig, wl_dir,top, bottom, c_dir);
//if (grow_debug)DUMP("lv pumpup2", p.Y, n_water_level, n_water_level_orig, wl_dir,top, bottom, nb.content);
break;

// Prefer pump up
// todo: its like cypress, by settings
if (top && params.leaves_grow_prefer_top) {
break;
}
// if (nb.top && params.leaves_grow_prefer_top)break;
}
}

++i;
}

const auto can_decrease = nbh[D_SELF].light < LIGHT_SUN - 1;
if (n_water_level > 1 && can_decrease &&
(!myrand_range(0, 10 * (grow_debug_fast ? 1 : 10)))) {
float humidity = map->updateBlockHumidity(env, pos);
if (humidity < params.tree_get_water_from_humidity)
--n_water_level;
}

// DUMP(allow_grow_fruit, n_water_level, leaves_to_fruit_water_min, heat, leaves_to_fruit_heat_min);
if (allow_grow_fruit && n_water_level >= params.leaves_to_fruit_water_min &&
heat >= params.leaves_to_fruit_heat_min &&
light >= params.leaves_to_fruit_light_min &&
nbh[D_SELF].light >= params.leaves_to_fruit_light_min &&
(grow_debug_fast || !myrand_range(0, params.leaves_to_fruit_chance))) {
map->setNode(p, {c_fruit});
map->setNode(pos, {c_fruit});
} else if (
(n_water_level == 1 && can_decrease &&
(!myrand_range(0, 30 * (grow_debug_fast ? 1 : 10)))) ||
(n_water_level >= 1 && // dont touch old static trees
have_air &&
((light < params.leaves_die_light_max &&
(light > 0 || activate ||
((nbh[D_SELF].light < params.leaves_die_light_max &&
(nbh[D_SELF].light > 0 || activate ||
!myrand_range(0, params.leaves_die_chance))) ||
((params.leaves_die_heat_max &&
heat < params.leaves_die_heat_max) ||
Expand All @@ -879,11 +924,11 @@ class GrowLeaves : public ActiveBlockModifier
(activate || !myrand_range(0, 10)))) {
//if (grow_debug) DUMP("lv die", p.X, p.Y, p.Z, have_tree_or_soil, n_water_level, l, heat);
//if (!grow_debug_no_die)
map->removeNodeWithEvent(p, false);
map->removeNodeWithEvent(pos, false);
} else if (n_water_level != n_water_level_orig) {
// save if self level changed
set_leaves_water_level(n, n_water_level);
map->setNode(p, n);
map->setNode(pos, n);
}
}
};
Expand Down

0 comments on commit d6473e2

Please sign in to comment.