I did, with a few changes for my shard, but I think I got it in the end. If Praxiiz doesn't mind, I'll post my modifications.

System works on tree hues - you don't need additional graphics to use this system, but then you need different tree hues for it to work.

When you chop down a tree the system checks the tree's hue and assigns resources matching that hue. You can define resources in 'HarvestableTrees.cs'. If you do not have additional graphics, every cut down tree will use SmallFallenTreeEastWest - define your resources for that fallen tree, when the tree is cut, depending on the hue of the tree, the log will have one resource matching the hue.
 
I did, with a few changes for my shard, but I think I got it in the end. If Praxiiz doesn't mind, I'll post my modifications.

System works on tree hues - you don't need additional graphics to use this system, but then you need different tree hues for it to work.

When you chop down a tree the system checks the tree's hue and assigns resources matching that hue. You can define resources in 'HarvestableTrees.cs'. If you do not have additional graphics, every cut down tree will use SmallFallenTreeEastWest - define your resources for that fallen tree, when the tree is cut, depending on the hue of the tree, the log will have one resource matching the hue.
I love this system but the old one you have on here don't work anymore for me can I have the new one that you are working on? Ty
 
I did, with a few changes for my shard, but I think I got it in the end. If Praxiiz doesn't mind, I'll post my modifications.

System works on tree hues - you don't need additional graphics to use this system, but then you need different tree hues for it to work.

When you chop down a tree the system checks the tree's hue and assigns resources matching that hue. You can define resources in 'HarvestableTrees.cs'. If you do not have additional graphics, every cut down tree will use SmallFallenTreeEastWest - define your resources for that fallen tree, when the tree is cut, depending on the hue of the tree, the log will have one resource matching the hue.

Just attach your files as a zip here and I'll update the resource with your additions.
 
Just attach your files as a zip here and I'll update the resource with your additions.
ok here is the file.. But when I installed it all its doing with the trees the leaves stay on the tree and the anim leaves fall and the old way of the tree stay and don't fall over.
I did have it working years a go when you first did this and I have custom trees for it the ones I artwork it I like to get this back working

TY
[doublepost=1491254742][/doublepost]
ok here is the file.. But when I installed it all its doing with the trees the leaves stay on the tree and the anim leaves fall and the old way of the tree stay and don't fall over.
I did have it working years a go when you first did this and I have custom trees for it the ones I artwork it I like to get this back working

TY
Ok here is a update of me getting this to work I did a fresh install of Runuo 2.3 and fresh install of uo 7.0.23.1 and the ultimalive and lumber harvesting and the ultimalive is working but the harvest don't work when I add this UltimaLiveLumberjacking.System; } }to the axe and polearms all is doing is hitting the tree one time and the leaves fall and the artwork stay all its nothing hitting it and no wood and the tree don't fall over So that is telling me I am not doing something here right

ty
 

Attachments

  • UltimaLive.rar
    620.6 KB · Views: 19
Just attach your files as a zip here and I'll update the resource with your additions.
Thanks. I'll post the code here instead of the files, though, since my modifications make tree hues redundant, not sure if that's what you intended.

