Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 ... 22 23 [24]

Author Topic: Roses' Script, System, and Utilities Collection (07/06/2021)  (Read 91355 times)

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #345 on: June 02, 2021, 10:41:28 am »

plant.lua looks like exactly what I need to simulate tree chopping. I was worried that I'd just have to ignore trees entirely if I couldn't figure it out! Do you mind if I add that to either dig-dug itself or the dfhack core library?

tiletypes might also be useful to force a tile into a particular shape/configuration. I'm writing dig-dug because I specifically want the tile tile transformations to follow the rules of "organic" digging.

If you think you could use the quickfort buildings db, I can work on putting it behind an API so it's queryable by multiple indices (e.g. by type,subtype,custom instead of its current "ui keystroke sequence" map key

Any of the code I write can be freely used by anyone. I do request that there is some mention of where it comes from (mainly so if there are questions about it in the future it is easier to track down). Before you change any code, let me make sure that I could actually make use of it :)

plant.lua looks like exactly what I need to simulate tree chopping. I was worried that I'd just have to ignore trees entirely if I couldn't figure it out!

I don't understand everything that's going on in the script, but it doesn't look like it will cause items to fall out of the removed tree, or water to recalculate. In other words, it's equivalent to using a ballista bolt.

dfhack.items.makeProjectile can be used to cause an item to fall, and liquids plugin probably has the proper way to update fluids. Should be simple enough to implement.



I've been preparing to disassemble the exact code for tree chopping by starting with understanding the less complex function for finding a plant at a tile (that the tree chopping function calls.) Knowing I'm probably seeing:
Code: [Select]
y_column = math.floor(base.y/16)
y_column-y_column%3
when I'm looking at
Code: [Select]
movsx   r12d, r8w ; y coord into r12
mov     eax, 2AAAAAABh
imul    r12d
mov     edi, r12d
movzx   ecx, r12w
sar     edx, 3
mov     eax, edx
shr     eax, 1Fh
add     edx, eax
lea     eax, [rdx+rdx*2]
shl     eax, 4
sub     edi, eax
sub cx, di
mov     [rsp+30h+arg_8], edi
is very helpful!

Edit: Actually, looks like the above assembly calculates math.floor(base.x / 48) * 48 and base.y%48 (which it later uses with dim_y to completely avoid using extent_north, etc.)

The optimized code used by DF is:
Code: [Select]
map_block_column = df.global.world.map.column_index[(math.floor(base.x / 48) * 48) / 16][(math.floor(base.y / 48) * 48) / 16]which avoids %3.

That's some interesting stuff, you are correct that the currently removal doesn't cause items to fall or water to recalculate. It also sometimes leaves a few branches way up high, and can mess up if there are multiple trees in overlapping vicinity, so it is definitely a work in progress. It definitely wouldn't be hard to loop over any items in the tree and turn them into projectiles and loop over any water tiles and add the update flows flag.
Logged

Bumber

  • Bay Watcher
  • REMOVE KOBOLD
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #346 on: June 02, 2021, 07:53:08 pm »

It definitely wouldn't be hard to loop over any items in the tree and turn them into projectiles and loop over any water tiles and add the update flows flag.

