From e584d81e0c014629603aa976928d188dd76a08ca Mon Sep 17 00:00:00 2001 From: Jon Cundill Date: Sun, 24 Jul 2011 00:18:03 +0100 Subject: [PATCH] added ability to control flock from scripts. --- Flocking/Boid.cs | 42 +++-- Flocking/BoidBehaviour.cs | 26 ++++ Flocking/ChatCommandParser.cs | 127 --------------- Flocking/FlockingCommandParser.cs | 247 ++++++++++++++++++++++++++++++ Flocking/FlockingModel.cs | 18 +-- Flocking/FlockingModule.cs | 123 ++++++--------- Flocking/FlockingView.cs | 5 +- Flocking/FlowField.cs | 142 +++++++++++------ 8 files changed, 457 insertions(+), 273 deletions(-) delete mode 100644 Flocking/ChatCommandParser.cs create mode 100644 Flocking/FlockingCommandParser.cs diff --git a/Flocking/Boid.cs b/Flocking/Boid.cs index 651ca54..310a9ec 100644 --- a/Flocking/Boid.cs +++ b/Flocking/Boid.cs @@ -44,7 +44,6 @@ namespace Flocking private Random m_rndnums = new Random (Environment.TickCount); private BoidBehaviour m_behaviour; - private FlowField m_flowField; /// @@ -59,31 +58,31 @@ namespace Flocking /// /// Mf. max force / acceleration this boid can extert /// - public Boid (string id, Vector3 size, BoidBehaviour behaviour, FlowField flowField) + public Boid (string id, Vector3 size, BoidBehaviour behaviour) { m_id = id; m_acc = Vector3.Zero; m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1)); m_size = size; m_behaviour = behaviour; - m_flowField = flowField; } public Vector3 Location { - get { return m_loc;} + get { return m_loc; } set { m_loc = value; } } public Vector3 Velocity { - get { return m_vel;} + get { return m_vel; } + set { m_vel = value; } } public Vector3 Size { - get { return m_size;} + get { return m_size; } } public String Id { - get {return m_id;} + get { return m_id; } } /// @@ -92,15 +91,14 @@ namespace Flocking /// /// Boids. all the other chaps in the scene /// - public void MoveInSceneRelativeToFlock (List neighbours) + public void MoveInSceneRelativeToFlock (List neighbours, FlowField field) { - //List neighbours = m_model.GetNeighbours(this); // we would like to stay with our mates Flock (neighbours); - // our first priority is to not hurt ourselves + // however, our first priority is to not hurt ourselves // so adjust where we would like to go to avoid hitting things - AvoidObstacles (); + AvoidObstacles (field); // then we want to avoid any threats // this not implemented yet @@ -127,7 +125,7 @@ namespace Flocking Vector3 sep = Separate (neighbours); // Separation Vector3 ali = Align (neighbours); // Alignment Vector3 coh = Cohesion (neighbours); // Cohesion - Vector3 ori = Orientation(); + Vector3 ori = Orientation(); // its tricky to fly directly up or down // Arbitrarily weight these forces sep *= m_behaviour.separationWeighting; @@ -213,10 +211,23 @@ namespace Flocking /// /// navigate away from whatever it is we are too close to /// - void AvoidObstacles () + void AvoidObstacles (FlowField field) { //look tolerance metres ahead - m_acc += m_flowField.AdjustVelocity( this, m_behaviour.tolerance ); + //m_acc += field.AdjustVelocity( this, m_behaviour.lookaheadDistance ); + Vector3 normVel = Vector3.Normalize (m_vel); + Vector3 inFront = m_loc + normVel * m_behaviour.LookaheadDistance; + + Vector3 adjustedDestintation = field.FieldStrength (m_loc, m_size, inFront); + Vector3 newVel = Vector3.Normalize (adjustedDestintation - m_loc) * Vector3.Mag (m_vel); + + float mOrigVel = Vector3.Mag(m_vel); + float mNewVel = Vector3.Mag(newVel); + if( mNewVel != 0f && mNewVel > mOrigVel ) { + newVel *= mOrigVel / mNewVel; + } + m_vel = newVel; + } @@ -243,6 +254,9 @@ namespace Flocking tooCloseNeighbours.ForEach( delegate(Boid neighbour) { // Calculate vector pointing away from neighbor Vector3 diff = m_loc - neighbour.Location; + if( diff == Vector3.Zero ) { + diff = new Vector3( (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble()); + } steer += Utils.GetNormalizedVector(diff) / (float)(Utils.GetDistanceTo (m_loc, neighbour.Location)); }); diff --git a/Flocking/BoidBehaviour.cs b/Flocking/BoidBehaviour.cs index d907957..5aad1b6 100644 --- a/Flocking/BoidBehaviour.cs +++ b/Flocking/BoidBehaviour.cs @@ -1,3 +1,29 @@ +/* + * Copyright (c) Contributors, https://github.com/jonc/osboids + * 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; diff --git a/Flocking/ChatCommandParser.cs b/Flocking/ChatCommandParser.cs deleted file mode 100644 index 7ec8123..0000000 --- a/Flocking/ChatCommandParser.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using log4net; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Framework.Console; -using OpenSim.Region.Framework.Interfaces; - -namespace Flocking -{ - public delegate void BoidCmdDelegate (string module,string[] args); - - public class BoidCmdDefn - { - public string Help = ""; - public string Args = ""; - public int NumParams = 0; - string m_name; - - public BoidCmdDefn (string name, string args, string help) - { - Help = help; - Args = args; - m_name = name; - - if (args.Trim ().Length > 0) { - NumParams = args.Split (",".ToCharArray ()).Length; - } else { - NumParams = 0; - } - } - - public string GetSyntax () - { - return m_name + " " + Args + " (" + Help + ")"; - } - } - - public class ChatCommandParser - { - private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType); - private string m_name; - private Scene m_scene; - private int m_chatChannel; - private Dictionary m_commandMap = new Dictionary (); - private Dictionary m_syntaxMap = new Dictionary (); - - public ChatCommandParser (IRegionModuleBase module, Scene scene, int channel) - { - m_name = module.Name; - m_scene = scene; - m_chatChannel = channel; - } - - public void AddCommand (string cmd, string args, string help, CommandDelegate fn) - { - m_commandMap.Add (cmd, new BoidCmdDelegate (fn)); - m_syntaxMap.Add (cmd, new BoidCmdDefn (cmd, args, help)); - } - - public void SimChatSent (Object x, OSChatMessage msg) - { - if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel) - return; // not for us - - // try and parse a valid cmd from this msg - string cmd = msg.Message.ToLower (); - - //stick ui in the args so we know to respond in world - //bit of a hack - but lets us use CommandDelegate inWorld - string[] args = (cmd + " ").Split (" ".ToCharArray ()); - - BoidCmdDefn defn = null; - if (m_syntaxMap.TryGetValue (args [0], out defn)) { - if (CorrectSignature (args, defn)) { - - // we got the signature of the command right - BoidCmdDelegate del = null; - if (m_commandMap.TryGetValue (args [0], out del)) { - del (m_name, args); - } else { - // we don't understand this command - // shouldn't happen - m_log.ErrorFormat ("Unable to invoke command {0}", args [0]); - RespondToMessage (msg, "Unable to invoke command " + args [0]); - } - - } else { - // we recognise the command, but we got the call wrong - RespondToMessage (msg, "wrong syntax: " + defn.GetSyntax ()); - } - } else { - // this is not a command we recognise - RespondToMessage (msg, args [0] + " is not a valid command for osboids"); - } - - } - - private bool CorrectSignature (string[] args, BoidCmdDefn defn) - { - // args contain cmd name at 0 and tagged in last pos - return args.Length - 2 == defn.NumParams; - } - - public void RespondToMessage (OSChatMessage msg, string message) - { - m_log.Debug ("sending response -> " + message); - IClientAPI sender = msg.Sender; - sender.SendChatMessage (message, (byte)ChatTypeEnum.Say, msg.Position, "osboids", msg.SenderUUID, (byte)ChatSourceType.Agent, (byte)ChatAudibleLevel.Fully); - } - - public void SendMessage (ScenePresence recipient, string message) - { - IClientAPI ownerAPI = recipient.ControllingClient; - ownerAPI.SendChatMessage (message, - (byte)ChatTypeEnum.Say, - recipient.AbsolutePosition, - "osboids", - recipient.UUID, - (byte)ChatSourceType.Agent, - (byte)ChatAudibleLevel.Fully - ); - } - } -} - diff --git a/Flocking/FlockingCommandParser.cs b/Flocking/FlockingCommandParser.cs new file mode 100644 index 0000000..2411081 --- /dev/null +++ b/Flocking/FlockingCommandParser.cs @@ -0,0 +1,247 @@ +/* + * Copyright (c) Contributors, https://github.com/jonc/osboids + * 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 log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Console; +using OpenSim.Region.Framework.Interfaces; + +namespace Flocking +{ + public delegate void BoidCmdDelegate (string module,string[] args); + + public class BoidCmdDefn + { + public string Help = ""; + public string Args = ""; + public int NumParams = 0; + string m_name; + + public BoidCmdDefn (string name, string args, string help) + { + Help = help; + Args = args; + m_name = name; + + if (args.Trim ().Length > 0) { + NumParams = args.Split (",".ToCharArray ()).Length; + } else { + NumParams = 0; + } + } + + public string GetSyntax () + { + return m_name + " " + Args + " (" + Help + ")"; + } + } + + public class FlockingCommandParser + { + private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType); + private IRegionModuleBase m_module; + private Scene m_scene; + private int m_chatChannel; + private UUID m_owner; + private Dictionary m_commandMap = new Dictionary (); + private Dictionary m_syntaxMap = new Dictionary (); + + + public FlockingCommandParser (IRegionModuleBase module, Scene scene, int channel) + { + m_module = module; + m_scene = scene; + m_chatChannel = channel; + + // who do we respond to in send messages + m_owner = scene.RegionInfo.EstateSettings.EstateOwner; + + // register our event handlers + m_scene.EventManager.OnChatFromClient += ProcessChatCommand; //listen for commands sent from the client + + IScriptModuleComms commsMod = scene.RequestModuleInterface(); + commsMod.OnScriptCommand += ProcessScriptCommand; + } + + public void Deregister () + { + m_scene.EventManager.OnChatFromClient -= ProcessChatCommand; + IScriptModuleComms commsMod = m_scene.RequestModuleInterface(); + commsMod.OnScriptCommand -= ProcessScriptCommand; + } + + public void AddCommand (string cmd, string args, string help, CommandDelegate fn) + { + string argStr = ""; + if (args.Trim ().Length > 0) { + argStr = " <" + args + "> "; + } + m_commandMap.Add (cmd, new BoidCmdDelegate (fn)); + m_syntaxMap.Add (cmd, new BoidCmdDefn (cmd, args, help)); + // register this command with the console + m_scene.AddCommand (m_module, "flock-" + cmd, "flock-" + cmd + argStr, help, fn); + } + + + #region handlers + + public void ProcessScriptCommand (UUID scriptId, string reqId, string module, string input, string key) + { + if (m_module.Name != module) { + return; + } + + string[] tokens = (input+"|