I modified the ReapResources() method in \GraphicBasedHarvestSystems\BaseHarvestablePhase.cs to treat the easiest defined resource as fallbackResource and checking for a random other defined resource which is not the fallbackResource. I also added a chance to get resources based on skill and resource difficulty and checking requirement based on Skill.Value instead Skill.Base - any SkillMods will allow to chop better resources:
Code:
        public virtual Item ReapResource(int hue, Mobile from, int amount)
        {
            Item resourceItem = null;
            HarvestResource resource = null;

            if (amount > 0)
            {
                // changes to check for all defined resources not just matching hue when chopping

                /*
                if( PhaseResources.ContainsKey(hue))
                {
                    resource = PhaseResources[hue];
                }
                else if (PhaseResources.ContainsKey(0))
                {
                    resource = PhaseResources[0];
                }
                */

                // get resources
                HarvestResource fallbackResource = null;
                if( PhaseResources.Count == 1 )
                {
                    // seriously... apart from Linq, no way to get ONE value from a dictionary?
                    foreach( HarvestResource val in PhaseResources.Values )
                    {
                        fallbackResource = val;
                    }
                }
                else if( PhaseResources.Count > 1 )
                {
                    // sort through resources - fallbackResource is the easiest to chop
                    int lowestKey = -1;
                    double lowestReqSkill = -1;
                    HarvestResource lowestRes = null;
                    foreach( int key in PhaseResources.Keys )
                    {
                        if( PhaseResources[key].ReqSkill < lowestReqSkill || lowestReqSkill == -1 )
                        {
                            lowestKey = key;
                            lowestReqSkill = PhaseResources[key].ReqSkill;
                            lowestRes = PhaseResources[key];
                        }
                    }
                    fallbackResource = lowestRes;

                    // get a random resource other than fallbackResource
                    List<HarvestResource> mainResources = new List<HarvestResource>();
                    foreach( int key in PhaseResources.Keys )
                    {
                        if( key == lowestKey )
                            continue;
                        mainResources.Add(PhaseResources[key]);
                    }

                    if( mainResources.Count > 1 )
                        resource = mainResources[Utility.RandomMinMax(1, PhaseResources.Count - 1)];
                    else
                        resource = mainResources[0];
                }

                // check for main resource
                if( resource != null && from.Skills[HarvestSkill].Value >= resource.ReqSkill )
                {
                    // from CheckSkill(lumberjacking, resource.MinSkill, resource.MaxSkill);
                    double minSkill = resource.MinSkill;
                    double maxSkill = resource.MaxSkill;
                    double value = from.Skills[HarvestSkill].Value;
                    double chance;

                    if( value < minSkill || from.Skills.Cap == 0 )
                        chance = 0; // Too difficult
                    else if( value >= maxSkill )
                        chance = 1; // No challenge
                    else
                        chance = (value - minSkill) / (maxSkill - minSkill);

                    if( chance < Utility.RandomDouble() )
                        resource = null; // unlucky - reset resource
                }

                // check for fallback resource
                if( resource == null && fallbackResource != null && from.Skills[HarvestSkill].Value >= fallbackResource.ReqSkill )
                {
                    resource = fallbackResource;
                    // from CheckSkill(lumberjacking, resource.MinSkill, resource.MaxSkill);
                    double minSkill = resource.MinSkill;
                    double maxSkill = resource.MaxSkill;
                    double value = from.Skills.Lumberjacking.Value;
                    double chance;

                    if( value < minSkill || from.Skills.Cap == 0 )
                        chance = 0; // Too difficult
                    else if( value >= maxSkill )
                        chance = 1; // No challenge
                    else
                        chance = (value - minSkill) / (maxSkill - minSkill);

                    if( chance < Utility.RandomDouble() )
                        resource = null; // unlucky - reset resource
                }

                if (resource != null)
                {
                    double skillBase = from.Skills[HarvestSkill].Value;

                    if (skillBase >= resource.ReqSkill)
                    {
                        try
                        {
                            Type type = resource.Types[Utility.Random(resource.Types.Length)];
                            Item item = Activator.CreateInstance(type) as Item;
                            if (item != null)
                            {
                                item.Amount = amount;
                                resourceItem = item;
                            }
                        }
                        catch
                        {
                            Console.WriteLine("exception caught when trying to create bonus resource");
                        }
                    }
                    else
                    {
                        //TODO: Inform player they don't have enough skill using a cliloc to do it
                        from.SendMessage("You don't have enough skill to harvest that!");
                    }
                }
            }

            return resourceItem;
        }

In HarvestableTrees.cs if you define resources for a fallen tree, e.g. 'Walnut' as:
Code:
            PhaseResources.Add(0, new HarvestResource(00.0, 00.0, 100.0, 1072540, typeof(Log)));
            //PhaseResources.Add(350, new HarvestResource(65.0, 25.0, 105.0, 1072541, typeof(OakLog)));
            PhaseResources.Add(751, new HarvestResource(80.0, 40.0, 120.0, 1072542, typeof(AshLog)));
            //PhaseResources.Add(545, new HarvestResource(95.0, 55.0, 135.0, 1072543, typeof(YewLog)));
            PhaseResources.Add(436, new HarvestResource(100.0, 60.0, 140.0, 1072544, typeof(HeartwoodLog)));
            PhaseResources.Add(339, new HarvestResource(100.0, 60.0, 140.0, 1072545, typeof(BloodwoodLog)));
you have a random chance to get Ash, Heartwood and Bloodwood when chopping Walnut trees based on your lumberjacking. If you do not have enough skill to chop a random from Ash, Heartwood and Bloodwood or the chance check failed, you still have a chance to get normal logs since it's the fallback resource - one with the lowest required skill.

NOTES:
- this modification removes any impact of hues in the system!
- to mimic the original lumberjacking WITHOUT additional fallen trees graphics, define resources for class SmallFallenTreeEastWest (default fallen tree) as:
Code:
            PhaseResources.Add(0, new HarvestResource(00.0, 00.0, 100.0, 1072540, typeof(Log)));
            PhaseResources.Add(350, new HarvestResource(65.0, 25.0, 105.0, 1072541, typeof(OakLog)));
            PhaseResources.Add(751, new HarvestResource(80.0, 40.0, 120.0, 1072542, typeof(AshLog)));
            PhaseResources.Add(545, new HarvestResource(95.0, 55.0, 135.0, 1072543, typeof(YewLog)));
            PhaseResources.Add(436, new HarvestResource(100.0, 60.0, 140.0, 1072544, typeof(HeartwoodLog)));
            PhaseResources.Add(339, new HarvestResource(100.0, 60.0, 140.0, 1072545, typeof(BloodwoodLog)));