As far as I know, you can just enable updates in the tile where the plant coordinate was, and it will end up updating the whole body of water. (Unless the liquids command is doing more than just setting the single tile's flag.) Although you'd probably want to set the flag where each solid tree tile was, just in case there are setups where that matters.
Logged
Reading his name would trigger it. Thinking of him would trigger it. No other circumstances would trigger it- it was strictly related to the concept of Bill Clinton entering the conscious mind.

THE xTROLL FUR SOCKx RUSE WAS A........... DISTACTION        the carp HAVE the wagon

A wizard has turned you into a wagon. This was inevitable (Y/y)?

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #347 on: June 03, 2021, 09:49:59 am »

It definitely wouldn't be hard to loop over any items in the tree and turn them into projectiles and loop over any water tiles and add the update flows flag.

As far as I know, you can just enable updates in the tile where the plant coordinate was, and it will end up updating the whole body of water. (Unless the liquids command is doing more than just setting the single tile's flag.) Although you'd probably want to set the flag where each solid tree tile was, just in case there are setups where that matters.

Yes, I think that is correct. It would be interesting to see if the actual tree cutting functions that DF uses causes some other updates. I suppose the inside/outside or light/dark flags (can't remember their exact names) are also probably updated, but I'm sure there are more that I am not thinking about.
Logged

Bumber

  • Bay Watcher
  • REMOVE KOBOLD
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #348 on: June 03, 2021, 01:40:31 pm »

I suppose the inside/outside or light/dark flags (can't remember their exact names) are also probably updated, but I'm sure there are more that I am not thinking about.

I don't think trees affect subterranean/above ground or light/dark flags, just outside/inside (like buildings.) Looks like your script always sets the tile to outside, which doesn't make sense for cavern trees. Something more may need to be done there, like setting it to the respective x/y tiles in the z-level above the tree.
Logged
Reading his name would trigger it. Thinking of him would trigger it. No other circumstances would trigger it- it was strictly related to the concept of Bill Clinton entering the conscious mind.

THE xTROLL FUR SOCKx RUSE WAS A........... DISTACTION        the carp HAVE the wagon

A wizard has turned you into a wagon. This was inevitable (Y/y)?

myk

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #349 on: June 03, 2021, 07:03:54 pm »

Something more may need to be done there, like setting it to the respective x/y tiles in the z-level above the tree.
This is what I do for the (in-progress) dig-now plugin so light can filter down through newly dug ramps/channels. Code is in C++ but we could probably move it to the core library and expose it via the Lua API.

Code: [Select]
    df::tile_designation td = map.designationAt(pos);

    if (!map.ensureBlockAt(DFCoord(pos.x, pos.y, pos.z+1))) {
        // only the sky above
        td.bits.light = true;
        td.bits.outside = true;
        td.bits.subterranean = false;
    }

    int32_t zlevel = pos.z;
    df::tiletype_shape shape =
            tileShape(map.tiletypeAt(DFCoord(pos.x, pos.y, zlevel)));
    while ((shape == df::tiletype_shape::EMPTY
            || shape == df::tiletype_shape::RAMP_TOP)
           && map.ensureBlockAt(DFCoord(pos.x, pos.y, --zlevel))) {
        DFCoord pos_below(pos.x, pos.y, zlevel);
        df::tile_designation td_below = map.designationAt(pos_below);
        if (td_below.bits.light == td.bits.light
                && td_below.bits.outside == td.bits.outside
                && td_below.bits.subterranean == td.bits.subterranean)
            break;
        td_below.bits.light = td.bits.light;
        td_below.bits.outside = td.bits.outside;
        td_below.bits.subterranean = td.bits.subterranean;
        map.setDesignationAt(pos_below, td_below);
        shape = tileShape(map.tiletypeAt(pos_below));
    }
Logged

Bumber

  • Bay Watcher
  • REMOVE KOBOLD
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #350 on: June 06, 2021, 11:04:58 am »

Here's a simplification of the code for erasing the plant from the map column:
Code: [Select]
-- Erase plant from correct map column
map_block_column = df.global.world.map.column_index[base.x//48*3][base.y//48*3]
for i,plant in pairs(map_block_column.plants) do
if plant.pos == base then
map_block_column.plants:erase(i)
break
end
end
"//" is integer division, which avoids the need for "math.floor()".
Note that "n//m" equals "n - n%m", because it's the non-remainder portion. Edit: I guess it's "(n - n%m)/m".
Dividing by 48 instead of 3 (since 48 = 3 * 16) takes care of the column conversion.
(It's worth pointing out that "base.x//48" is a mid-level tile coord. MLT's are 3*3 tile block columns, so "base.x//48*3" is a tile block column coord. It all checks out.)
Reused "map_block_column" instead of calculating it again.


Some shroom trees use just "body.connection_north" to indicate part of their cap, so you'll need to add that to "getPositions()". You also used z1,y1,x1 in the displace logic instead of the loop variables z,y,x.

You're probably going to need to remove roots, unless that's handled automatically somehow. "tree.roots" is similar to "tree.body", except it's upside-down. It's indexed from 0 to "tree.root_depth - 1". Presence of a root is indicated by "trunk" flag. Should be as simple as adding a second z loop after the first one. (Technically, you could get away with just "tree.roots[0]" in the current version of DF, but a loop is forward compatible for root_depth > 1.)

Changes:
Code: [Select]
...
local z1 = tree.pos.z
local z2 = tree.pos.z + tree.tree_info.body_height - 1
local z_root1 = tree.pos.z - 1
local z_root2 = tree.pos.z - tree.tree_info.root_depth
for x = x1,x2 do
for y = y1,y2 do
for z = z1,z2 do
pos = {x=x,y=y,z=z}
body = tree.tree_info.body[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if body.trunk or body.twigs or body.branches or body.connection_north then
positions[#positions+1] = pos
end
end
for z = z_root2,z_root1 do
pos = {x=x,y=y,z=z}
root = tree.tree_info.root[pos.z - z - 1]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if root.trunk then
positions[#positions+1] = pos
end
end
end
...
I also simplified the if statements for body checks.

All that's left is to account for "position.z < base.z" for the tiletypes. Assuming roots can't grow in stone (need to test this,) then all you have to do is convert those positions to soil walls (tiletype 261.)

For the base, the floor tiletype should chose randomly from {348,349,350,351} instead of just 350 for tile variations. (That was probably already on the to-do list.)
« Last Edit: June 13, 2021, 05:54:44 am by Bumber »
Logged
Reading his name would trigger it. Thinking of him would trigger it. No other circumstances would trigger it- it was strictly related to the concept of Bill Clinton entering the conscious mind.

THE xTROLL FUR SOCKx RUSE WAS A........... DISTACTION        the carp HAVE the wagon

A wizard has turned you into a wagon. This was inevitable (Y/y)?

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #351 on: June 07, 2021, 03:42:11 pm »

I suppose the inside/outside or light/dark flags (can't remember their exact names) are also probably updated, but I'm sure there are more that I am not thinking about.

I don't think trees affect subterranean/above ground or light/dark flags, just outside/inside (like buildings.) Looks like your script always sets the tile to outside, which doesn't make sense for cavern trees. Something more may need to be done there, like setting it to the respective x/y tiles in the z-level above the tree.
Ah, you are correct, yeah that doesn't make much sense for underground trees.

Here's a simplification of the code for erasing the plant from the map column:
Code: [Select]
-- Erase plant from correct map column
map_block_column = df.global.world.map.column_index[base.x//48*3][base.y//48*3]
for i,plant in pairs(map_block_column.plants) do
if plant.pos == base then
map_block_column.plants:erase(i)
break
end
end
"//" is integer division, which avoids the need for "math.floor()".
Note that "n//m" equals "n - n%m", because it's the non-remainder portion.
Dividing by 48 instead of 3 (since 48 = 3 * 16) takes care of the column conversion.
(It's worth pointing out that "base.x//48" is a mid-level tile coord. MLT's are 3*3 tile block columns, so "base.x//48*3" is a tile block column coord. It all checks out.)
Reused "map_block_column" instead of calculating it again.
That's a lot cleaner. Honestly I don't even know where my previous code of figuring out the column blocks and such.


Some shroom trees use just "body.connection_north" to indicate part of their cap, so you'll need to add that to "getPositions()". You also used z1,y1,x1 in the displace logic instead of the loop variables z,y,x.

You're probably going to need to remove roots, unless that's handled automatically somehow. "tree.roots" is similar to "tree.body", except it's upside-down. It's indexed from 0 to "tree.root_depth - 1". Presence of a root is indicated by "trunk" flag. Should be as simple as adding a second z loop after the first one. (Technically, you could get away with just "tree.roots[0]" in the current version of DF, but a loop is forward compatible for root_depth > 1.)

Changes:
Code: [Select]
...
local z1 = tree.pos.z
local z2 = tree.pos.z + tree.tree_info.body_height - 1
local z_root1 = tree.pos.z
local z_root2 = tree.pos.z + tree.tree_info.root_depth - 1
for x = x1,x2 do
for y = y1,y2 do
for z = z1,z2 do
pos = {x=x,y=y,z=z}
body = tree.tree_info.body[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if body.trunk or body.twigs or body.branches or body.connection_north then
positions[#positions+1] = pos
end
end
for z = z_root2,z_root1 do
pos = {x=x,y=y,z=z}
root = tree.tree_info.root[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if root.trunk then
positions[#positions+1] = pos
end
end
end
...
I also simplified the if statements for body checks.

All that's left is to account for "position.z < base.z" for the tiletypes. Assuming roots can't grow in stone (need to test this,) then all you have to do is convert those positions to soil walls (tiletype 261.)

For the base, the floor tiletype should chose randomly from {348,349,350,351} instead of just 350 for tile variations. (That was probably already on the to-do list.)
I think I originally wrote this because I was only interested in removing trees in such a way that I could build buildings where they were, so it isn't surprising to me that a bunch of what I did was so piecemeal (and that I ignored roots entirely). This all looks good, and I'm glad other people are looking into this stuff.


On a different note, I finally got through reviewing the rest of my code and running through all the tests. Everything seems to be good there, so I am starting back with the stuff I said I would take care of way back when. This first update is mainly going to be adding some extra hooks to the Enhanced Items system so that you can easily add attribute and skill adjustments on gear. Currently this is already doable with linking scripts to the {ON_EQUIP} and {ON_UNEQUIP} tags, but my plan was always to have a {ATTRIBUTE:X:Y} tag that acted as a shortcut. I will have to do some tests to make sure that the equips/unequips are being triggered correctly everytime, and if they get missed sometimes I might have to add a periodic check that will run through units and make sure that the attributes/skills have been adjusted correctly.

The other Enhanced Items trigger tags (e.g. {ON_ATTACK}, {ON_WOUND}, etc...) seem to be working correctly. And the non-trigger tags (e.g. {FIRE_RATE:X:Y}, etc...) are also working correctly (see this post for an example of rapid fire crossbow).


Lastly, I want to get back into finishing up the Encyclopedia and Detailed Unit Viewer, since they are basically feature complete, most of what needs to be done now is visual and performance improvements. It currently looks rather unpleasing so I'll have to experiment with different color and organizational schemes. The, performance is fine for an unmodded DF, but if you try to open the Encyclopedia when using something like MW-DF it takes a while because it loads everything in and the start. This can be fixed by only loading parts, and smart loading certain information at game start, but I will have to play around with the exact configurations since we don't want to have a huge memory footprint.

Eventually I think it would be awesome to do a sort of export of the Encyclopedia into html or something similar that could be viewed externally from playing, but that would be a long way down the road unless I got someone to help me with it.
Logged

Bumber

  • Bay Watcher
  • REMOVE KOBOLD
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #352 on: June 12, 2021, 03:01:56 am »

Discovered an edge case where part of a 2x2 trunk can be over air. Need to check for natural wall below first before putting soil floor down.

Interestingly, only trunk tiles themselves are considered indoors. They don't actually propagate it downwards like buildings or constructions do. Does this break myk's script?

Have to check for the case where the tree is separated from the surface by a thin floor and make sure it doesn't inherit the outside state. Player can also build between tree branches.

Need to do research on what happens if you turn a tree into a popsicle and chop it down. The original tiletype of a frozen tile is stored in a map block's block_events. Wouldn't surprise me if Toady himself didn't consider this bizarre case and floating branches appear when it thaws.

In keeping with a DF bug, running the script on shroom trees leaves floating down ramps. Going to need to handle those when removing an up ramp tiletype.
« Last Edit: June 12, 2021, 03:06:13 am by Bumber »
Logged
Reading his name would trigger it. Thinking of him would trigger it. No other circumstances would trigger it- it was strictly related to the concept of Bill Clinton entering the conscious mind.

THE xTROLL FUR SOCKx RUSE WAS A........... DISTACTION        the carp HAVE the wagon

A wizard has turned you into a wagon. This was inevitable (Y/y)?

Fleeting Frames

  • Bay Watcher
  • Spooky cart at distance
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #353 on: June 12, 2021, 03:08:21 pm »

I can confirm tree roots are created only in soil: When cavern trees have soil below them, they'll spawn roots.

Also
Quote
[DFHack]# :lua ~(7//2)
3
[DFHack]# :lua ~(7-7%2)
6

Meant (n-n%m)/m, I assume?

As for popsicle trees, quick test shows cast walls in area tree is cut down are converted into floors. As far as weird effects go, I see some ice floors didn't melt when they should have (the floating ones eventually collapse, leaving behind Unknown tile in at least one case, though generally just ice floors even with arrival of summer heat - albeit some do melt by then) , and some ice floors have ice floorx3 probe report, while others have ice floor/empty/empty.

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #354 on: June 12, 2021, 10:28:02 pm »

Interesting stuff. So it seems like there are probably several cases where a created script might differ from in-game behavior, both on the script side of things not treating edge cases and on the DF side of things not necessarily treating edge cases consistently. In my mind I would think we should be looking at creating a script that mostly matches the in-game behavior, but treats any edge cases (that Toady might not have even thought about or considered as possibilities) self consistently. The important part for a script, in mu opinion, is that any user of the script has an expectation of what happens when they use it, even if that expectation doesn't match in game behavior (although I can definitely see how someone would want any script to exactly replicate in-game behavior, so I think it is a personal preference thing).

All that being said, I am super happy people are looking into this stuff, like I said before, I only messed around with this so that I could delete trees in order to build buildings, in particular my multi-story buildings. But if we can get a proper tree removal/script tree chopping going I could definitely see some custom civs using a version of it instead of built-in tree chopping
Logged

FantasticDorf

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (10/13/2020)
« Reply #355 on: June 26, 2021, 10:33:24 am »

All that being said, I am super happy people are looking into this stuff, like I said before, I only messed around with this so that I could delete trees in order to build buildings, in particular my multi-story buildings. But if we can get a proper tree removal/script tree chopping going I could definitely see some custom civs using a version of it instead of built-in tree chopping

Not that that's particularly a problem when civilization ethics are concerned unless they dont have a equivlocal sharp axe tool, if citizens love trees usuallly because they are vanilla-elves, they often end up crying when they're cut down with a traumatic memory event.

Still handy to have around if a creative way to use the script is implemented, like construction/deconstruction of tree blocks set to templates (like a magma aquifer building directionality, generate tree height options of 1 2 3 blocks high with no leaves), but didn't want to risk cutting down your entire connected treehouse (depending on if treetiles of the same type auto-merge) to remove just a few specific blocks of it.

((i think what im trying to describe in this example is more of a natural timber-pillar spawned in with a modified /create-tree script than anything else))
« Last Edit: June 26, 2021, 10:38:57 am by FantasticDorf »
Logged

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (07/06/2021)
« Reply #356 on: July 06, 2021, 03:20:55 pm »

Update to the Enhanced Item System. Previously the {ATTRIBUTE and {SKILL shortcuts were not working correctly. This fix makes sure that they are. Note that the changes when equipping/unequipping is currently happening when the item enters/exits the units inventory. Meaning a unit that picks up an item to haul it will experience the change. In the next update I will limit the equipping/unequipping changes to actually wearing/using the item, and I will add a OnPickup/OnDrop functionality for those that want something to happen on pickup/drop.

I'm going to step away from the Enhanced Systems for the next couple of updates (except for minor updates like the one above) as I get the journal and compendium back up to snuff.
Logged

Roses

  • Bay Watcher
    • View Profile
Re: Roses' Script, System, and Utilities Collection (07/06/2021)
« Reply #357 on: September 16, 2021, 11:35:40 am »

Small update, finally got around to pushing the changes to Enhanced Items so that only equipping an item as a piece of armor or using an item as a weapon will trigger the OnEquip and OnUnequip flags. Later I will put trigger flags for things like OnStuck, OnBandage, etc...

The Journal and Detailed Unit Viewer GUIs are also almost finished updating, all the new functions have been written and they have been moved into my new GUI system (which should make it a lot easier for me to write other GUIs going forward). The only thing left is to add all the functionality that the old GUIs had, and to make sure that they work on heavily modded games (and don't take forever to load)
Logged
Pages: 1 ... 22 23 [24]