• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Custom Lumber Harvesting

Status
Not open for further replies.
I'm trying to make a sort of fancy way of harvesting lumber - the worker goes up to the tree, starts channeling, and after X seconds, the tree turns into a treant. The treant then waddles to the nearest town hall and deposits itself as lumber, dying in the process.

Few problems:
I thought about using something like Eat Tree to make the worker target the tree easier and have a trigger run whenever the spell is cast (giving a cast time equivalent to the time it would generally take to harvest a whole tree), but that's kind of... inellegant.

An alternative would be to give the worker a modified lumber harvest that peasants/orcs/ghouls have that deals 0 damage to the tree. Each time they hit the tree, an integer goes +1 and when reaching 20 or something, gets reset and the tree dies and a treant spawns in it's place. Use something like GetClosestUnit to pick nearest town hall and ask treant to go sacrifice itself. The problem with this solution is I can't find a way to detect when the tree in question is being struck to count up the integer.

Any alternate solution would be welcome.

PS: Didn't they have something like that in Creep Revolution?
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
You could try use the Wisp harvest ability and see if you can catch when it enters the tree as an event.

If that doesn't work, you could have a looping trigger that periodically checks all your workers to see if they are busy harvesting a tree, if they are you can order them to cast a hidden channel ability that will convert the tree into a unit.

The best might just be to trigger the entire thing yourself: automatic tree finding, harvesting, returning, finding a new target, etc.
 
Okay, so I found a way to work around this using Eat Tree and GetClosestWidget. I do have a little issue with a move order, however. When the treant reaches town hall it's supposed to cast a spell that will trigger it's death a give the lumber. The way this is supposed to look in game is that the town hall in question is a bonfire, and I want the treant to walk into the flames.

When the unit begins casting, add locust, move the treant to it's own location to make it stop casting, add expiration timer based on how long the distance is, and order it to move to the location of the town hall. Everything works fine, except for the movement. I tried using Starts Casting or even asking the treant to stop instead of moving it, and using move's orderID to make it move, but nothing's working so far. It just stays there until it's timer runs out and dies.

Any thoughts?
 
Level 13
Joined
May 11, 2008
Messages
1,198
Make a custom Goblin Shredder, edit whatever you need to, including attack animation. Make him attack very slowly and as he attacks a tree for normal harvesting you can make it so he one-hit kills the tree. This might mean adjusting health of trees or adjusting the damage the worker or the harvest ability does. FYI the default health for trees is 100, but you can change that by tree type.

I feel like a crazy person going into all this, but your idea seems odd, also. I would go on but I fear I'm in too deep already.
If it helps any, trees are considered destructibles in case you didn't already know.
 