- to mimic the original lumberjacking WITH additional fallen trees graphics, define the above resources for each fallen tree

----------------------------

[doublepost=1491303359][/doublepost]
harvest don't work when I add this UltimaLiveLumberjacking.System; } }to the axe and polearms all is doing is hitting the tree one time and the leaves fall and the artwork stay all its nothing hitting it and no wood and the tree don't fall over So that is telling me I am not doing something here right
Check your \GraphicBasedHarvestSystems\LumberHarvest\HarvestableTrees.cs

Do you have custom graphics for fallen trees? If no, the first line of the file above should be:
Code:
//#define CUSTOM_TREE_GRAPHICS
Try this first and check first if the system works with basic graphics (i.e. with the '#define' commented in the first line), then experiment with custom graphics.

If you have custom graphics, make sure the graphics match each GraphicAsset for each fallen tree, e.g.
Code:
GraphicAsset asset1 = new GraphicAsset(0xCF5, -3, 0);
If asset's graphic does not match the hex number it will create wrong graphics or none at all when chopping down a tree.
 
Last edited:
Thanks. I'll post the code here instead of the files, though, since my modifications make tree hues redundant, not sure if that's what you intended.

I modified the ReapResources() method in \GraphicBasedHarvestSystems\BaseHarvestablePhase.cs to treat the easiest defined resource as fallbackResource and checking for a random other defined resource which is not the fallbackResource. I also added a chance to get resources based on skill and resource difficulty and checking requirement based on Skill.Value instead Skill.Base - any SkillMods will allow to chop better resources:
Code:
        public virtual Item ReapResource(int hue, Mobile from, int amount)
        {
            Item resourceItem = null;
            HarvestResource resource = null;

            if (amount > 0)
            {
                // changes to check for all defined resources not just matching hue when chopping

                /*
                if( PhaseResources.ContainsKey(hue))
                {
                    resource = PhaseResources[hue];
                }
                else if (PhaseResources.ContainsKey(0))
                {
                    resource = PhaseResources[0];
                }
                */

                // get resources
                HarvestResource fallbackResource = null;
                if( PhaseResources.Count == 1 )
                {
                    // seriously... apart from Linq, no way to get ONE value from a dictionary?
                    foreach( HarvestResource val in PhaseResources.Values )
                    {
                        fallbackResource = val;
                    }
                }
                else if( PhaseResources.Count > 1 )
                {
                    // sort through resources - fallbackResource is the easiest to chop
                    int lowestKey = -1;
                    double lowestReqSkill = -1;
                    HarvestResource lowestRes = null;
                    foreach( int key in PhaseResources.Keys )
                    {
                        if( PhaseResources[key].ReqSkill < lowestReqSkill || lowestReqSkill == -1 )
                        {
                            lowestKey = key;
                            lowestReqSkill = PhaseResources[key].ReqSkill;
                            lowestRes = PhaseResources[key];
                        }
                    }
                    fallbackResource = lowestRes;

                    // get a random resource other than fallbackResource
                    List<HarvestResource> mainResources = new List<HarvestResource>();
                    foreach( int key in PhaseResources.Keys )
                    {
                        if( key == lowestKey )
                            continue;
                        mainResources.Add(PhaseResources[key]);
                    }

                    if( mainResources.Count > 1 )
                        resource = mainResources[Utility.RandomMinMax(1, PhaseResources.Count - 1)];
                    else
                        resource = mainResources[0];
                }

                // check for main resource
                if( resource != null && from.Skills[HarvestSkill].Value >= resource.ReqSkill )
                {
                    // from CheckSkill(lumberjacking, resource.MinSkill, resource.MaxSkill);
                    double minSkill = resource.MinSkill;
                    double maxSkill = resource.MaxSkill;
                    double value = from.Skills[HarvestSkill].Value;
                    double chance;

                    if( value < minSkill || from.Skills.Cap == 0 )
                        chance = 0; // Too difficult
                    else if( value >= maxSkill )
                        chance = 1; // No challenge
                    else
                        chance = (value - minSkill) / (maxSkill - minSkill);

                    if( chance < Utility.RandomDouble() )
                        resource = null; // unlucky - reset resource
                }

                // check for fallback resource
                if( resource == null && fallbackResource != null && from.Skills[HarvestSkill].Value >= fallbackResource.ReqSkill )
                {
                    resource = fallbackResource;
                    // from CheckSkill(lumberjacking, resource.MinSkill, resource.MaxSkill);
                    double minSkill = resource.MinSkill;
                    double maxSkill = resource.MaxSkill;
                    double value = from.Skills.Lumberjacking.Value;
                    double chance;

                    if( value < minSkill || from.Skills.Cap == 0 )
                        chance = 0; // Too difficult
                    else if( value >= maxSkill )
                        chance = 1; // No challenge
                    else
                        chance = (value - minSkill) / (maxSkill - minSkill);

                    if( chance < Utility.RandomDouble() )
                        resource = null; // unlucky - reset resource
                }

                if (resource != null)
                {
                    double skillBase = from.Skills[HarvestSkill].Value;

                    if (skillBase >= resource.ReqSkill)
                    {
                        try
                        {
                            Type type = resource.Types[Utility.Random(resource.Types.Length)];
                            Item item = Activator.CreateInstance(type) as Item;
                            if (item != null)
                            {
                                item.Amount = amount;
                                resourceItem = item;
                            }
                        }
                        catch
                        {
                            Console.WriteLine("exception caught when trying to create bonus resource");
                        }
                    }
                    else
                    {
                        //TODO: Inform player they don't have enough skill using a cliloc to do it
                        from.SendMessage("You don't have enough skill to harvest that!");
                    }
                }
            }

            return resourceItem;
        }

