public override void FinishHarvesting(Mobile from, Item tool, HarvestDefinition def, object toHarvest, object locked)
        {
            from.EndAction(locked);

            if (!CheckHarvest(from, tool))
            {
                return;
            }

            int     tileID;
            Map     map;
            Point3D loc;

            if (!GetHarvestDetails(from, tool, toHarvest, out tileID, out map, out loc))
            {
                OnBadHarvestTarget(from, tool, toHarvest);
                return;
            }
            else if (!def.Validate(tileID))
            {
                OnBadHarvestTarget(from, tool, toHarvest);
                return;
            }

            if (!CheckRange(from, tool, def, map, loc, true))
            {
                return;
            }
            else if (!CheckHarvest(from, tool, def, toHarvest))
            {
                return;
            }

            double skillBase  = from.Skills[def.Skill].Base;
            double skillValue = from.Skills[def.Skill].Value;

            StaticTarget harvestTarget = toHarvest as StaticTarget;

            if (harvestTarget != null)
            {
                if (from.CheckSkill(def.Skill, 0, 120))
                {
                    if (tool is IUsesRemaining)
                    {
                        IUsesRemaining toolWithUses = (IUsesRemaining)tool;

                        toolWithUses.ShowUsesRemaining = true;

                        if (toolWithUses.UsesRemaining > 0)
                        {
                            --toolWithUses.UsesRemaining;
                        }
                        if (toolWithUses.UsesRemaining < 1)
                        {
                            tool.Delete();
                            def.SendMessageTo(from, def.ToolBrokeMessage);
                        }
                    }
                }

                BaseHarvestablePhase hTreePhase = BaseHarvestablePhase.LookupPhase(harvestTarget.ItemID);

                if (hTreePhase != null && hTreePhase is BaseTreeHarvestPhase)
                {
                    hTreePhase.Harvest(from, harvestTarget.ItemID, harvestTarget.Location, map);
                }
            }
        }
        public static void OnSave(WorldSaveEventArgs e)
        {
            if (!Directory.Exists(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation))
            {
                Directory.CreateDirectory(UltimaLiveSettings.LumberHarvestFallenTreeSaveLocation);
            }

            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

                    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();
                }
            }

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