I have no way to catch when a Goblin Shredder (or any harvester) hits a tree, or catch the destroyer of a destructible. So going the creep rebillion route of turning trees into ents when they die through attacks is kinda out of my hands (though I'd love to know how they did it). That's why I use a spell. The only problem is preventing two or more of the same harvesters from picking the same tree.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
The only problem is preventing two or more of the same harvesters from picking the same tree.
You add trees being harvested to a hashtable and ignore trees in the hashtable during the search. Once the tree is removed or harvest fails you remove it from the hashtable to prevent leaks. This prevents the same tree being returned multiple times by different searches.
 
You add trees being harvested to a hashtable and ignore trees in the hashtable during the search. Once the tree is removed or harvest fails you remove it from the hashtable to prevent leaks. This prevents the same tree being returned multiple times by different searches.

Hmm, I actually tried that once, but I couldn't find a way to identify whether a tree was stored inside a hashtable or not, kinda like you check if a unit is in a unit group. I mean it would be possible if I could somehow find what specific value I assigned to a destructible but I'm sure how to go about doing that either. Also should the key to that hashtable be some dummy unit that's always present somewhere on the map? Can the key be something else than a unit? Maybe if I can attach an integer to the tree I can use that to identify its value in the destructible hashtable. I've only ever used hashtables in a limited capacity.

Makes me wonder, is there's some way to index destructibles? I use a method to index floating texts that uses loops to recycle unused ones but I'm not quite sure if that would work the same way, not that I've tried it.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
Hmm, I actually tried that once, but I couldn't find a way to identify whether a tree was stored inside a hashtable or not, kinda like you check if a unit is in a unit group. I mean it would be possible if I could somehow find what specific value I assigned to a destructible but I'm sure how to go about doing that either. Also should the key to that hashtable be some dummy unit that's always present somewhere on the map? Can the key be something else than a unit? Maybe if I can attach an integer to the tree I can use that to identify its value in the destructible hashtable. I've only ever used hashtables in a limited capacity.
You are over thinking...

Parent key -> HandleId of destructible tree.
Child key -> some unique constant integer for this purpose.
Value -> boolean true (or any value works).

Test if tree is inside hashtable by looking up with parent being the HandleId of the tree, the Child being the constant integer defined earlier and function being if the entry exists or not (since the value we do not care about, only that it exists).

The native to use to convert a tree into a unique integer is...
JASS:
native GetHandleId takes handle h returns integer
This works on not just trees, but also units, effects, floating text, groups, forces, locations, and everything labelled "Handle". No two different objects will have the same handle so you will never have a unit and a group with the same HandleId at the same time.

This does exist in GUI as an integer function. Do note that it can bug in JNGP since it uses a version of WE before that GUI feature was added. Some newer JNGP like editors should have it fixed. As such I would recommend using JASS for it, even if you do something like...
JASS:
set udg_reg_int1 = GetHandleId(udg_reg_des1)
Where reg_des1 is a destructible global variable and the result is put in an integer global variable reg_int1. You run that line before the hashtable actions/tests. Obviously inlining is faster but this allows you to keep using GUI when the inbuilt GUI support for the function is missing.

Do remember to explicitly remove the entry from the hashtable. This should be done when the tree is removed/killed or when the conversion times out (caster dies, or is interrupted).
 
Oh my, how do I even code that xD I've never use natives before, let alone know what they are. I'm using a function to filter out dead trees and I use GetClosestWidget with that:
set Tree = GetClosestDestructable(GetLocationX(udg_TempPoint), GetLocationY(udg_TempPoint), Filter(function FilterDest))

and the filter in question is:
JASS:
function FilterDest takes nothing returns boolean
    return GetDestructableLife(GetFilterDestructable()) > 0
endfunction
If I understand what you're telling me, once I get the HandleId of the destructible and store that in a hashtable - then I need to extend the filter to exclude the destructible if it's inside said hashtable. I guess the question is what's the code for that :\
 
Level 13
Joined
May 11, 2008
Messages
1,198
Ah, so far what I'm working on is a modifiable value for trees through the triggers, giving lumber directly while the worker eats trees with the ability that Night Elf Ancients get by default. I found it by using the HWS search engine. I got the values working right but I can't get dead trees to not interfere so I'm completely removing the trees when they are eaten by the custom workers. This is unfortunate because I'm reformatting the tech trees and there will be exactly 3 of them and the first worker from one of those will be this one (the blue tech tree) and I want the workers from the other 2 tech trees (red and purple) to be leaving behind stumps like normal workers. I'm tempted to not bother fixing the problem but non-dead destructables that are not trees can and eventually will also interfere.
 
There's a pretty easy fix to make your workers ignore dead destructibles. It's the boolexpr thing - make it call a function that filters them out.

Paste this in your map's script:
JASS:
function FilterDest takes nothing returns boolean
    return GetDestructableLife(GetFilterDestructable()) > 0
endfunction

then in your GetClosestWidget call to pick a tree for harvest/eating, use that function Filter(function FilterDest)) at the end. Eg:

set Tree = GetClosestDestructable(GetLocationX(udg_TempPoint), GetLocationY(udg_TempPoint), Filter(function FilterDest))

Your workers will now ignore dead trees. Also perhaps make sure they're picking trees instead of other types of destructible if you have those on your map. I haven't used it myself, but I imagine you could use IsDestructableTree for that.
 
Level 13
Joined
May 11, 2008
Messages
1,198
That did the trick, thanks. I'm using PitzerMike's version instead since I've already read that one. It's working perfectly with other tree killers, expect formal thanks for the tip!
JASS:
function FilterDestForEating takes nothing returns boolean
if GetDestructableLife(GetFilterDestructable()) > 0 then
if  IsDestructableTree(GetFilterDestructable()) then
return true
else;return false
endif
else;return false
endif
return false
endfunction
 
Status
Not open for further replies.
Top