In HarvestableTrees.cs if you define resources for a fallen tree, e.g. 'Walnut' as:
Code:
            PhaseResources.Add(0, new HarvestResource(00.0, 00.0, 100.0, 1072540, typeof(Log)));
            //PhaseResources.Add(350, new HarvestResource(65.0, 25.0, 105.0, 1072541, typeof(OakLog)));
            PhaseResources.Add(751, new HarvestResource(80.0, 40.0, 120.0, 1072542, typeof(AshLog)));
            //PhaseResources.Add(545, new HarvestResource(95.0, 55.0, 135.0, 1072543, typeof(YewLog)));
            PhaseResources.Add(436, new HarvestResource(100.0, 60.0, 140.0, 1072544, typeof(HeartwoodLog)));
            PhaseResources.Add(339, new HarvestResource(100.0, 60.0, 140.0, 1072545, typeof(BloodwoodLog)));
you have a random chance to get Ash, Heartwood and Bloodwood when chopping Walnut trees based on your lumberjacking. If you do not have enough skill to chop a random from Ash, Heartwood and Bloodwood or the chance check failed, you still have a chance to get normal logs since it's the fallback resource - one with the lowest required skill.

NOTES:
- this modification removes any impact of hues in the system!
- to mimic the original lumberjacking WITHOUT additional fallen trees graphics, define resources for class SmallFallenTreeEastWest (default fallen tree) as:
Code:
            PhaseResources.Add(0, new HarvestResource(00.0, 00.0, 100.0, 1072540, typeof(Log)));
            PhaseResources.Add(350, new HarvestResource(65.0, 25.0, 105.0, 1072541, typeof(OakLog)));
            PhaseResources.Add(751, new HarvestResource(80.0, 40.0, 120.0, 1072542, typeof(AshLog)));
            PhaseResources.Add(545, new HarvestResource(95.0, 55.0, 135.0, 1072543, typeof(YewLog)));
            PhaseResources.Add(436, new HarvestResource(100.0, 60.0, 140.0, 1072544, typeof(HeartwoodLog)));
            PhaseResources.Add(339, new HarvestResource(100.0, 60.0, 140.0, 1072545, typeof(BloodwoodLog)));
- to mimic the original lumberjacking WITH additional fallen trees graphics, define the above resources for each fallen tree

----------------------------

[doublepost=1491303359][/doublepost]Check your \GraphicBasedHarvestSystems\LumberHarvest\HarvestableTrees.cs

Do you have custom graphics for fallen trees? If no, the first line of the file above should be:
Code:
//#define CUSTOM_TREE_GRAPHICS
Try this first and check first if the system works with basic graphics (i.e. with the '#define' commented in the first line), then experiment with custom graphics.

If you have custom graphics, make sure the graphics match each GraphicAsset for each fallen tree, e.g.
Code:
GraphicAsset asset1 = new GraphicAsset(0xCF5, -3, 0);
If asset's graphic does not match the hex number it will create wrong graphics or none at all when chopping down a tree.
Hello timginter
Yes I do have this //#define CUSTOM_TREE_GRAPHICS on and then I did this #define CUSTOM_TREE_GRAPHICS and its still doing the same thing so I don't know what going on with it

ty
 
Hello timginter
Yes I do have this //#define CUSTOM_TREE_GRAPHICS on and then I did this #define CUSTOM_TREE_GRAPHICS and its still doing the same thing so I don't know what going on with it
Leave it commented, don't change things and 'hope it works' ;) If you leave it as '//#define CUSTOM_TREE_GRAPHICS' (commented) the system should cut down every tree and create a fallen log as per 'SmallFallenTreeEastWest'. It's difficult to troubleshoot - it worked straight away for me (ServUO Publish 54, client 7.0.50.0), it just took me a bit of reading the code to get the hang of the system.
 
