Files
opensim/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs
Robert Adams beeec1c467 varregion: elimination of Constants.RegionSize from all over OpenSimulator.
Routines in Util to compute region world coordinates from region coordinates
as well as the conversion to and from region handles. These routines have
replaced a lot of math scattered throughout the simulator.
Should be no functional changes.
2013-11-08 20:53:37 -08:00

784 lines
30 KiB
C#

/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Timers;
using OpenMetaverse;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace OpenSim.Region.OptionalModules.World.TreePopulator
{
/// <summary>
/// Version 2.02 - Still hacky
/// </summary>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "TreePopulatorModule")]
public class TreePopulatorModule : INonSharedRegionModule, ICommandableModule, IVegetationModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private readonly Commander m_commander = new Commander("tree");
private Scene m_scene;
[XmlRootAttribute(ElementName = "Copse", IsNullable = false)]
public class Copse
{
public string m_name;
public Boolean m_frozen;
public Tree m_tree_type;
public int m_tree_quantity;
public float m_treeline_low;
public float m_treeline_high;
public Vector3 m_seed_point;
public double m_range;
public Vector3 m_initial_scale;
public Vector3 m_maximum_scale;
public Vector3 m_rate;
[XmlIgnore]
public Boolean m_planted;
[XmlIgnore]
public List<UUID> m_trees;
public Copse()
{
}
public Copse(string fileName, Boolean planted)
{
Copse cp = (Copse)DeserializeObject(fileName);
this.m_name = cp.m_name;
this.m_frozen = cp.m_frozen;
this.m_tree_quantity = cp.m_tree_quantity;
this.m_treeline_high = cp.m_treeline_high;
this.m_treeline_low = cp.m_treeline_low;
this.m_range = cp.m_range;
this.m_tree_type = cp.m_tree_type;
this.m_seed_point = cp.m_seed_point;
this.m_initial_scale = cp.m_initial_scale;
this.m_maximum_scale = cp.m_maximum_scale;
this.m_initial_scale = cp.m_initial_scale;
this.m_rate = cp.m_rate;
this.m_planted = planted;
this.m_trees = new List<UUID>();
}
public Copse(string copsedef)
{
char[] delimiterChars = {':', ';'};
string[] field = copsedef.Split(delimiterChars);
this.m_name = field[1].Trim();
this.m_frozen = (copsedef[0] == 'F');
this.m_tree_quantity = int.Parse(field[2]);
this.m_treeline_high = float.Parse(field[3], Culture.NumberFormatInfo);
this.m_treeline_low = float.Parse(field[4], Culture.NumberFormatInfo);
this.m_range = double.Parse(field[5], Culture.NumberFormatInfo);
this.m_tree_type = (Tree) Enum.Parse(typeof(Tree),field[6]);
this.m_seed_point = Vector3.Parse(field[7]);
this.m_initial_scale = Vector3.Parse(field[8]);
this.m_maximum_scale = Vector3.Parse(field[9]);
this.m_rate = Vector3.Parse(field[10]);
this.m_planted = true;
this.m_trees = new List<UUID>();
}
public Copse(string name, int quantity, float high, float low, double range, Vector3 point, Tree type, Vector3 scale, Vector3 max_scale, Vector3 rate, List<UUID> trees)
{
this.m_name = name;
this.m_frozen = false;
this.m_tree_quantity = quantity;
this.m_treeline_high = high;
this.m_treeline_low = low;
this.m_range = range;
this.m_tree_type = type;
this.m_seed_point = point;
this.m_initial_scale = scale;
this.m_maximum_scale = max_scale;
this.m_rate = rate;
this.m_planted = false;
this.m_trees = trees;
}
public override string ToString()
{
string frozen = (this.m_frozen ? "F" : "A");
return string.Format("{0}TPM: {1}; {2}; {3:0.0}; {4:0.0}; {5:0.0}; {6}; {7:0.0}; {8:0.0}; {9:0.0}; {10:0.00};",
frozen,
this.m_name,
this.m_tree_quantity,
this.m_treeline_high,
this.m_treeline_low,
this.m_range,
this.m_tree_type,
this.m_seed_point.ToString(),
this.m_initial_scale.ToString(),
this.m_maximum_scale.ToString(),
this.m_rate.ToString());
}
}
private List<Copse> m_copse;
private double m_update_ms = 1000.0; // msec between updates
private bool m_active_trees = false;
Timer CalculateTrees;
#region ICommandableModule Members
public ICommander CommandInterface
{
get { return m_commander; }
}
#endregion
#region Region Module interface
public void Initialise(IConfigSource config)
{
// ini file settings
try
{
m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees);
}
catch (Exception)
{
m_log.Debug("[TREES]: ini failure for active_trees - using default");
}
try
{
m_update_ms = config.Configs["Trees"].GetDouble("update_rate", m_update_ms);
}
catch (Exception)
{
m_log.Debug("[TREES]: ini failure for update_rate - using default");
}
InstallCommands();
m_log.Debug("[TREES]: Initialised tree module");
}
public void AddRegion(Scene scene)
{
m_scene = scene;
m_scene.RegisterModuleCommander(m_commander);
m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
}
public void RemoveRegion(Scene scene)
{
}
public void RegionLoaded(Scene scene)
{
ReloadCopse();
if (m_copse.Count > 0)
m_log.Info("[TREES]: Copse load complete");
if (m_active_trees)
activeizeTreeze(true);
}
public void Close()
{
}
public string Name
{
get { return "TreePopulatorModule"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
#endregion
//--------------------------------------------------------------
#region ICommandableModule Members
private void HandleTreeActive(Object[] args)
{
if ((Boolean)args[0] && !m_active_trees)
{
m_log.InfoFormat("[TREES]: Activating Trees");
m_active_trees = true;
activeizeTreeze(m_active_trees);
}
else if (!(Boolean)args[0] && m_active_trees)
{
m_log.InfoFormat("[TREES]: Trees module is no longer active");
m_active_trees = false;
activeizeTreeze(m_active_trees);
}
else
{
m_log.InfoFormat("[TREES]: Trees module is already in the required state");
}
}
private void HandleTreeFreeze(Object[] args)
{
string copsename = ((string)args[0]).Trim();
Boolean freezeState = (Boolean) args[1];
foreach (Copse cp in m_copse)
{
if (cp.m_name == copsename && (!cp.m_frozen && freezeState || cp.m_frozen && !freezeState))
{
cp.m_frozen = freezeState;
foreach (UUID tree in cp.m_trees)
{
SceneObjectPart sop = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
sop.Name = (freezeState ? sop.Name.Replace("ATPM", "FTPM") : sop.Name.Replace("FTPM", "ATPM"));
sop.ParentGroup.HasGroupChanged = true;
}
m_log.InfoFormat("[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState);
return;
}
else if (cp.m_name == copsename && (cp.m_frozen && freezeState || !cp.m_frozen && !freezeState))
{
m_log.InfoFormat("[TREES]: Copse {0} is already in the requested freeze state", copsename);
return;
}
}
m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
}
private void HandleTreeLoad(Object[] args)
{
Copse copse;
m_log.InfoFormat("[TREES]: Loading copse definition....");
copse = new Copse(((string)args[0]), false);
foreach (Copse cp in m_copse)
{
if (cp.m_name == copse.m_name)
{
m_log.InfoFormat("[TREES]: Copse: {0} is already defined - command failed", copse.m_name);
return;
}
}
m_copse.Add(copse);
m_log.InfoFormat("[TREES]: Loaded copse: {0}", copse.ToString());
}
private void HandleTreePlant(Object[] args)
{
string copsename = ((string)args[0]).Trim();
m_log.InfoFormat("[TREES]: New tree planting for copse {0}", copsename);
UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
foreach (Copse copse in m_copse)
{
if (copse.m_name == copsename)
{
if (!copse.m_planted)
{
// The first tree for a copse is created here
CreateTree(uuid, copse, copse.m_seed_point);
copse.m_planted = true;
return;
}
else
{
m_log.InfoFormat("[TREES]: Copse {0} has already been planted", copsename);
}
}
}
m_log.InfoFormat("[TREES]: Copse {0} not found for planting", copsename);
}
private void HandleTreeRate(Object[] args)
{
m_update_ms = (double)args[0];
if (m_update_ms >= 1000.0)
{
if (m_active_trees)
{
activeizeTreeze(false);
activeizeTreeze(true);
}
m_log.InfoFormat("[TREES]: Update rate set to {0} mSec", m_update_ms);
}
else
{
m_log.InfoFormat("[TREES]: minimum rate is 1000.0 mSec - command failed");
}
}
private void HandleTreeReload(Object[] args)
{
if (m_active_trees)
{
CalculateTrees.Stop();
}
ReloadCopse();
if (m_active_trees)
{
CalculateTrees.Start();
}
}
private void HandleTreeRemove(Object[] args)
{
string copsename = ((string)args[0]).Trim();
Copse copseIdentity = null;
foreach (Copse cp in m_copse)
{
if (cp.m_name == copsename)
{
copseIdentity = cp;
}
}
if (copseIdentity != null)
{
foreach (UUID tree in copseIdentity.m_trees)
{
if (m_scene.Entities.ContainsKey(tree))
{
SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
// Delete tree and alert clients (not silent)
m_scene.DeleteSceneObject(selectedTree.ParentGroup, false);
}
else
{
m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
}
}
copseIdentity.m_trees = new List<UUID>();
m_copse.Remove(copseIdentity);
m_log.InfoFormat("[TREES]: Copse {0} has been removed", copsename);
}
else
{
m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
}
}
private void HandleTreeStatistics(Object[] args)
{
m_log.InfoFormat("[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms);
foreach (Copse cp in m_copse)
{
m_log.InfoFormat("[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen);
}
}
private void InstallCommands()
{
Command treeActiveCommand =
new Command("active", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeActive, "Change activity state for the trees module");
treeActiveCommand.AddArgument("activeTF", "The required activity state", "Boolean");
Command treeFreezeCommand =
new Command("freeze", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeFreeze, "Freeze/Unfreeze activity for a defined copse");
treeFreezeCommand.AddArgument("copse", "The required copse", "String");
treeFreezeCommand.AddArgument("freezeTF", "The required freeze state", "Boolean");
Command treeLoadCommand =
new Command("load", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeLoad, "Load a copse definition from an xml file");
treeLoadCommand.AddArgument("filename", "The (xml) file you wish to load", "String");
Command treePlantCommand =
new Command("plant", CommandIntentions.COMMAND_HAZARDOUS, HandleTreePlant, "Start the planting on a copse");
treePlantCommand.AddArgument("copse", "The required copse", "String");
Command treeRateCommand =
new Command("rate", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRate, "Reset the tree update rate (mSec)");
treeRateCommand.AddArgument("updateRate", "The required update rate (minimum 1000.0)", "Double");
Command treeReloadCommand =
new Command("reload", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload, "Reload copse definitions from the in-scene trees");
Command treeRemoveCommand =
new Command("remove", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove, "Remove a copse definition and all its in-scene trees");
treeRemoveCommand.AddArgument("copse", "The required copse", "String");
Command treeStatisticsCommand =
new Command("statistics", CommandIntentions.COMMAND_STATISTICAL, HandleTreeStatistics, "Log statistics about the trees");
m_commander.RegisterCommand("active", treeActiveCommand);
m_commander.RegisterCommand("freeze", treeFreezeCommand);
m_commander.RegisterCommand("load", treeLoadCommand);
m_commander.RegisterCommand("plant", treePlantCommand);
m_commander.RegisterCommand("rate", treeRateCommand);
m_commander.RegisterCommand("reload", treeReloadCommand);
m_commander.RegisterCommand("remove", treeRemoveCommand);
m_commander.RegisterCommand("statistics", treeStatisticsCommand);
}
/// <summary>
/// Processes commandline input. Do not call directly.
/// </summary>
/// <param name="args">Commandline arguments</param>
private void EventManager_OnPluginConsole(string[] args)
{
if (args[0] == "tree")
{
if (args.Length == 1)
{
m_commander.ProcessConsoleCommand("help", new string[0]);
return;
}
string[] tmpArgs = new string[args.Length - 2];
int i;
for (i = 2; i < args.Length; i++)
{
tmpArgs[i - 2] = args[i];
}
m_commander.ProcessConsoleCommand(args[1], tmpArgs);
}
}
#endregion
#region IVegetationModule Members
public SceneObjectGroup AddTree(
UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree)
{
PrimitiveBaseShape treeShape = new PrimitiveBaseShape();
treeShape.PathCurve = 16;
treeShape.PathEnd = 49900;
treeShape.PCode = newTree ? (byte)PCode.NewTree : (byte)PCode.Tree;
treeShape.Scale = scale;
treeShape.State = (byte)treeType;
return m_scene.AddNewPrim(uuid, groupID, position, rotation, treeShape);
}
#endregion
#region IEntityCreator Members
protected static readonly PCode[] creationCapabilities = new PCode[] { PCode.NewTree, PCode.Tree };
public PCode[] CreationCapabilities { get { return creationCapabilities; } }
public SceneObjectGroup CreateEntity(
UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
{
if (Array.IndexOf(creationCapabilities, (PCode)shape.PCode) < 0)
{
m_log.DebugFormat("[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name);
return null;
}
SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape);
SceneObjectPart rootPart = sceneObject.GetPart(sceneObject.UUID);
rootPart.AddFlag(PrimFlags.Phantom);
m_scene.AddNewSceneObject(sceneObject, true);
sceneObject.SetGroup(groupID, null);
return sceneObject;
}
#endregion
//--------------------------------------------------------------
#region Tree Utilities
static public void SerializeObject(string fileName, Object obj)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(Copse));
using (XmlTextWriter writer = new XmlTextWriter(fileName, Util.UTF8))
{
writer.Formatting = Formatting.Indented;
xs.Serialize(writer, obj);
}
}
catch (SystemException ex)
{
throw new ApplicationException("Unexpected failure in Tree serialization", ex);
}
}
static public object DeserializeObject(string fileName)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(Copse));
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
return xs.Deserialize(fs);
}
catch (SystemException ex)
{
throw new ApplicationException("Unexpected failure in Tree de-serialization", ex);
}
}
private void ReloadCopse()
{
m_copse = new List<Copse>();
EntityBase[] objs = m_scene.GetEntities();
foreach (EntityBase obj in objs)
{
if (obj is SceneObjectGroup)
{
SceneObjectGroup grp = (SceneObjectGroup)obj;
if (grp.Name.Length > 5 && (grp.Name.Substring(0, 5) == "ATPM:" || grp.Name.Substring(0, 5) == "FTPM:"))
{
// Create a new copse definition or add uuid to an existing definition
try
{
Boolean copsefound = false;
Copse copse = new Copse(grp.Name);
foreach (Copse cp in m_copse)
{
if (cp.m_name == copse.m_name)
{
copsefound = true;
cp.m_trees.Add(grp.UUID);
//m_log.DebugFormat("[TREES]: Found tree {0}", grp.UUID);
}
}
if (!copsefound)
{
m_log.InfoFormat("[TREES]: Found copse {0}", grp.Name);
m_copse.Add(copse);
copse.m_trees.Add(grp.UUID);
}
}
catch
{
m_log.InfoFormat("[TREES]: Ill formed copse definition {0} - ignoring", grp.Name);
}
}
}
}
}
#endregion
private void activeizeTreeze(bool activeYN)
{
if (activeYN)
{
CalculateTrees = new Timer(m_update_ms);
CalculateTrees.Elapsed += CalculateTrees_Elapsed;
CalculateTrees.Start();
}
else
{
CalculateTrees.Stop();
}
}
private void growTrees()
{
foreach (Copse copse in m_copse)
{
if (!copse.m_frozen)
{
foreach (UUID tree in copse.m_trees)
{
if (m_scene.Entities.ContainsKey(tree))
{
SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
if (s_tree.Scale.X < copse.m_maximum_scale.X && s_tree.Scale.Y < copse.m_maximum_scale.Y && s_tree.Scale.Z < copse.m_maximum_scale.Z)
{
s_tree.Scale += copse.m_rate;
s_tree.ParentGroup.HasGroupChanged = true;
s_tree.ScheduleFullUpdate();
}
}
else
{
m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
}
}
}
}
}
private void seedTrees()
{
foreach (Copse copse in m_copse)
{
if (!copse.m_frozen)
{
foreach (UUID tree in copse.m_trees)
{
if (m_scene.Entities.ContainsKey(tree))
{
SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
if (copse.m_trees.Count < copse.m_tree_quantity)
{
// Tree has grown enough to seed if it has grown by at least 25% of seeded to full grown height
if (s_tree.Scale.Z > copse.m_initial_scale.Z + (copse.m_maximum_scale.Z - copse.m_initial_scale.Z) / 4.0)
{
if (Util.RandomClass.NextDouble() > 0.75)
{
SpawnChild(copse, s_tree);
}
}
}
}
else
{
m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
}
}
}
}
}
private void killTrees()
{
foreach (Copse copse in m_copse)
{
if (!copse.m_frozen && copse.m_trees.Count >= copse.m_tree_quantity)
{
foreach (UUID tree in copse.m_trees)
{
double killLikelyhood = 0.0;
if (m_scene.Entities.ContainsKey(tree))
{
SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
double selectedTreeScale = Math.Sqrt(Math.Pow(selectedTree.Scale.X, 2) +
Math.Pow(selectedTree.Scale.Y, 2) +
Math.Pow(selectedTree.Scale.Z, 2));
foreach (UUID picktree in copse.m_trees)
{
if (picktree != tree)
{
SceneObjectPart pickedTree = ((SceneObjectGroup)m_scene.Entities[picktree]).RootPart;
double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) +
Math.Pow(pickedTree.Scale.Y, 2) +
Math.Pow(pickedTree.Scale.Z, 2));
double pickedTreeDistance = Vector3.Distance(pickedTree.AbsolutePosition, selectedTree.AbsolutePosition);
killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1;
}
}
if (Util.RandomClass.NextDouble() < killLikelyhood)
{
// Delete tree and alert clients (not silent)
m_scene.DeleteSceneObject(selectedTree.ParentGroup, false);
copse.m_trees.Remove(selectedTree.ParentGroup.UUID);
break;
}
}
else
{
m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
}
}
}
}
}
private void SpawnChild(Copse copse, SceneObjectPart s_tree)
{
Vector3 position = new Vector3();
double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
position.X = s_tree.AbsolutePosition.X + (float)randX;
position.Y = s_tree.AbsolutePosition.Y + (float)randY;
if (position.X <= (m_scene.RegionInfo.RegionSizeX - 1) && position.X >= 0 &&
position.Y <= (m_scene.RegionInfo.RegionSizeY - 1) && position.Y >= 0 &&
Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range)
{
UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
CreateTree(uuid, copse, position);
}
}
private void CreateTree(UUID uuid, Copse copse, Vector3 position)
{
position.Z = (float)m_scene.Heightmap[(int)position.X, (int)position.Y];
if (position.Z >= copse.m_treeline_low && position.Z <= copse.m_treeline_high)
{
SceneObjectGroup tree = AddTree(uuid, UUID.Zero, copse.m_initial_scale, Quaternion.Identity, position, copse.m_tree_type, false);
tree.Name = copse.ToString();
copse.m_trees.Add(tree.UUID);
tree.SendGroupFullUpdate();
}
}
private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e)
{
growTrees();
seedTrees();
killTrees();
}
}
}