clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Timers;
32 using OpenMetaverse;
33 using log4net;
34 using Mono.Addins;
35 using Nini.Config;
36 using OpenSim.Framework;
37 using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40  
41 using System.Xml;
42 using System.Xml.Serialization;
43 using System.IO;
44  
45 namespace OpenSim.Region.OptionalModules.World.TreePopulator
46 {
47 /// <summary>
48 /// Version 2.02 - Still hacky
49 /// </summary>
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "TreePopulatorModule")]
51 public class TreePopulatorModule : INonSharedRegionModule, ICommandableModule, IVegetationModule
52 {
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 private readonly Commander m_commander = new Commander("tree");
55 private Scene m_scene;
56  
57 [XmlRootAttribute(ElementName = "Copse", IsNullable = false)]
58 public class Copse
59 {
60 public string m_name;
61 public Boolean m_frozen;
62 public Tree m_tree_type;
63 public int m_tree_quantity;
64 public float m_treeline_low;
65 public float m_treeline_high;
66 public Vector3 m_seed_point;
67 public double m_range;
68 public Vector3 m_initial_scale;
69 public Vector3 m_maximum_scale;
70 public Vector3 m_rate;
71  
72 [XmlIgnore]
73 public Boolean m_planted;
74 [XmlIgnore]
75 public List<UUID> m_trees;
76  
77 public Copse()
78 {
79 }
80  
81 public Copse(string fileName, Boolean planted)
82 {
83 Copse cp = (Copse)DeserializeObject(fileName);
84  
85 this.m_name = cp.m_name;
86 this.m_frozen = cp.m_frozen;
87 this.m_tree_quantity = cp.m_tree_quantity;
88 this.m_treeline_high = cp.m_treeline_high;
89 this.m_treeline_low = cp.m_treeline_low;
90 this.m_range = cp.m_range;
91 this.m_tree_type = cp.m_tree_type;
92 this.m_seed_point = cp.m_seed_point;
93 this.m_initial_scale = cp.m_initial_scale;
94 this.m_maximum_scale = cp.m_maximum_scale;
95 this.m_initial_scale = cp.m_initial_scale;
96 this.m_rate = cp.m_rate;
97 this.m_planted = planted;
98 this.m_trees = new List<UUID>();
99 }
100  
101 public Copse(string copsedef)
102 {
103 char[] delimiterChars = {':', ';'};
104 string[] field = copsedef.Split(delimiterChars);
105  
106 this.m_name = field[1].Trim();
107 this.m_frozen = (copsedef[0] == 'F');
108 this.m_tree_quantity = int.Parse(field[2]);
109 this.m_treeline_high = float.Parse(field[3], Culture.NumberFormatInfo);
110 this.m_treeline_low = float.Parse(field[4], Culture.NumberFormatInfo);
111 this.m_range = double.Parse(field[5], Culture.NumberFormatInfo);
112 this.m_tree_type = (Tree) Enum.Parse(typeof(Tree),field[6]);
113 this.m_seed_point = Vector3.Parse(field[7]);
114 this.m_initial_scale = Vector3.Parse(field[8]);
115 this.m_maximum_scale = Vector3.Parse(field[9]);
116 this.m_rate = Vector3.Parse(field[10]);
117 this.m_planted = true;
118 this.m_trees = new List<UUID>();
119 }
120  
121 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)
122 {
123 this.m_name = name;
124 this.m_frozen = false;
125 this.m_tree_quantity = quantity;
126 this.m_treeline_high = high;
127 this.m_treeline_low = low;
128 this.m_range = range;
129 this.m_tree_type = type;
130 this.m_seed_point = point;
131 this.m_initial_scale = scale;
132 this.m_maximum_scale = max_scale;
133 this.m_rate = rate;
134 this.m_planted = false;
135 this.m_trees = trees;
136 }
137  
138 public override string ToString()
139 {
140 string frozen = (this.m_frozen ? "F" : "A");
141  
142 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};",
143 frozen,
144 this.m_name,
145 this.m_tree_quantity,
146 this.m_treeline_high,
147 this.m_treeline_low,
148 this.m_range,
149 this.m_tree_type,
150 this.m_seed_point.ToString(),
151 this.m_initial_scale.ToString(),
152 this.m_maximum_scale.ToString(),
153 this.m_rate.ToString());
154 }
155 }
156  
157 private List<Copse> m_copse;
158  
159 private double m_update_ms = 1000.0; // msec between updates
160 private bool m_active_trees = false;
161  
162 Timer CalculateTrees;
163  
164 #region ICommandableModule Members
165  
166 public ICommander CommandInterface
167 {
168 get { return m_commander; }
169 }
170  
171 #endregion
172  
173 #region Region Module interface
174  
175 public void Initialise(IConfigSource config)
176 {
177  
178 // ini file settings
179 try
180 {
181 m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees);
182 }
183 catch (Exception)
184 {
185 m_log.Debug("[TREES]: ini failure for active_trees - using default");
186 }
187  
188 try
189 {
190 m_update_ms = config.Configs["Trees"].GetDouble("update_rate", m_update_ms);
191 }
192 catch (Exception)
193 {
194 m_log.Debug("[TREES]: ini failure for update_rate - using default");
195 }
196  
197 InstallCommands();
198  
199 m_log.Debug("[TREES]: Initialised tree module");
200 }
201  
202 public void AddRegion(Scene scene)
203 {
204 m_scene = scene;
205 m_scene.RegisterModuleCommander(m_commander);
206 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
207  
208 }
209  
210 public void RemoveRegion(Scene scene)
211 {
212 }
213  
214 public void RegionLoaded(Scene scene)
215 {
216 ReloadCopse();
217 if (m_copse.Count > 0)
218 m_log.Info("[TREES]: Copse load complete");
219  
220 if (m_active_trees)
221 activeizeTreeze(true);
222 }
223  
224 public void Close()
225 {
226 }
227  
228 public string Name
229 {
230 get { return "TreePopulatorModule"; }
231 }
232  
233 public Type ReplaceableInterface
234 {
235 get { return null; }
236 }
237  
238  
239 #endregion
240  
241 //--------------------------------------------------------------
242  
243 #region ICommandableModule Members
244  
245 private void HandleTreeActive(Object[] args)
246 {
247 if ((Boolean)args[0] && !m_active_trees)
248 {
249 m_log.InfoFormat("[TREES]: Activating Trees");
250 m_active_trees = true;
251 activeizeTreeze(m_active_trees);
252 }
253 else if (!(Boolean)args[0] && m_active_trees)
254 {
255 m_log.InfoFormat("[TREES]: Trees module is no longer active");
256 m_active_trees = false;
257 activeizeTreeze(m_active_trees);
258 }
259 else
260 {
261 m_log.InfoFormat("[TREES]: Trees module is already in the required state");
262 }
263 }
264  
265 private void HandleTreeFreeze(Object[] args)
266 {
267 string copsename = ((string)args[0]).Trim();
268 Boolean freezeState = (Boolean) args[1];
269  
270 foreach (Copse cp in m_copse)
271 {
272 if (cp.m_name == copsename && (!cp.m_frozen && freezeState || cp.m_frozen && !freezeState))
273 {
274 cp.m_frozen = freezeState;
275 foreach (UUID tree in cp.m_trees)
276 {
277 SceneObjectPart sop = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
278 sop.Name = (freezeState ? sop.Name.Replace("ATPM", "FTPM") : sop.Name.Replace("FTPM", "ATPM"));
279 sop.ParentGroup.HasGroupChanged = true;
280 }
281  
282 m_log.InfoFormat("[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState);
283 return;
284 }
285 else if (cp.m_name == copsename && (cp.m_frozen && freezeState || !cp.m_frozen && !freezeState))
286 {
287 m_log.InfoFormat("[TREES]: Copse {0} is already in the requested freeze state", copsename);
288 return;
289 }
290 }
291 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
292 }
293  
294 private void HandleTreeLoad(Object[] args)
295 {
296 Copse copse;
297  
298 m_log.InfoFormat("[TREES]: Loading copse definition....");
299  
300 copse = new Copse(((string)args[0]), false);
301 foreach (Copse cp in m_copse)
302 {
303 if (cp.m_name == copse.m_name)
304 {
305 m_log.InfoFormat("[TREES]: Copse: {0} is already defined - command failed", copse.m_name);
306 return;
307 }
308 }
309  
310 m_copse.Add(copse);
311 m_log.InfoFormat("[TREES]: Loaded copse: {0}", copse.ToString());
312 }
313  
314 private void HandleTreePlant(Object[] args)
315 {
316 string copsename = ((string)args[0]).Trim();
317  
318 m_log.InfoFormat("[TREES]: New tree planting for copse {0}", copsename);
319 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
320  
321 foreach (Copse copse in m_copse)
322 {
323 if (copse.m_name == copsename)
324 {
325 if (!copse.m_planted)
326 {
327 // The first tree for a copse is created here
328 CreateTree(uuid, copse, copse.m_seed_point);
329 copse.m_planted = true;
330 return;
331 }
332 else
333 {
334 m_log.InfoFormat("[TREES]: Copse {0} has already been planted", copsename);
335 }
336 }
337 }
338 m_log.InfoFormat("[TREES]: Copse {0} not found for planting", copsename);
339 }
340  
341 private void HandleTreeRate(Object[] args)
342 {
343 m_update_ms = (double)args[0];
344 if (m_update_ms >= 1000.0)
345 {
346 if (m_active_trees)
347 {
348 activeizeTreeze(false);
349 activeizeTreeze(true);
350 }
351 m_log.InfoFormat("[TREES]: Update rate set to {0} mSec", m_update_ms);
352 }
353 else
354 {
355 m_log.InfoFormat("[TREES]: minimum rate is 1000.0 mSec - command failed");
356 }
357 }
358  
359 private void HandleTreeReload(Object[] args)
360 {
361 if (m_active_trees)
362 {
363 CalculateTrees.Stop();
364 }
365  
366 ReloadCopse();
367  
368 if (m_active_trees)
369 {
370 CalculateTrees.Start();
371 }
372 }
373  
374 private void HandleTreeRemove(Object[] args)
375 {
376 string copsename = ((string)args[0]).Trim();
377 Copse copseIdentity = null;
378  
379 foreach (Copse cp in m_copse)
380 {
381 if (cp.m_name == copsename)
382 {
383 copseIdentity = cp;
384 }
385 }
386  
387 if (copseIdentity != null)
388 {
389 foreach (UUID tree in copseIdentity.m_trees)
390 {
391 if (m_scene.Entities.ContainsKey(tree))
392 {
393 SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
394 // Delete tree and alert clients (not silent)
395 m_scene.DeleteSceneObject(selectedTree.ParentGroup, false);
396 }
397 else
398 {
399 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
400 }
401 }
402 copseIdentity.m_trees = new List<UUID>();
403 m_copse.Remove(copseIdentity);
404 m_log.InfoFormat("[TREES]: Copse {0} has been removed", copsename);
405 }
406 else
407 {
408 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
409 }
410 }
411  
412 private void HandleTreeStatistics(Object[] args)
413 {
414 m_log.InfoFormat("[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms);
415 foreach (Copse cp in m_copse)
416 {
417 m_log.InfoFormat("[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen);
418 }
419 }
420  
421 private void InstallCommands()
422 {
423 Command treeActiveCommand =
424 new Command("active", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeActive, "Change activity state for the trees module");
425 treeActiveCommand.AddArgument("activeTF", "The required activity state", "Boolean");
426  
427 Command treeFreezeCommand =
428 new Command("freeze", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeFreeze, "Freeze/Unfreeze activity for a defined copse");
429 treeFreezeCommand.AddArgument("copse", "The required copse", "String");
430 treeFreezeCommand.AddArgument("freezeTF", "The required freeze state", "Boolean");
431  
432 Command treeLoadCommand =
433 new Command("load", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeLoad, "Load a copse definition from an xml file");
434 treeLoadCommand.AddArgument("filename", "The (xml) file you wish to load", "String");
435  
436 Command treePlantCommand =
437 new Command("plant", CommandIntentions.COMMAND_HAZARDOUS, HandleTreePlant, "Start the planting on a copse");
438 treePlantCommand.AddArgument("copse", "The required copse", "String");
439  
440 Command treeRateCommand =
441 new Command("rate", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRate, "Reset the tree update rate (mSec)");
442 treeRateCommand.AddArgument("updateRate", "The required update rate (minimum 1000.0)", "Double");
443  
444 Command treeReloadCommand =
445 new Command("reload", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload, "Reload copse definitions from the in-scene trees");
446  
447 Command treeRemoveCommand =
448 new Command("remove", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove, "Remove a copse definition and all its in-scene trees");
449 treeRemoveCommand.AddArgument("copse", "The required copse", "String");
450  
451 Command treeStatisticsCommand =
452 new Command("statistics", CommandIntentions.COMMAND_STATISTICAL, HandleTreeStatistics, "Log statistics about the trees");
453  
454 m_commander.RegisterCommand("active", treeActiveCommand);
455 m_commander.RegisterCommand("freeze", treeFreezeCommand);
456 m_commander.RegisterCommand("load", treeLoadCommand);
457 m_commander.RegisterCommand("plant", treePlantCommand);
458 m_commander.RegisterCommand("rate", treeRateCommand);
459 m_commander.RegisterCommand("reload", treeReloadCommand);
460 m_commander.RegisterCommand("remove", treeRemoveCommand);
461 m_commander.RegisterCommand("statistics", treeStatisticsCommand);
462 }
463  
464 /// <summary>
465 /// Processes commandline input. Do not call directly.
466 /// </summary>
467 /// <param name="args">Commandline arguments</param>
468 private void EventManager_OnPluginConsole(string[] args)
469 {
470 if (args[0] == "tree")
471 {
472 if (args.Length == 1)
473 {
474 m_commander.ProcessConsoleCommand("help", new string[0]);
475 return;
476 }
477  
478 string[] tmpArgs = new string[args.Length - 2];
479 int i;
480 for (i = 2; i < args.Length; i++)
481 {
482 tmpArgs[i - 2] = args[i];
483 }
484  
485 m_commander.ProcessConsoleCommand(args[1], tmpArgs);
486 }
487 }
488 #endregion
489  
490 #region IVegetationModule Members
491  
492 public SceneObjectGroup AddTree(
493 UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree)
494 {
495 PrimitiveBaseShape treeShape = new PrimitiveBaseShape();
496 treeShape.PathCurve = 16;
497 treeShape.PathEnd = 49900;
498 treeShape.PCode = newTree ? (byte)PCode.NewTree : (byte)PCode.Tree;
499 treeShape.Scale = scale;
500 treeShape.State = (byte)treeType;
501  
502 return m_scene.AddNewPrim(uuid, groupID, position, rotation, treeShape);
503 }
504  
505 #endregion
506  
507 #region IEntityCreator Members
508  
509 protected static readonly PCode[] creationCapabilities = new PCode[] { PCode.NewTree, PCode.Tree };
510 public PCode[] CreationCapabilities { get { return creationCapabilities; } }
511  
512 public SceneObjectGroup CreateEntity(
513 UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
514 {
515 if (Array.IndexOf(creationCapabilities, (PCode)shape.PCode) < 0)
516 {
517 m_log.DebugFormat("[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name);
518 return null;
519 }
520  
521 SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape);
522 SceneObjectPart rootPart = sceneObject.GetPart(sceneObject.UUID);
523  
524 rootPart.AddFlag(PrimFlags.Phantom);
525  
526 m_scene.AddNewSceneObject(sceneObject, true);
527 sceneObject.SetGroup(groupID, null);
528  
529 return sceneObject;
530 }
531  
532 #endregion
533  
534 //--------------------------------------------------------------
535  
536 #region Tree Utilities
537 static public void SerializeObject(string fileName, Object obj)
538 {
539 try
540 {
541 XmlSerializer xs = new XmlSerializer(typeof(Copse));
542  
543 using (XmlTextWriter writer = new XmlTextWriter(fileName, Util.UTF8))
544 {
545 writer.Formatting = Formatting.Indented;
546 xs.Serialize(writer, obj);
547 }
548 }
549 catch (SystemException ex)
550 {
551 throw new ApplicationException("Unexpected failure in Tree serialization", ex);
552 }
553 }
554  
555 static public object DeserializeObject(string fileName)
556 {
557 try
558 {
559 XmlSerializer xs = new XmlSerializer(typeof(Copse));
560  
561 using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
562 return xs.Deserialize(fs);
563 }
564 catch (SystemException ex)
565 {
566 throw new ApplicationException("Unexpected failure in Tree de-serialization", ex);
567 }
568 }
569  
570 private void ReloadCopse()
571 {
572 m_copse = new List<Copse>();
573  
574 EntityBase[] objs = m_scene.GetEntities();
575 foreach (EntityBase obj in objs)
576 {
577 if (obj is SceneObjectGroup)
578 {
579 SceneObjectGroup grp = (SceneObjectGroup)obj;
580  
581 if (grp.Name.Length > 5 && (grp.Name.Substring(0, 5) == "ATPM:" || grp.Name.Substring(0, 5) == "FTPM:"))
582 {
583 // Create a new copse definition or add uuid to an existing definition
584 try
585 {
586 Boolean copsefound = false;
587 Copse copse = new Copse(grp.Name);
588  
589 foreach (Copse cp in m_copse)
590 {
591 if (cp.m_name == copse.m_name)
592 {
593 copsefound = true;
594 cp.m_trees.Add(grp.UUID);
595 //m_log.DebugFormat("[TREES]: Found tree {0}", grp.UUID);
596 }
597 }
598  
599 if (!copsefound)
600 {
601 m_log.InfoFormat("[TREES]: Found copse {0}", grp.Name);
602 m_copse.Add(copse);
603 copse.m_trees.Add(grp.UUID);
604 }
605 }
606 catch
607 {
608 m_log.InfoFormat("[TREES]: Ill formed copse definition {0} - ignoring", grp.Name);
609 }
610 }
611 }
612 }
613 }
614 #endregion
615  
616 private void activeizeTreeze(bool activeYN)
617 {
618 if (activeYN)
619 {
620 CalculateTrees = new Timer(m_update_ms);
621 CalculateTrees.Elapsed += CalculateTrees_Elapsed;
622 CalculateTrees.Start();
623 }
624 else
625 {
626 CalculateTrees.Stop();
627 }
628 }
629  
630 private void growTrees()
631 {
632 foreach (Copse copse in m_copse)
633 {
634 if (!copse.m_frozen)
635 {
636 foreach (UUID tree in copse.m_trees)
637 {
638 if (m_scene.Entities.ContainsKey(tree))
639 {
640 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
641  
642 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)
643 {
644 s_tree.Scale += copse.m_rate;
645 s_tree.ParentGroup.HasGroupChanged = true;
646 s_tree.ScheduleFullUpdate();
647 }
648 }
649 else
650 {
651 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
652 }
653 }
654 }
655 }
656 }
657  
658 private void seedTrees()
659 {
660 foreach (Copse copse in m_copse)
661 {
662 if (!copse.m_frozen)
663 {
664 foreach (UUID tree in copse.m_trees)
665 {
666 if (m_scene.Entities.ContainsKey(tree))
667 {
668 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
669  
670 if (copse.m_trees.Count < copse.m_tree_quantity)
671 {
672 // Tree has grown enough to seed if it has grown by at least 25% of seeded to full grown height
673 if (s_tree.Scale.Z > copse.m_initial_scale.Z + (copse.m_maximum_scale.Z - copse.m_initial_scale.Z) / 4.0)
674 {
675 if (Util.RandomClass.NextDouble() > 0.75)
676 {
677 SpawnChild(copse, s_tree);
678 }
679 }
680 }
681 }
682 else
683 {
684 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
685 }
686 }
687 }
688 }
689 }
690  
691 private void killTrees()
692 {
693 foreach (Copse copse in m_copse)
694 {
695 if (!copse.m_frozen && copse.m_trees.Count >= copse.m_tree_quantity)
696 {
697 foreach (UUID tree in copse.m_trees)
698 {
699 double killLikelyhood = 0.0;
700  
701 if (m_scene.Entities.ContainsKey(tree))
702 {
703 SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
704 double selectedTreeScale = Math.Sqrt(Math.Pow(selectedTree.Scale.X, 2) +
705 Math.Pow(selectedTree.Scale.Y, 2) +
706 Math.Pow(selectedTree.Scale.Z, 2));
707  
708 foreach (UUID picktree in copse.m_trees)
709 {
710 if (picktree != tree)
711 {
712 SceneObjectPart pickedTree = ((SceneObjectGroup)m_scene.Entities[picktree]).RootPart;
713  
714 double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) +
715 Math.Pow(pickedTree.Scale.Y, 2) +
716 Math.Pow(pickedTree.Scale.Z, 2));
717  
718 double pickedTreeDistance = Vector3.Distance(pickedTree.AbsolutePosition, selectedTree.AbsolutePosition);
719  
720 killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1;
721 }
722 }
723  
724 if (Util.RandomClass.NextDouble() < killLikelyhood)
725 {
726 // Delete tree and alert clients (not silent)
727 m_scene.DeleteSceneObject(selectedTree.ParentGroup, false);
728 copse.m_trees.Remove(selectedTree.ParentGroup.UUID);
729 break;
730 }
731 }
732 else
733 {
734 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
735 }
736 }
737 }
738 }
739 }
740  
741 private void SpawnChild(Copse copse, SceneObjectPart s_tree)
742 {
743 Vector3 position = new Vector3();
744  
745 double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
746 double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
747  
748 position.X = s_tree.AbsolutePosition.X + (float)randX;
749 position.Y = s_tree.AbsolutePosition.Y + (float)randY;
750  
751 if (position.X <= ((int)Constants.RegionSize - 1) && position.X >= 0 &&
752 position.Y <= ((int)Constants.RegionSize - 1) && position.Y >= 0 &&
753 Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range)
754 {
755 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
756  
757 CreateTree(uuid, copse, position);
758 }
759 }
760  
761 private void CreateTree(UUID uuid, Copse copse, Vector3 position)
762 {
763  
764 position.Z = (float)m_scene.Heightmap[(int)position.X, (int)position.Y];
765 if (position.Z >= copse.m_treeline_low && position.Z <= copse.m_treeline_high)
766 {
767 SceneObjectGroup tree = AddTree(uuid, UUID.Zero, copse.m_initial_scale, Quaternion.Identity, position, copse.m_tree_type, false);
768  
769 tree.Name = copse.ToString();
770 copse.m_trees.Add(tree.UUID);
771 tree.SendGroupFullUpdate();
772 }
773 }
774  
775 private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e)
776 {
777 growTrees();
778 seedTrees();
779 killTrees();
780 }
781 }
782 }
783