Leave it commented, don't change things and 'hope it works' ;) If you leave it as '//#define CUSTOM_TREE_GRAPHICS' (commented) the system should cut down every tree and create a fallen log as per 'SmallFallenTreeEastWest'. It's difficult to troubleshoot - it worked straight away for me (ServUO Publish 54, client 7.0.50.0), it just took me a bit of reading the code to get the hang of the system.
Hi
I did what you say '//#define CUSTOM_TREE_GRAPHICS' and I delete my server of runuo 2.3 and install runuo 2.5 and reinstalled your system and still its the same.
I am old school and I don't like how the codes look like in servUO that is why I like Runuo codes But you did one system for runuo befor and it worked good so anyway I have now Runuo 2.5 and UO 7.0.57.20
If you cant get that one working for that UO Version is there away you can give me client 7.0.50.0 of yours that work?
I really one your system to work so my artwork can be back in
TY for reading this
 
Last edited:
Hi
I did what you say '//#define CUSTOM_TREE_GRAPHICS' and I delete my server of runuo 2.3 and install runuo 2.5 and reinstalled your system and still its the same.
I am old school and I don't like how the codes look like in servUO that is why I like Runuo codes But you did one system for runuo befor and it worked good so anyway I have now Runuo 2.5 and UO 7.0.57.20
If you cant get that one working for that UO Version is there away you can give me client 7.0.50.0 of yours that work?
I really one your system to work so my artwork can be back in
TY for reading this
It doesn't sound like it's a client issue, sounds like your lumber can't delete original tree statics and create new, fallen ones.
 
If you dont have Ultima Live working, this system won't work. Its an addon system. Thats your problem.
 
Check out his post for a few UltimaLive tips: https://www.servuo.com/threads/server-crashing-going-to-custom-map.3509/page-3#post-32033

Not entirely sure about it, but I heard that RunUO/ServUO must be started with Administrator privileges if starting for the first time on Windows for UltimaLive to work properly.

Client MUST be started with Administrator privileges at least for the first time (so it can create the folder where streamed maps are). Still, the client will crash sometimes for me when 'Entering Britannia'. Setting compatibility to Win XP SP3 or SP2 seemed to have helped a bit, but I still get an occasional crash/not responding.

Copy your 'Saves' folder to, e.g. your Desktop. Go to the folder you have RunUO/ServUO in, go to 'Saves' and remove 'Mobiles' and 'Items' folders. Check if it made any difference if you're logging in on an empty shard.

By default characters are created on Trammel - make sure you have UltimaLive set to handle Trammel.

Once you get UltimaLive to work this Lumber system should be just plug-and-play.
 
Check out his post for a few UltimaLive tips: https://www.servuo.com/threads/server-crashing-going-to-custom-map.3509/page-3#post-32033

Not entirely sure about it, but I heard that RunUO/ServUO must be started with Administrator privileges if starting for the first time on Windows for UltimaLive to work properly.

Client MUST be started with Administrator privileges at least for the first time (so it can create the folder where streamed maps are). Still, the client will crash sometimes for me when 'Entering Britannia'. Setting compatibility to Win XP SP3 or SP2 seemed to have helped a bit, but I still get an occasional crash/not responding.

Copy your 'Saves' folder to, e.g. your Desktop. Go to the folder you have RunUO/ServUO in, go to 'Saves' and remove 'Mobiles' and 'Items' folders. Check if it made any difference if you're logging in on an empty shard.

By default characters are created on Trammel - make sure you have UltimaLive set to handle Trammel.

Once you get UltimaLive to work this Lumber system should be just plug-and-play.
Ok there is something I am not doing right here I delete my uo and server and I started new and then I put in Uitimalive in and followed how to install it and then I try to login and its doing the same thing so maybe I am not doing it right or something but is there away I can have step by step ?
[doublepost=1491598059][/doublepost]
Ok there is something I am not doing right here I delete my uo and server and I started new and then I put in Uitimalive in and followed how to install it and then I try to login and its doing the same thing so maybe I am not doing it right or something but is there away I can have step by step ?

Ultima Online Client has stopped working
A problem caused the program to stop working correctly.
Windows will close the program and notify you if a solution is available.
 
I found an odd bug - when the server crashes, all trees that were cut down at the time of the crash will never grow back. I'll have to look into the code for more details when I have some time, though.


I am not doing it right or something but is there away I can have step by step ?
The lumber system itself works, it's your UltimaLive that's missing.
Ask in UltimaLive thread, I'm sure we'll be able to find out what's wrong there.
 
I found an odd bug - when the server crashes, all trees that were cut down at the time of the crash will never grow back. I'll have to look into the code for more details when I have some time, though.


The lumber system itself works, it's your UltimaLive that's missing.
Ask in UltimaLive thread, I'm sure we'll be able to find out what's wrong there.
Thank you and yes I did ask there too and no one help me out but you but its not working for me to I am not going to use it...
I am building my own tree's system now but Thank you tho
 
