-// \r
-// Radegast Metaverse Client\r
-// Copyright (c) 2009, Radegast Development Team\r
-// All rights reserved.\r
-// \r
-// Redistribution and use in source and binary forms, with or without\r
-// modification, are permitted provided that the following conditions are met:\r
-// \r
-// * Redistributions of source code must retain the above copyright notice,\r
-// this list of conditions and the following disclaimer.\r
-// * Redistributions in binary form must reproduce the above copyright\r
-// notice, this list of conditions and the following disclaimer in the\r
-// documentation and/or other materials provided with the distribution.\r
-// * Neither the name of the application "Radegast", nor the names of its\r
-// contributors may be used to endorse or promote products derived from\r
-// this software without specific prior written permission.\r
-// \r
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-//\r
-// $Id$\r
-//\r
-using System;\r
-using System.Collections.Generic;\r
-using System.Timers;\r
-using System.Threading;\r
-using OpenMetaverse;\r
-using Radegast.Netcom;\r
-\r
-namespace Radegast\r
-{\r
- public class StateManager : IDisposable\r
- {\r
- private RadegastInstance instance;\r
- private GridClient client;\r
- private RadegastNetcom netcom;\r
-\r
- private bool typing = false;\r
- private bool away = false;\r
- private bool busy = false;\r
- private bool flying = false;\r
- private bool alwaysrun = false;\r
- private bool sitting = false;\r
- \r
- private bool following = false;\r
- private string followName = string.Empty;\r
- private float followDistance = 3.0f;\r
-\r
- private UUID awayAnimationID = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");\r
- private UUID busyAnimationID = new UUID("efcf670c2d188128973a034ebc806b67");\r
- private UUID typingAnimationID = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");\r
-\r
- /// <summary>\r
- /// Passes walk state\r
- /// </summary>\r
- /// <param name="walking">True if we are walking towards a targer</param>\r
- public delegate void WalkStateCanged(bool walking);\r
-\r
- /// <summary>\r
- /// Fires when we start or stop walking towards a target\r
- /// </summary>\r
- public event WalkStateCanged OnWalkStateCanged;\r
-\r
-\r
- public StateManager(RadegastInstance instance)\r
- {\r
- this.instance = instance;\r
- netcom = this.instance.Netcom;\r
- client = this.instance.Client;\r
-\r
- beamTimer = new System.Timers.Timer();\r
- beamTimer.Enabled = false;\r
- beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);\r
-\r
- // Callbacks\r
- netcom.ClientLoginStatus += new EventHandler<ClientLoginEventArgs>(netcom_ClientLoginStatus);\r
- netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);\r
- client.Objects.OnObjectUpdated += new ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);\r
- client.Self.OnAlertMessage += new AgentManager.AlertMessageCallback(Self_OnAlertMessage);\r
-\r
- }\r
-\r
- public void Dispose()\r
- {\r
- netcom.ClientLoginStatus -= new EventHandler<ClientLoginEventArgs>(netcom_ClientLoginStatus);\r
- netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);\r
- client.Objects.OnObjectUpdated -= new ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);\r
- client.Self.OnAlertMessage -= new AgentManager.AlertMessageCallback(Self_OnAlertMessage);\r
- beamTimer.Dispose();\r
- beamTimer = null;\r
-\r
- if (walkTimer != null)\r
- {\r
- walkTimer.Dispose();\r
- walkTimer = null;\r
- }\r
- }\r
-\r
- private void netcom_ClientLoggedOut(object sender, EventArgs e)\r
- {\r
- typing = away = busy = walking = false;\r
- }\r
-\r
- private void netcom_ClientLoginStatus(object sender, ClientLoginEventArgs e)\r
- {\r
- if (e.Status == LoginStatus.Success) {\r
- client.Self.Movement.Camera.Far = 256f;\r
- effectSource = client.Self.AgentID;\r
- }\r
- }\r
-\r
- private void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)\r
- {\r
- if (!update.Avatar) return;\r
- if (!following) return;\r
-\r
- Avatar av;\r
- client.Network.CurrentSim.ObjectsAvatars.TryGetValue(update.LocalID, out av);\r
- if (av == null) return;\r
-\r
- if (av.Name == followName)\r
- {\r
- Vector3 pos;\r
-\r
- if (av.ParentID == 0)\r
- {\r
- pos = av.Position;\r
- }\r
- else\r
- {\r
- Primitive prim;\r
- client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim);\r
- \r
- if (prim == null)\r
- pos = client.Self.SimPosition;\r
- else\r
- pos = prim.Position + av.Position;\r
- }\r
-\r
- if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)\r
- {\r
- int followRegionX = (int)(regionHandle >> 32);\r
- int followRegionY = (int)(regionHandle & 0xFFFFFFFF);\r
- ulong x = (ulong)(pos.X + followRegionX);\r
- ulong y = (ulong)(pos.Y + followRegionY);\r
-\r
- client.Self.AutoPilotCancel();\r
- client.Self.AutoPilot(x, y, pos.Z);\r
- }\r
- }\r
- }\r
-\r
- public void Follow(string name)\r
- {\r
- followName = name;\r
- following = !string.IsNullOrEmpty(followName);\r
-\r
- if (following)\r
- {\r
- walking = false;\r
- }\r
- }\r
-\r
- #region Walking (move to)\r
- private bool walking = false;\r
- private System.Threading.Timer walkTimer;\r
- private int walkChekInterval = 500;\r
- private Vector3d walkToTarget;\r
- int lastDistance = 0;\r
- int lastDistanceChanged = 0;\r
-\r
- public void WalkTo(Primitive prim)\r
- {\r
- WalkTo(GlobalPosition(prim));\r
- }\r
-\r
- public void WalkTo(Vector3d globalPos)\r
- {\r
- walkToTarget = globalPos;\r
-\r
- if (following)\r
- {\r
- following = false;\r
- followName = string.Empty;\r
- }\r
-\r
- if (walkTimer == null)\r
- {\r
- walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);\r
- }\r
-\r
- lastDistanceChanged = System.Environment.TickCount;\r
- client.Self.AutoPilotCancel();\r
- walking = true;\r
- client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);\r
- FireWalkStateCanged();\r
- }\r
-\r
- void walkTimerElapsed(object sender)\r
- {\r
-\r
- double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);\r
-\r
- if (distance < 2d)\r
- {\r
- // We're there\r
- EndWalking();\r
- }\r
- else\r
- {\r
- if (lastDistance != (int)distance)\r
- {\r
- lastDistanceChanged = System.Environment.TickCount;\r
- lastDistance = (int)distance;\r
- }\r
- else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)\r
- {\r
- // Our distance to the target has not changed in 10s, give up\r
- EndWalking();\r
- return;\r
- }\r
- walkTimer.Change(walkChekInterval, Timeout.Infinite);\r
- }\r
- }\r
-\r
- void Self_OnAlertMessage(string message)\r
- {\r
- if (message.Contains("Autopilot cancel"))\r
- {\r
- if (walking)\r
- {\r
- EndWalking();\r
- }\r
- }\r
- }\r
-\r
- void FireWalkStateCanged()\r
- {\r
- if (OnWalkStateCanged != null)\r
- {\r
- try { OnWalkStateCanged(walking); }\r
- catch (Exception) { }\r
- }\r
- }\r
-\r
- public void EndWalking()\r
- {\r
- if (walking)\r
- {\r
- walking = false;\r
- Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);\r
- walkTimer.Dispose();\r
- walkTimer = null;\r
- client.Self.AutoPilotCancel();\r
- FireWalkStateCanged();\r
- }\r
- }\r
- #endregion\r
-\r
- public void SetTyping(bool typing)\r
- {\r
- Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();\r
- typingAnim.Add(typingAnimationID, typing);\r
-\r
- client.Self.Animate(typingAnim, false);\r
-\r
- if (typing)\r
- client.Self.Chat(string.Empty, 0, ChatType.StartTyping);\r
- else\r
- client.Self.Chat(string.Empty, 0, ChatType.StopTyping);\r
-\r
- this.typing = typing;\r
- }\r
-\r
- public void SetAway(bool away)\r
- {\r
- Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();\r
- awayAnim.Add(awayAnimationID, away);\r
-\r
- client.Self.Animate(awayAnim, true);\r
- this.away = away;\r
- }\r
-\r
- public void SetBusy(bool busy)\r
- {\r
- Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();\r
- busyAnim.Add(busyAnimationID, busy);\r
-\r
- client.Self.Animate(busyAnim, true);\r
- this.busy = busy;\r
- }\r
-\r
- public void SetFlying(bool flying)\r
- {\r
- this.flying = client.Self.Movement.Fly = flying;\r
- }\r
-\r
- public void SetAlwaysRun(bool alwaysrun)\r
- {\r
- this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;\r
- }\r
-\r
- public void SetSitting(bool sitting, UUID target)\r
- {\r
- this.sitting = sitting;\r
-\r
- if (sitting)\r
- {\r
- client.Self.RequestSit(target, Vector3.Zero);\r
- client.Self.Sit();\r
- }\r
- else\r
- {\r
- client.Self.Stand();\r
- }\r
- }\r
-\r
- public Vector3d GlobalPosition(Primitive prim)\r
- {\r
- uint globalX, globalY;\r
- Utils.LongToUInts(client.Network.CurrentSim.Handle, out globalX, out globalY);\r
- Vector3 pos = prim.Position;\r
-\r
- return new Vector3d(\r
- (double)globalX + (double)pos.X,\r
- (double)globalY + (double)pos.Y,\r
- (double)pos.Z);\r
- }\r
-\r
- private System.Timers.Timer beamTimer;\r
- private List<Vector3d> beamTarget;\r
- private Random beamRandom = new Random();\r
- private UUID pointID;\r
- private UUID sphereID;\r
- private List<UUID> beamID;\r
- private int numBeans;\r
- private Color4[] beamColors = new Color4[3] { new Color4(0, 255, 0, 255), new Color4(255, 0, 0, 255), new Color4(0, 0, 255, 255) };\r
- private Primitive targetPrim;\r
- private UUID effectSource;\r
-\r
- public void UnSetPointing()\r
- {\r
- beamTimer.Enabled = false;\r
- if (pointID != UUID.Zero)\r
- {\r
- client.Self.PointAtEffect(effectSource, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);\r
- pointID = UUID.Zero;\r
- }\r
-\r
- if (beamID != null)\r
- {\r
- foreach (UUID id in beamID)\r
- {\r
- client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);\r
- }\r
- beamID = null;\r
- }\r
-\r
- if (sphereID != null)\r
- {\r
- client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);\r
- sphereID = UUID.Zero;\r
- }\r
-\r
- }\r
-\r
- void beamTimer_Elapsed(object sender, EventArgs e)\r
- {\r
- if (beamID == null) return;\r
-\r
- try\r
- {\r
- client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);\r
- int i = 0;\r
- for (i = 0; i < numBeans; i++)\r
- {\r
- UUID newBeam = UUID.Random();\r
- Vector3d scatter;\r
-\r
- if (i == 0)\r
- {\r
- scatter = GlobalPosition(targetPrim);\r
- }\r
- else\r
- {\r
- Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);\r
- Vector3d cross = direction % new Vector3d(0, 0, 1);\r
- cross.Normalize();\r
- scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);\r
- }\r
- client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);\r
- }\r
-\r
- for (int j = 1; j < numBeans; j++)\r
- {\r
- UUID newBeam = UUID.Random();\r
- Vector3d scatter;\r
- Vector3d cross = new Vector3d(0, 0, 1);\r
- cross.Normalize();\r
- scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);\r
-\r
- client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);\r
- }\r
- }\r
- catch (Exception) { };\r
-\r
- }\r
-\r
- public void SetPointing(Primitive prim, int numBeans)\r
- {\r
- UnSetPointing();\r
- client.Self.Movement.TurnToward(prim.Position);\r
- pointID = UUID.Random();\r
- sphereID = UUID.Random();\r
- beamID = new List<UUID>();\r
- beamTarget = new List<Vector3d>();\r
- targetPrim = prim;\r
- this.numBeans = numBeans;\r
-\r
- client.Self.PointAtEffect(effectSource, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);\r
-\r
- for (int i = 0; i < numBeans; i++)\r
- {\r
- UUID newBeam = UUID.Random();\r
- beamID.Add(newBeam);\r
- beamTarget.Add(Vector3d.Zero);\r
- }\r
-\r
- for (int i = 1; i < numBeans; i++)\r
- {\r
- UUID newBeam = UUID.Random();\r
- beamID.Add(newBeam);\r
- beamTarget.Add(Vector3d.Zero);\r
- }\r
-\r
- beamTimer.Interval = 1000;\r
- beamTimer.Enabled = true;\r
- }\r
-\r
- public UUID TypingAnimationID\r
- {\r
- get { return typingAnimationID; }\r
- set { typingAnimationID = value; }\r
- }\r
- \r
- public UUID AwayAnimationID\r
- {\r
- get { return awayAnimationID; }\r
- set { awayAnimationID = value; }\r
- }\r
-\r
- public UUID BusyAnimationID\r
- {\r
- get { return busyAnimationID; }\r
- set { busyAnimationID = value; }\r
- }\r
-\r
- public UUID EffectSource\r
- {\r
- get { return effectSource; }\r
- set { effectSource = value; }\r
- }\r
-\r
- public bool IsTyping\r
- {\r
- get { return typing; }\r
- }\r
-\r
- public bool IsAway\r
- {\r
- get { return away; }\r
- }\r
-\r
- public bool IsBusy\r
- {\r
- get { return busy; }\r
- }\r
-\r
- public bool IsFlying\r
- {\r
- get { return flying; }\r
- }\r
-\r
- public bool IsSitting\r
- {\r
- get { return sitting; }\r
- }\r
-\r
- public bool IsPointing\r
- {\r
- get { return pointID != UUID.Zero; }\r
- }\r
-\r
- public bool IsFollowing\r
- {\r
- get { return following; }\r
- }\r
-\r
- public string FollowName\r
- {\r
- get { return followName; }\r
- set { followName = value; }\r
- }\r
-\r
- public float FollowDistance\r
- {\r
- get { return followDistance; }\r
- set { followDistance = value; }\r
- }\r
-\r
- public bool IsWalking\r
- {\r
- get { return walking; }\r
- }\r
- }\r
-}\r
+//
+// Radegast Metaverse Client
+// Copyright (c) 2009-2014, Radegast Development Team
+// All rights reserved.
+//
+// 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 application "Radegast", 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR 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.
+//
+// $Id$
+//
+using System;
+using System.Collections.Generic;
+using System.Timers;
+using System.Threading;
+
+using OpenMetaverse;
+
+using Radegast.Automation;
+using Radegast.Netcom;
+
+namespace Radegast
+{
+ public class KnownHeading
+ {
+ public string ID { get; set; }
+ public string Name { get; set; }
+ public Quaternion Heading { get; set; }
+
+ public KnownHeading(string id, string name, Quaternion heading)
+ {
+ this.ID = id;
+ this.Name = name;
+ this.Heading = heading;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+
+ public class StateManager : IDisposable
+ {
+ public Parcel Parcel { get; set; }
+
+ private RadegastInstance instance;
+ private GridClient client { get { return instance.Client; } }
+ private RadegastNetcom netcom { get { return instance.Netcom; } }
+
+ private bool typing = false;
+ private bool away = false;
+ private bool busy = false;
+ private bool flying = false;
+ private bool alwaysrun = false;
+ private bool sitting = false;
+
+ private bool following = false;
+ private string followName = string.Empty;
+ private float followDistance = 3.0f;
+ private UUID followID;
+ private bool displayEndWalk = false;
+
+ private UUID awayAnimationID = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");
+ private UUID busyAnimationID = new UUID("efcf670c2d188128973a034ebc806b67");
+ private UUID typingAnimationID = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
+ internal static Random rnd = new Random();
+ private System.Threading.Timer lookAtTimer;
+
+ public float FOVVerticalAngle = Utils.TWO_PI - 0.05f;
+
+ /// <summary>
+ /// Passes walk state
+ /// </summary>
+ /// <param name="walking">True if we are walking towards a targer</param>
+ public delegate void WalkStateCanged(bool walking);
+
+ /// <summary>
+ /// Fires when we start or stop walking towards a target
+ /// </summary>
+ public event WalkStateCanged OnWalkStateCanged;
+
+ /// <summary>
+ /// Fires when avatar stands
+ /// </summary>
+ public event EventHandler<SitEventArgs> SitStateChanged;
+
+ static List<KnownHeading> m_Headings;
+ public static List<KnownHeading> KnownHeadings
+ {
+ get
+ {
+ if (m_Headings == null)
+ {
+ m_Headings = new List<KnownHeading>(16);
+ m_Headings.Add(new KnownHeading("E", "East", new Quaternion(0.00000f, 0.00000f, 0.00000f, 1.00000f)));
+ m_Headings.Add(new KnownHeading("ENE", "East by Northeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, 0.98079f)));
+ m_Headings.Add(new KnownHeading("NE", "Northeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, 0.92388f)));
+ m_Headings.Add(new KnownHeading("NNE", "North by Northeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, 0.83147f)));
+ m_Headings.Add(new KnownHeading("N", "North", new Quaternion(0.00000f, 0.00000f, 0.70711f, 0.70711f)));
+ m_Headings.Add(new KnownHeading("NNW", "North by Northwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, 0.55557f)));
+ m_Headings.Add(new KnownHeading("NW", "Nortwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, 0.38268f)));
+ m_Headings.Add(new KnownHeading("WNW", "West by Northwest", new Quaternion(0.00000f, 0.00000f, 0.98079f, 0.19509f)));
+ m_Headings.Add(new KnownHeading("W", "West", new Quaternion(0.00000f, 0.00000f, 1.00000f, -0.00000f)));
+ m_Headings.Add(new KnownHeading("WSW", "West by Southwest", new Quaternion(0.00000f, 0.00000f, 0.98078f, -0.19509f)));
+ m_Headings.Add(new KnownHeading("SW", "Southwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, -0.38268f)));
+ m_Headings.Add(new KnownHeading("SSW", "South by Southwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, -0.55557f)));
+ m_Headings.Add(new KnownHeading("S", "South", new Quaternion(0.00000f, 0.00000f, 0.70711f, -0.70711f)));
+ m_Headings.Add(new KnownHeading("SSE", "South by Southeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, -0.83147f)));
+ m_Headings.Add(new KnownHeading("SE", "Southeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, -0.92388f)));
+ m_Headings.Add(new KnownHeading("ESE", "East by Southeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, -0.98078f)));
+ }
+ return m_Headings;
+ }
+ }
+
+ public static Vector3 RotToEuler(Quaternion r)
+ {
+ Quaternion t = new Quaternion(r.X * r.X, r.Y * r.Y, r.Z * r.Z, r.W * r.W);
+
+ float m = (t.X + t.Y + t.Z + t.W);
+ if (Math.Abs(m) < 0.001) return Vector3.Zero;
+ float n = 2 * (r.Y * r.W + r.X * r.Z);
+ float p = m * m - n * n;
+
+ if (p > 0)
+ return new Vector3(
+ (float)Math.Atan2(2.0 * (r.X * r.W - r.Y * r.Z), (-t.X - t.Y + t.Z + t.W)),
+ (float)Math.Atan2(n, Math.Sqrt(p)),
+ (float)Math.Atan2(2.0 * (r.Z * r.W - r.X * r.Y), t.X - t.Y - t.Z + t.W)
+ );
+ else if (n > 0)
+ return new Vector3(
+ 0f,
+ (float)(Math.PI / 2d),
+ (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Y)
+ );
+ else
+ return new Vector3(
+ 0f,
+ -(float)(Math.PI / 2d),
+ (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z)
+ );
+ }
+
+ public static KnownHeading ClosestKnownHeading(int degrees)
+ {
+ KnownHeading ret = KnownHeadings[0];
+ int facing = (int)(57.2957795d * RotToEuler(KnownHeadings[0].Heading).Z);
+ if (facing < 0) facing += 360;
+ int minDistance = Math.Abs(degrees - facing);
+
+ for (int i = 1; i < KnownHeadings.Count; i++)
+ {
+ facing = (int)(57.2957795d * RotToEuler(KnownHeadings[i].Heading).Z);
+ if (facing < 0) facing += 360;
+
+ int distance = Math.Abs(degrees - facing);
+ if (distance < minDistance)
+ {
+ ret = KnownHeadings[i];
+ minDistance = distance;
+ }
+ }
+
+ return ret;
+ }
+
+ public Dictionary<UUID, string> KnownAnimations;
+ public bool CameraTracksOwnAvatar = true;
+ public Vector3 DefaultCameraOffset = new Vector3(-5, 0, 0);
+
+ public StateManager(RadegastInstance instance)
+ {
+ this.instance = instance;
+ this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
+ KnownAnimations = Animations.ToDictionary();
+ autosit = new AutoSit(this.instance);
+ pseudohome = new PseudoHome(this.instance);
+ lslHelper = new LSLHelper(this.instance);
+
+ beamTimer = new System.Timers.Timer();
+ beamTimer.Enabled = false;
+ beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);
+
+ // Callbacks
+ netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);
+ netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
+ netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);
+ RegisterClientEvents(client);
+ }
+
+
+ private void RegisterClientEvents(GridClient client)
+ {
+ client.Objects.AvatarUpdate += new EventHandler<AvatarUpdateEventArgs>(Objects_AvatarUpdate);
+ client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
+ client.Objects.AvatarSitChanged += new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
+ client.Self.AlertMessage += new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
+ client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
+ client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
+ client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);
+ }
+
+ private void UnregisterClientEvents(GridClient client)
+ {
+ client.Objects.AvatarUpdate -= new EventHandler<AvatarUpdateEventArgs>(Objects_AvatarUpdate);
+ client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
+ client.Objects.AvatarSitChanged -= new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
+ client.Self.AlertMessage -= new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
+ client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
+ client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
+ client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);
+ }
+
+ public void Dispose()
+ {
+ netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);
+ netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
+ netcom.ChatReceived -= new EventHandler<ChatEventArgs>(netcom_ChatReceived);
+ UnregisterClientEvents(client);
+ beamTimer.Dispose();
+ beamTimer = null;
+
+ if (lookAtTimer != null)
+ {
+ lookAtTimer.Dispose();
+ lookAtTimer = null;
+ }
+
+ if (walkTimer != null)
+ {
+ walkTimer.Dispose();
+ walkTimer = null;
+ }
+
+ if (autosit != null)
+ {
+ autosit.Dispose();
+ autosit = null;
+ }
+
+ if (lslHelper == null)
+ {
+ lslHelper.Dispose();
+ lslHelper = null;
+ }
+ }
+
+ void instance_ClientChanged(object sender, ClientChangedEventArgs e)
+ {
+ UnregisterClientEvents(e.OldClient);
+ RegisterClientEvents(client);
+ }
+
+ void Objects_AvatarSitChanged(object sender, AvatarSitChangedEventArgs e)
+ {
+ if (e.Avatar.LocalID != client.Self.LocalID) return;
+
+ sitting = e.SittingOn != 0;
+
+ if (client.Self.SittingOn != 0 && !client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(client.Self.SittingOn))
+ {
+ client.Objects.RequestObject(client.Network.CurrentSim, client.Self.SittingOn);
+ }
+
+ if (SitStateChanged != null)
+ {
+ SitStateChanged(this, new SitEventArgs(this.sitting));
+ }
+ }
+
+ /// <summary>
+ /// Locates avatar in the current sim, or adjacents sims
+ /// </summary>
+ /// <param name="person">Avatar UUID</param>
+ /// <param name="position">Position within sim</param>
+ /// <returns>True if managed to find the avatar</returns>
+ public bool TryFindAvatar(UUID person, out Vector3 position)
+ {
+ Simulator sim;
+ if (!TryFindAvatar(person, out sim, out position)) return false;
+ // same sim?
+ if (sim == client.Network.CurrentSim) return true;
+ position = ToLocalPosition(sim.Handle, position);
+ return true;
+ }
+
+ public Vector3 ToLocalPosition(ulong handle, Vector3 position)
+ {
+ Vector3d diff = ToVector3D(handle, position) - client.Self.GlobalPosition;
+ position = new Vector3((float) diff.X, (float) diff.Y, (float) diff.Z) - position;
+ return position;
+ }
+
+ public static Vector3d ToVector3D(ulong handle, Vector3 pos)
+ {
+ uint globalX, globalY;
+ Utils.LongToUInts(handle, out globalX, out globalY);
+
+ return new Vector3d(
+ (double)globalX + (double)pos.X,
+ (double)globalY + (double)pos.Y,
+ (double)pos.Z);
+ }
+
+ /// <summary>
+ /// Locates avatar in the current sim, or adjacents sims
+ /// </summary>
+ /// <param name="person">Avatar UUID</param>
+ /// <param name="sim">Simulator avatar is in</param>
+ /// <param name="position">Position within sim</param>
+ /// <returns>True if managed to find the avatar</returns>
+ public bool TryFindAvatar(UUID person, out Simulator sim, out Vector3 position)
+ {
+ return TryFindPrim(person, out sim, out position, true);
+ }
+ public bool TryFindPrim(UUID person, out Simulator sim, out Vector3 position, bool onlyAvatars)
+ {
+ Simulator[] Simulators = null;
+ lock (client.Network.Simulators)
+ {
+ Simulators = client.Network.Simulators.ToArray();
+ }
+ sim = null;
+ position = Vector3.Zero;
+
+ Primitive avi = null;
+
+ // First try the object tracker
+ foreach (var s in Simulators)
+ {
+ avi = s.ObjectsAvatars.Find((Avatar av) => { return av.ID == person; });
+ if (avi != null)
+ {
+ sim = s;
+ break;
+ }
+ }
+ if (avi == null && !onlyAvatars)
+ {
+ foreach (var s in Simulators)
+ {
+ avi = s.ObjectsPrimitives.Find((Primitive av) => { return av.ID == person; });
+ if (avi != null)
+ {
+ sim = s;
+ break;
+ }
+ }
+ }
+ if (avi != null)
+ {
+ if (avi.ParentID == 0)
+ {
+ position = avi.Position;
+ }
+ else
+ {
+ Primitive seat;
+ if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
+ {
+ position = seat.Position + avi.Position * seat.Rotation;
+ }
+ }
+ }
+ else
+ {
+ foreach (var s in Simulators)
+ {
+ if (s.AvatarPositions.ContainsKey(person))
+ {
+ position = s.AvatarPositions[person];
+ sim = s;
+ break;
+ }
+ }
+ }
+
+ if (position.Z > 0.1f)
+ return true;
+ else
+ return false;
+ }
+
+ public bool TryLocatePrim(Primitive avi, out Simulator sim, out Vector3 position)
+ {
+ Simulator[] Simulators = null;
+ lock (client.Network.Simulators)
+ {
+ Simulators = client.Network.Simulators.ToArray();
+ }
+
+ sim = client.Network.CurrentSim;
+ position = Vector3.Zero;
+ {
+ foreach (var s in Simulators)
+ {
+ if (s.Handle == avi.RegionHandle)
+ {
+ sim = s;
+ break;
+ }
+ }
+ }
+ if (avi != null)
+ {
+ if (avi.ParentID == 0)
+ {
+ position = avi.Position;
+ }
+ else
+ {
+ Primitive seat;
+ if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
+ {
+ position = seat.Position + avi.Position*seat.Rotation;
+ }
+ }
+ }
+ if (position.Z > 0.1f)
+ return true;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Move to target position either by walking or by teleporting
+ /// </summary>
+ /// <param name="target">Sim local position of the target</param>
+ /// <param name="useTP">Move using teleport</param>
+ public void MoveTo(Vector3 target, bool useTP)
+ {
+ MoveTo(client.Network.CurrentSim, target, useTP);
+ }
+
+ /// <summary>
+ /// Move to target position either by walking or by teleporting
+ /// </summary>
+ /// <param name="sim">Simulator in which the target is</param>
+ /// <param name="target">Sim local position of the target</param>
+ /// <param name="useTP">Move using teleport</param>
+ public void MoveTo(Simulator sim, Vector3 target, bool useTP)
+ {
+ SetSitting(false, UUID.Zero);
+
+ if (useTP)
+ {
+ client.Self.RequestTeleport(sim.Handle, target);
+ }
+ else
+ {
+ displayEndWalk = true;
+ client.Self.Movement.TurnToward(target);
+ WalkTo(GlobalPosition(sim, target));
+ }
+ }
+
+
+ public void SetRandomHeading()
+ {
+ client.Self.Movement.UpdateFromHeading(Utils.TWO_PI * rnd.NextDouble(), true);
+ LookInFront();
+ }
+
+ void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
+ {
+ if (e.Simulator == client.Network.CurrentSim)
+ {
+ SetRandomHeading();
+ }
+ }
+
+ void Network_SimChanged(object sender, SimChangedEventArgs e)
+ {
+ WorkPool.QueueUserWorkItem(sync =>
+ {
+ Thread.Sleep(15 * 1000);
+ autosit.TrySit();
+ pseudohome.ETGoHome();
+ });
+ client.Self.Movement.SetFOVVerticalAngle(FOVVerticalAngle);
+ }
+
+ private UUID teleportEffect = UUID.Random();
+
+ void Self_TeleportProgress(object sender, TeleportEventArgs e)
+ {
+ if (!client.Network.Connected) return;
+
+ if (e.Status == TeleportStatus.Progress)
+ {
+ client.Self.SphereEffect(client.Self.GlobalPosition, Color4.White, 4f, teleportEffect);
+ }
+
+ if (e.Status == TeleportStatus.Finished)
+ {
+ client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
+ SetRandomHeading();
+ }
+
+ if (e.Status == TeleportStatus.Failed)
+ {
+ client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
+ }
+ }
+
+ void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
+ {
+ typing = away = busy = walking = false;
+
+ if (lookAtTimer != null)
+ {
+ lookAtTimer.Dispose();
+ lookAtTimer = null;
+ }
+
+ }
+
+ void netcom_ClientConnected(object sender, EventArgs e)
+ {
+ if (!instance.GlobalSettings.ContainsKey("draw_distance"))
+ {
+ instance.GlobalSettings["draw_distance"] = 48;
+ }
+
+ client.Self.Movement.Camera.Far = instance.GlobalSettings["draw_distance"];
+
+ if (lookAtTimer == null)
+ {
+ lookAtTimer = new System.Threading.Timer(new TimerCallback(lookAtTimerTick), null, Timeout.Infinite, Timeout.Infinite);
+ }
+ }
+
+ void Objects_AvatarUpdate(object sender, AvatarUpdateEventArgs e)
+ {
+ if (e.Avatar.LocalID == client.Self.LocalID)
+ {
+ SetDefaultCamera();
+ }
+ }
+
+ void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
+ {
+ if (!e.Update.Avatar) return;
+
+ if (e.Prim.LocalID == client.Self.LocalID)
+ {
+ SetDefaultCamera();
+ }
+
+ if (!following) return;
+
+ Avatar av;
+ client.Network.CurrentSim.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av);
+ if (av == null) return;
+
+ if (av.ID == followID)
+ {
+ Vector3 pos = AvatarPosition(client.Network.CurrentSim, av);
+
+ FollowUpdate(pos);
+ }
+ }
+
+ void FollowUpdate(Vector3 pos)
+ {
+ if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)
+ {
+ Vector3 target = pos + Vector3.Normalize(client.Self.SimPosition - pos) * (followDistance - 1f);
+ client.Self.AutoPilotCancel();
+ Vector3d glb = GlobalPosition(client.Network.CurrentSim, target);
+ client.Self.AutoPilot(glb.X, glb.Y, glb.Z);
+ }
+ else
+ {
+ client.Self.AutoPilotCancel();
+ client.Self.Movement.TurnToward(pos);
+ }
+ }
+
+ public void SetDefaultCamera()
+ {
+ if (CameraTracksOwnAvatar)
+ {
+ if (client.Self.SittingOn != 0 && !client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(client.Self.SittingOn))
+ {
+ // We are sitting but don't have the information about the object we are sitting on
+ // Sim seems to ignore RequestMutlipleObjects message
+ // client.Objects.RequestObject(client.Network.CurrentSim, client.Self.SittingOn);
+ }
+ else
+ {
+ Vector3 pos = client.Self.SimPosition + DefaultCameraOffset * client.Self.Movement.BodyRotation;
+ //Logger.Log("Setting camera position to " + pos.ToString(), Helpers.LogLevel.Debug);
+ client.Self.Movement.Camera.LookAt(
+ pos,
+ client.Self.SimPosition
+ );
+ }
+ }
+ }
+
+ public Quaternion AvatarRotation(Simulator sim, UUID avID)
+ {
+ Quaternion rot = Quaternion.Identity;
+ Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
+
+ if (av == null)
+ return rot;
+
+ if (av.ParentID == 0)
+ {
+ rot = av.Rotation;
+ }
+ else
+ {
+ Primitive prim;
+ if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
+ {
+ rot = prim.Rotation + av.Rotation;
+ }
+ }
+
+ return rot;
+ }
+
+
+ public Vector3 AvatarPosition(Simulator sim, UUID avID)
+ {
+ Vector3 pos = Vector3.Zero;
+ Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
+ if (av != null)
+ {
+ return AvatarPosition(sim, av);
+ }
+ else
+ {
+ Vector3 coarse;
+ if (sim.AvatarPositions.TryGetValue(avID, out coarse))
+ {
+ if (coarse.Z > 0.01)
+ return coarse;
+ }
+ }
+ return pos;
+ }
+
+ public Vector3 AvatarPosition(Simulator sim, Avatar av)
+ {
+ Vector3 pos = Vector3.Zero;
+
+ if (av.ParentID == 0)
+ {
+ pos = av.Position;
+ }
+ else
+ {
+ Primitive prim;
+ if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
+ {
+ pos = prim.Position + av.Position;
+ }
+ }
+
+ return pos;
+ }
+
+ public void Follow(string name, UUID id)
+ {
+ followName = name;
+ followID = id;
+ following = followID != UUID.Zero;
+
+ if (following)
+ {
+ walking = false;
+
+ Vector3 target = AvatarPosition(client.Network.CurrentSim, id);
+ if (Vector3.Zero != target)
+ {
+ client.Self.Movement.TurnToward(target);
+ FollowUpdate(target);
+ }
+
+ }
+ }
+
+ public void StopFollowing()
+ {
+ following = false;
+ followName = string.Empty;
+ followID = UUID.Zero;
+ }
+
+ #region Look at effect
+ private int lastLookAtEffect = 0;
+ private UUID lookAtEffect = UUID.Random();
+
+ /// <summary>
+ /// Set eye focus 3m in front of us
+ /// </summary>
+ public void LookInFront()
+ {
+ if (!client.Network.Connected || instance.GlobalSettings["disable_look_at"]) return;
+
+ client.Self.LookAtEffect(client.Self.AgentID, client.Self.AgentID,
+ new Vector3d(new Vector3(3, 0, 0) * Quaternion.Identity),
+ LookAtType.Idle, lookAtEffect);
+ }
+
+ void lookAtTimerTick(object state)
+ {
+ LookInFront();
+ }
+
+ void netcom_ChatReceived(object sender, ChatEventArgs e)
+ {
+ //somehow it can be too early (when Radegast is loaded from running bot)
+ if (instance.GlobalSettings==null) return;
+ if (!instance.GlobalSettings["disable_look_at"]
+ && e.SourceID != client.Self.AgentID
+ && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))
+ {
+ // change focus max every 4 seconds
+ if (Environment.TickCount - lastLookAtEffect > 4000)
+ {
+ lastLookAtEffect = Environment.TickCount;
+ client.Self.LookAtEffect(client.Self.AgentID, e.SourceID, Vector3d.Zero, LookAtType.Respond, lookAtEffect);
+ // keep looking at the speaker for 10 seconds
+ if (lookAtTimer != null)
+ {
+ lookAtTimer.Change(10000, Timeout.Infinite);
+ }
+ }
+ }
+ }
+ #endregion Look at effect
+
+ #region Walking (move to)
+ private bool walking = false;
+ private System.Threading.Timer walkTimer;
+ private int walkChekInterval = 500;
+ private Vector3d walkToTarget;
+ int lastDistance = 0;
+ int lastDistanceChanged = 0;
+
+ public void WalkTo(Primitive prim)
+ {
+ WalkTo(GlobalPosition(prim));
+ }
+ public double WaitUntilPosition(Vector3d pos, TimeSpan maxWait, double howClose)
+ {
+
+ DateTime until = DateTime.Now + maxWait;
+ while (until > DateTime.Now)
+ {
+ double dist = Vector3d.Distance(client.Self.GlobalPosition, pos);
+ if (howClose >= dist) return dist;
+ Thread.Sleep(250);
+ }
+ return Vector3d.Distance(client.Self.GlobalPosition, pos);
+
+ }
+
+ public void WalkTo(Vector3d globalPos)
+ {
+ walkToTarget = globalPos;
+
+ if (following)
+ {
+ following = false;
+ followName = string.Empty;
+ }
+
+ if (walkTimer == null)
+ {
+ walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);
+ }
+
+ lastDistanceChanged = System.Environment.TickCount;
+ client.Self.AutoPilotCancel();
+ walking = true;
+ client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);
+ FireWalkStateCanged();
+ }
+
+ void walkTimerElapsed(object sender)
+ {
+
+ double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);
+
+ if (distance < 2d)
+ {
+ // We're there
+ EndWalking();
+ }
+ else
+ {
+ if (lastDistance != (int)distance)
+ {
+ lastDistanceChanged = System.Environment.TickCount;
+ lastDistance = (int)distance;
+ }
+ else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)
+ {
+ // Our distance to the target has not changed in 10s, give up
+ EndWalking();
+ return;
+ }
+ if (walkTimer != null) walkTimer.Change(walkChekInterval, Timeout.Infinite);
+ }
+ }
+
+ void Self_AlertMessage(object sender, AlertMessageEventArgs e)
+ {
+ if (e.Message.Contains("Autopilot cancel"))
+ {
+ if (walking)
+ {
+ EndWalking();
+ }
+ }
+ }
+
+ void FireWalkStateCanged()
+ {
+ if (OnWalkStateCanged != null)
+ {
+ try { OnWalkStateCanged(walking); }
+ catch (Exception) { }
+ }
+ }
+
+ public void EndWalking()
+ {
+ if (walking)
+ {
+ walking = false;
+ Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);
+ walkTimer.Dispose();
+ walkTimer = null;
+ client.Self.AutoPilotCancel();
+
+ if (displayEndWalk)
+ {
+ displayEndWalk = false;
+ string msg = "Finished walking";
+
+ if (walkToTarget != Vector3d.Zero)
+ {
+ System.Threading.Thread.Sleep(1000);
+ msg += string.Format(" {0:0} meters from destination", Vector3d.Distance(client.Self.GlobalPosition, walkToTarget));
+ walkToTarget = Vector3d.Zero;
+ }
+
+ instance.TabConsole.DisplayNotificationInChat(msg);
+ }
+
+ FireWalkStateCanged();
+ }
+ }
+ #endregion
+
+ public void SetTyping(bool typing)
+ {
+ if (!client.Network.Connected) return;
+
+ Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();
+ typingAnim.Add(typingAnimationID, typing);
+
+ client.Self.Animate(typingAnim, false);
+
+ if (typing)
+ client.Self.Chat(string.Empty, 0, ChatType.StartTyping);
+ else
+ client.Self.Chat(string.Empty, 0, ChatType.StopTyping);
+
+ this.typing = typing;
+ }
+
+ public void SetAway(bool away)
+ {
+ Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();
+ awayAnim.Add(awayAnimationID, away);
+
+ client.Self.Animate(awayAnim, true);
+ if (UseMoveControl) client.Self.Movement.Away = away;
+ this.away = away;
+ }
+
+ public void SetBusy(bool busy)
+ {
+ Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();
+ busyAnim.Add(busyAnimationID, busy);
+
+ client.Self.Animate(busyAnim, true);
+ this.busy = busy;
+ }
+
+ public void SetFlying(bool flying)
+ {
+ this.flying = client.Self.Movement.Fly = flying;
+ }
+
+ public void SetAlwaysRun(bool alwaysrun)
+ {
+ this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;
+ }
+
+ public void SetSitting(bool sitting, UUID target)
+ {
+ this.sitting = sitting;
+
+ if (sitting)
+ {
+ client.Self.RequestSit(target, Vector3.Zero);
+ client.Self.Sit();
+ }
+ else
+ {
+ if (!instance.RLV.RestictionActive("unsit"))
+ {
+ client.Self.Stand();
+ }
+ else
+ {
+ instance.TabConsole.DisplayNotificationInChat("Unsit prevented by RLV");
+ this.sitting = true;
+ return;
+ }
+ }
+
+ if (SitStateChanged != null)
+ {
+ SitStateChanged(this, new SitEventArgs(this.sitting));
+ }
+
+ if (!this.sitting)
+ {
+ StopAllAnimations();
+ }
+ }
+
+ public void StopAllAnimations()
+ {
+ Dictionary<UUID, bool> stop = new Dictionary<UUID, bool>();
+
+ client.Self.SignaledAnimations.ForEach((UUID anim) =>
+ {
+ if (!KnownAnimations.ContainsKey(anim))
+ {
+ stop.Add(anim, false);
+ }
+ });
+
+ if (stop.Count > 0)
+ {
+ client.Self.Animate(stop, true);
+ }
+ }
+
+ static public Vector3d GlobalPosition(Simulator sim, Vector3 pos)
+ {
+ uint globalX, globalY;
+ Utils.LongToUInts(sim.Handle, out globalX, out globalY);
+
+ return new Vector3d(
+ (double)globalX + (double)pos.X,
+ (double)globalY + (double)pos.Y,
+ (double)pos.Z);
+ }
+
+ public Vector3d GlobalPosition(Primitive prim)
+ {
+ return GlobalPosition(client.Network.CurrentSim, prim.Position);
+ }
+
+ private System.Timers.Timer beamTimer;
+ private List<Vector3d> beamTarget;
+ private Random beamRandom = new Random();
+ private UUID pointID;
+ private UUID sphereID;
+ private List<UUID> beamID;
+ private int numBeans;
+ private Color4[] beamColors = new Color4[3] { new Color4(0, 255, 0, 255), new Color4(255, 0, 0, 255), new Color4(0, 0, 255, 255) };
+ private Primitive targetPrim;
+
+ public void UnSetPointing()
+ {
+ beamTimer.Enabled = false;
+ if (pointID != UUID.Zero)
+ {
+ client.Self.PointAtEffect(client.Self.AgentID, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);
+ pointID = UUID.Zero;
+ }
+
+ if (beamID != null)
+ {
+ foreach (UUID id in beamID)
+ {
+ client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);
+ }
+ beamID = null;
+ }
+
+ if (sphereID != UUID.Zero)
+ {
+ client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);
+ sphereID = UUID.Zero;
+ }
+
+ }
+
+ void beamTimer_Elapsed(object sender, EventArgs e)
+ {
+ if (beamID == null) return;
+
+ try
+ {
+ client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);
+ int i = 0;
+ for (i = 0; i < numBeans; i++)
+ {
+ UUID newBeam = UUID.Random();
+ Vector3d scatter;
+
+ if (i == 0)
+ {
+ scatter = GlobalPosition(targetPrim);
+ }
+ else
+ {
+ Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);
+ Vector3d cross = direction % new Vector3d(0, 0, 1);
+ cross.Normalize();
+ scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);
+ }
+ client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);
+ }
+
+ for (int j = 1; j < numBeans; j++)
+ {
+ UUID newBeam = UUID.Random();
+ Vector3d scatter;
+ Vector3d cross = new Vector3d(0, 0, 1);
+ cross.Normalize();
+ scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);
+
+ client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);
+ }
+ }
+ catch (Exception) { };
+
+ }
+
+ public void SetPointing(Primitive prim, int numBeans)
+ {
+ UnSetPointing();
+ client.Self.Movement.TurnToward(prim.Position);
+ pointID = UUID.Random();
+ sphereID = UUID.Random();
+ beamID = new List<UUID>();
+ beamTarget = new List<Vector3d>();
+ targetPrim = prim;
+ this.numBeans = numBeans;
+
+ client.Self.PointAtEffect(client.Self.AgentID, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);
+
+ for (int i = 0; i < numBeans; i++)
+ {
+ UUID newBeam = UUID.Random();
+ beamID.Add(newBeam);
+ beamTarget.Add(Vector3d.Zero);
+ }
+
+ for (int i = 1; i < numBeans; i++)
+ {
+ UUID newBeam = UUID.Random();
+ beamID.Add(newBeam);
+ beamTarget.Add(Vector3d.Zero);
+ }
+
+ beamTimer.Interval = 1000;
+ beamTimer.Enabled = true;
+ }
+
+ public UUID TypingAnimationID
+ {
+ get { return typingAnimationID; }
+ set { typingAnimationID = value; }
+ }
+
+ public UUID AwayAnimationID
+ {
+ get { return awayAnimationID; }
+ set { awayAnimationID = value; }
+ }
+
+ public UUID BusyAnimationID
+ {
+ get { return busyAnimationID; }
+ set { busyAnimationID = value; }
+ }
+
+ public bool IsTyping
+ {
+ get { return typing; }
+ }
+
+ public bool IsAway
+ {
+ get
+ {
+ if (UseMoveControl) return client.Self.Movement.Away;
+ return away;
+ }
+ }
+
+ public bool IsBusy
+ {
+ get { return busy; }
+ }
+
+ public bool IsFlying
+ {
+ get { return client.Self.Movement.Fly; }
+ }
+
+ public bool IsSitting
+ {
+ get
+ {
+ if (client.Self.Movement.SitOnGround || client.Self.SittingOn != 0) return true;
+ if (sitting) {
+ Logger.Log("out of sync sitting", Helpers.LogLevel.Debug);
+ sitting = false;
+ }
+ return false;
+ }
+ }
+
+ public bool IsPointing
+ {
+ get { return pointID != UUID.Zero; }
+ }
+
+ public bool IsFollowing
+ {
+ get { return following; }
+ }
+
+ public string FollowName
+ {
+ get { return followName; }
+ set { followName = value; }
+ }
+
+ public float FollowDistance
+ {
+ get { return followDistance; }
+ set { followDistance = value; }
+ }
+
+ public bool IsWalking
+ {
+ get { return walking; }
+ }
+
+ private AutoSit autosit;
+ public AutoSit AutoSit
+ {
+ get { return autosit; }
+ }
+
+ private LSLHelper lslHelper;
+ public LSLHelper LSLHelper
+ {
+ get { return lslHelper; }
+ }
+
+ private PseudoHome pseudohome;
+
+ /// <summary>
+ /// Experimental Option that sometimes the Client has more authority than state mananger
+ /// </summary>
+ public static bool UseMoveControl;
+
+ public PseudoHome PseudoHome
+ {
+ get { return pseudohome; }
+ }
+ }
+
+ public class SitEventArgs : EventArgs
+ {
+ public bool Sitting;
+
+ public SitEventArgs(bool sitting)
+ {
+ this.Sitting = sitting;
+ }
+ }
+}