I've just noticed a bug in my previous code - it won't give you resources until you have enough skill to be able to chop both main and the fallback resource. I had to make more modifications to the whole system, though, I'll post the 2 files I changed here if you don't mind, Praxiiz.

BaseAxe.cs seem to not have IUsesRemaining interface in original ServUO files - you have to add that interface for axes to lose 'Uses' on chopping, BaseAxe declaration should look like this:
Code:
public abstract class BaseAxe : BaseMeleeWeapon, IUsesRemaining
(reported as bug)

\Scripts\Services\Harvest\HarvestTarget.cs - around line 60 you have to change all this.m_System is Lumberjacking to this.m_System is UltimaLiveLumberjacking, otherwise you will not be able to carve anything or destroy furniture with axes.

\Scripts\Custom\GraphicBasedHarvestSystems\LumberHarvest\LumberHarvest.cs - file attached below - now deleting tree phases and giving tree resources dependant on a successful Lumberjacking SkillCheck. Added FailMessage on unsuccessful chop (unsuccessful SkillCheck).

\Scripts\Custom\GraphicBasedHarvestSystems\BaseHarvestablePhase.cs - file attached below - added a check for skillReq for main and fallback resource earlier in the code. Changed the way main and fallback resource are given - main as a chance, fallback as a guaranteed resource since the character passed a SkillCheck() earlier. If player doesn't have enough skill for either main and fallback resources he'll get a message telling him he will not yet be able to chop any useful wood out of that type of trees.


------------------------------------------------

Thank you and yes I did ask there too and no one help me out but you but its not working for me to I am not going to use it...
I am building my own tree's system now but Thank you tho
It's difficult to troubleshoot if you do not debug on your own. It's clear UltimaLive is the not working one, hard to give more clues as to what to do. Hope you'll get UltimaLive working in the end.
 

Attachments

  • LumberHarvest.cs
    13.8 KB · Views: 15
  • BaseHarvestablePhase.cs
    26.6 KB · Views: 15
I added a small mod to the script, hope you don't mind me posting here, Praxiiz

I modified the Configure(), OnSave() and OnLoad() methods to regrow trees on server restart and added a callback function for regrowing trees not only on saves (callback values set to 5 minutes).
Replace the original Configure(), OnLoad() and OnSave() in LumberHarvest.cs with the code below:
Code:
        public static void Configure()
        {
            foreach (Map m in Map.AllMaps)
            {
                RegrowthMasterLookupTable.Add(m.MapID, new Dictionary<Point3D, int>());
            }

            EventSink.WorldSave += new WorldSaveEventHandler(OnSave);
            EventSink.WorldLoad += new WorldLoadEventHandler(OnLoad);

            // add regrowth callback
            Timer regrowthTimer = Timer.DelayCall(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5), new TimerCallback(Regrowth_Callback));
        }

        public static void OnLoad()
        {
            for (int mapId = 0; mapId < RegrowthMasterLookupTable.Count; ++mapId)
            {
                string filename = Path.Combine(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation, "TreeLocations." + mapId);
                FileInfo treeFileInfo = new FileInfo(filename);

                if (treeFileInfo.Exists)
                {
                    using (FileStream fs = new FileStream(filename, FileMode.Open))
                    {
                        using (BinaryReader br = new BinaryReader(fs))
                        {
                            GenericReader reader = new BinaryFileReader(br);

                            while (fs.Position < fs.Length)
                            {
                                Point3D p = reader.ReadPoint3D();
                                int itemId = reader.ReadInt();
                                if (!RegrowthMasterLookupTable[mapId].ContainsKey(p))
                                {
                                    RegrowthMasterLookupTable[mapId].Add(p, itemId);
                                }
                            }
                        }
                    }
                }
            }

            // restore trees
            RegrowTrees();
        }

        // a primitive function to restore trees - just calls Regrowth_Callback() 3 times to reuse the code
        public static void RegrowTrees()
        {
            for( int i = 0; i < 3; i++ )
            {
                LastGrowth = DateTime.Now - TimeBetweenRegrowth - TimeBetweenRegrowth;
                Regrowth_Callback();
            }
        }

        // modify OnSave, move regrowth code outside of OnSave, use as callback to grow trees not only on world save
        public static void Regrowth_Callback()
        {
            bool updateRegrowthTime = false;

            //foreach map in the lookup table
            foreach( Map m in Map.AllMaps )
            {
                if( RegrowthMasterLookupTable.ContainsKey(m.MapID) )
                {
                    #region Regrowth
                    if( DateTime.Now > LastGrowth + TimeBetweenRegrowth )
                    {
                        updateRegrowthTime = true;

                        Dictionary<Point3D, int> mapLookupTable = RegrowthMasterLookupTable[m.MapID];
                        MapOperationSeries mapOperations = null;

                        List<Point3D> locationsToRemove = new List<Point3D>();

                        //for each tree location in the lookup table
                        foreach( KeyValuePair<Point3D, int> treeLocationKvp in mapLookupTable )
                        {
                            if( BaseHarvestablePhase.MasterHarvestablePhaseLookupByItemIdList.ContainsKey(treeLocationKvp.Value) )
                            {
                                //look up the current phase
                                bool existingTileFound = false;
                                foreach( StaticTile tile in m.Tiles.GetStaticTiles(treeLocationKvp.Key.X, treeLocationKvp.Key.Y) )
                                {
                                    if( tile.Z == treeLocationKvp.Key.Z ) // if the z altitude matches
                                    {
                                        if( BaseHarvestablePhase.MasterHarvestablePhaseLookupByItemIdList.ContainsKey(tile.ID) ) //if the item id is linked to a phase
                                        {

                                            BaseHarvestablePhase currentPhase = BaseHarvestablePhase.MasterHarvestablePhaseLookupByItemIdList[tile.ID];

                                            foreach( GraphicAsset[] assetSet in currentPhase.BaseAssetSets )
                                            {
                                                if( assetSet.Length > 0 && assetSet[0].ItemID == tile.ID )
                                                {
                                                    existingTileFound = true;
                                                }
                                            }

                                            if( existingTileFound && !currentPhase.grow(treeLocationKvp.Key, m, ref mapOperations) )
                                            {
                                                locationsToRemove.Add(treeLocationKvp.Key);
                                            }

                                            break;
                                        }
                                    }
                                }

                                //nothing to grow at this location, so construct the starting phase
                                if( !existingTileFound )
                                {
                                    //lookup original phase that was saved
                                    BaseHarvestablePhase grownPhase = BaseHarvestablePhase.MasterHarvestablePhaseLookupByItemIdList[treeLocationKvp.Value];

                                    //cleanup final harvest graphics
                                    if( grownPhase.FinalHarvestPhase != null && BaseHarvestablePhase.MasterHarvestablePhaseLookupByTypeList.ContainsKey(grownPhase.FinalHarvestPhase) )
                                    {
                                        BaseHarvestablePhase.MasterHarvestablePhaseLookupByTypeList[grownPhase.FinalHarvestPhase].Teardown(treeLocationKvp.Key, m, ref mapOperations);
                                    }

                                    if( grownPhase.StartingGrowthPhase != null && BaseHarvestablePhase.MasterHarvestablePhaseLookupByTypeList.ContainsKey(grownPhase.StartingGrowthPhase) )
                                    {
                                        //remove stump
                                        BaseTreeHarvestPhase maturePhase = grownPhase as BaseTreeHarvestPhase;
                                        if( maturePhase != null )
                                        {
                                            if( mapOperations != null )
                                            {
                                                mapOperations.Add(new DeleteStatic(m.MapID, new StaticTarget(treeLocationKvp.Key, maturePhase.StumpGraphic)));
                                            }
                                            else
                                            {
                                                mapOperations = new MapOperationSeries(new DeleteStatic(m.MapID, new StaticTarget(treeLocationKvp.Key, maturePhase.StumpGraphic)), m.MapID);
                                            }
                                        }

                                        BaseHarvestablePhase.MasterHarvestablePhaseLookupByTypeList[grownPhase.StartingGrowthPhase].Construct(treeLocationKvp.Key, m, ref mapOperations);
                                    }
                                }
                            }
                        }

                        if( mapOperations != null )
                        {
                            mapOperations.DoOperation();
                        }

                        foreach( Point3D p in locationsToRemove )
                        {
                            RegrowthMasterLookupTable[m.MapID].Remove(p);
                        }
                    }
                    #endregion
                }
            }

            if( updateRegrowthTime )
            {
                LastGrowth = DateTime.Now;
            }
        }

        public static void OnSave(WorldSaveEventArgs e)
        {
            if (!Directory.Exists(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation))
            {
                Directory.CreateDirectory(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation);
            }

            //foreach map in the lookup table
            foreach (Map m in Map.AllMaps)
            {
                if (RegrowthMasterLookupTable.ContainsKey(m.MapID))
                {
                    GenericWriter writer = new BinaryFileWriter(Path.Combine(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation, "TreeLocations." + m.MapID), true);

                    foreach (KeyValuePair<Point3D, int> kvp in RegrowthMasterLookupTable[m.MapID])
                    {
                        writer.Write(kvp.Key);
                        writer.Write(kvp.Value);
                    }
                    writer.Close();
                }
            }
        }
 
Its possible to put like 2880 minutes for regrow the trees? or its a lot of time?
 
Last edited:
I am getting this error with this package:

+ _Customs/GraphicBasedHarvestSystems/BaseHarvestablePhase.cs:
CS0246: Line 279: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 329: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 336: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 386: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 499: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
+ _Customs/GraphicBasedHarvestSystems/LumberHarvest/BaseTreeHarvestPhase.cs:
CS0246: Line 61: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 93: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
CS0246: Line 132: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
+ _Customs/GraphicBasedHarvestSystems/LumberHarvest/HarvestableTrees.cs:
CS0246: Line 86: The type or namespace name `MapOperationSeries' could not be found. Are you missing an assembly reference?
 
I'm not sure I understand how to use the custom graphic. Do we have to make our own custom art and add it somewhere? I was going to message @Hank but his profile is private or something and I cannot. I've got UltimaLive and the harvesting system all up and working, but when using the custom graphic by commenting out the appropriate line, I end up with unused tiles. Was a graphic provided somewhere that I missed?
 
I got this system to work, but all of my trees regrow at the same time. My regrowth timer is set to 5 minutes. Say I chop a tree and 3 minutes later I chop another tree. The first tree that was chopped should respawn in 2 minutes and the second one in 5 minutes.
However, in that situation they both respawn in 2 minutes. It's like the regrowth counts since the first tree that was chopped. Don't they have individual timers?
 
I got this system to work, but all of my trees regrow at the same time. My regrowth timer is set to 5 minutes. Say I chop a tree and 3 minutes later I chop another tree. The first tree that was chopped should respawn in 2 minutes and the second one in 5 minutes.
However, in that situation they both respawn in 2 minutes. It's like the regrowth counts since the first tree that was chopped. Don't they have individual timers?

Having a timer for each tree would cause a issue if you had a lot of players chopping trees, if you want to solve this, I would think that you would need to have a enum on the tree that has 5 states, which represent each regrowth state and have the single timer just move up the enum to the next state, this would allow each tree to be at different states and have only one timer controlling it all. I would use a timer at one minute intervals, which after 5 intervals is the 5 min total time for regrowth from time it is chopped!

Just my 2 cents, there could be better ways to solve this!
 
I only get regular logs nothing else any ideas???
that is because the other logs, if I remember correctly, aren't defined in the system. However if I trouble shoot this further, Ash, Oak, Heartwood etc. are all defined randomly as to what tree yields what logs. Once found though, that tree will always produce those logs. The question remains: if said tree falls and it yielded heartwood while standing, will its fallen counterpart yield the same type of log?

My assumption is no. The reason is because the fallen tree is a different itemID and therefore a different object in the game.
 
I've just noticed a bug in my previous code - it won't give you resources until you have enough skill to be able to chop both main and the fallback resource. I had to make more modifications to the whole system, though, I'll post the 2 files I changed here if you don't mind, Praxiiz.

BaseAxe.cs seem to not have IUsesRemaining interface in original ServUO files - you have to add that interface for axes to lose 'Uses' on chopping, BaseAxe declaration should look like this:
Code:
public abstract class BaseAxe : BaseMeleeWeapon, IUsesRemaining
(reported as bug)

\Scripts\Services\Harvest\HarvestTarget.cs - around line 60 you have to change all this.m_System is Lumberjacking to this.m_System is UltimaLiveLumberjacking, otherwise you will not be able to carve anything or destroy furniture with axes.

\Scripts\Custom\GraphicBasedHarvestSystems\LumberHarvest\LumberHarvest.cs - file attached below - now deleting tree phases and giving tree resources dependant on a successful Lumberjacking SkillCheck. Added FailMessage on unsuccessful chop (unsuccessful SkillCheck).

\Scripts\Custom\GraphicBasedHarvestSystems\BaseHarvestablePhase.cs - file attached below - added a check for skillReq for main and fallback resource earlier in the code. Changed the way main and fallback resource are given - main as a chance, fallback as a guaranteed resource since the character passed a SkillCheck() earlier. If player doesn't have enough skill for either main and fallback resources he'll get a message telling him he will not yet be able to chop any useful wood out of that type of trees.


------------------------------------------------

It's difficult to troubleshoot if you do not debug on your own. It's clear UltimaLive is the not working one, hard to give more clues as to what to do. Hope you'll get UltimaLive working in the end.

@Cody messaged me with an "Index out of bounds" exception he was getting. I was less experienced with C# and programming when I wrote the changes 4 years ago, it could have been much better... anyway, I have no idea why I wrote it that way. There's an error in my changes, in BaseHarvestablePhase.cs line 453 - I'm building a list of main resources from PhaseResources a few lines above that, but when getting a random main resource I'm using:
C#:
resource = mainResources[Utility.RandomMinMax(1, PhaseResources.Count - 1)];
this is not correct - replace it with:
C#:
resource = mainResources[Utility.RandomMinMax(0, mainResources.Count - 1)];

The changes I made to this system are in post-45087 - read this thread from that post to get my changes and corrections (corrections in post-45448, post-47873 and this post - the change to RandomMinMax() above)
 
Back