// // 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 Raindrop.Netcom; namespace Raindrop { 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 RaindropInstance instance; private GridClient client { get { return instance.Client; } } private RaindropNetcom 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 = OpenMetaverse.Utils.TWO_PI - 0.05f; /// /// Passes walk state /// /// True if we are walking towards a targer public delegate void WalkStateCanged(bool walking); /// /// Fires when we start or stop walking towards a target /// public event WalkStateCanged OnWalkStateCanged; /// /// Fires when avatar stands /// public event EventHandler SitStateChanged; static List m_Headings; public static List KnownHeadings { get { if (m_Headings == null) { m_Headings = new List(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 KnownAnimations; public bool CameraTracksOwnAvatar = true; public Vector3 DefaultCameraOffset = new Vector3(-5, 0, 0); public StateManager(RaindropInstance instance) { this.instance = instance; this.instance.ClientChanged += new EventHandler(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(netcom_ClientConnected); netcom.ClientDisconnected += new EventHandler(netcom_ClientDisconnected); netcom.ChatReceived += new EventHandler(netcom_ChatReceived); RegisterClientEvents(client); } private void RegisterClientEvents(GridClient client) { client.Objects.AvatarUpdate += new EventHandler(Objects_AvatarUpdate); client.Objects.TerseObjectUpdate += new EventHandler(Objects_TerseObjectUpdate); client.Objects.AvatarSitChanged += new EventHandler(Objects_AvatarSitChanged); client.Self.AlertMessage += new EventHandler(Self_AlertMessage); client.Self.TeleportProgress += new EventHandler(Self_TeleportProgress); client.Network.EventQueueRunning += new EventHandler(Network_EventQueueRunning); client.Network.SimChanged += new EventHandler(Network_SimChanged); } private void UnregisterClientEvents(GridClient client) { client.Objects.AvatarUpdate -= new EventHandler(Objects_AvatarUpdate); client.Objects.TerseObjectUpdate -= new EventHandler(Objects_TerseObjectUpdate); client.Objects.AvatarSitChanged -= new EventHandler(Objects_AvatarSitChanged); client.Self.AlertMessage -= new EventHandler(Self_AlertMessage); client.Self.TeleportProgress -= new EventHandler(Self_TeleportProgress); client.Network.EventQueueRunning -= new EventHandler(Network_EventQueueRunning); client.Network.SimChanged -= new EventHandler(Network_SimChanged); } public void Dispose() { netcom.ClientConnected -= new EventHandler(netcom_ClientConnected); netcom.ClientDisconnected -= new EventHandler(netcom_ClientDisconnected); netcom.ChatReceived -= new EventHandler(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)); } } /// /// Locates avatar in the current sim, or adjacents sims /// /// Avatar UUID /// Position within sim /// True if managed to find the avatar 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; OpenMetaverse.Utils.LongToUInts(handle, out globalX, out globalY); return new Vector3d( (double)globalX + (double)pos.X, (double)globalY + (double)pos.Y, (double)pos.Z); } /// /// Locates avatar in the current sim, or adjacents sims /// /// Avatar UUID /// Simulator avatar is in /// Position within sim /// True if managed to find the avatar 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; } /// /// Move to target position either by walking or by teleporting /// /// Sim local position of the target /// Move using teleport public void MoveTo(Vector3 target, bool useTP) { MoveTo(client.Network.CurrentSim, target, useTP); } /// /// Move to target position either by walking or by teleporting /// /// Simulator in which the target is /// Sim local position of the target /// Move using teleport 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(OpenMetaverse.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) { //ThreadPool.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) { instance.MediaManager.PlayUISound(UISounds.Teleport); 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) { instance.MediaManager.PlayUISound(UISounds.Error); 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 (true /*!instance.GlobalSettings.ContainsKey("draw_distance")*/) //todo { instance.GlobalSettings["draw_distance"] = 20; } 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(); /// /// Set eye focus 3m in front of us /// 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 typingAnim = new Dictionary(); 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 awayAnim = new Dictionary(); 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 busyAnim = new Dictionary(); 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 stop = new Dictionary(); 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; OpenMetaverse.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 beamTarget; private Random beamRandom = new Random(); private UUID pointID; private UUID sphereID; private List 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(); beamTarget = new List(); 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; /// /// Experimental Option that sometimes the Client has more authority than state mananger /// public static bool UseMoveControl; //public PseudoHome PseudoHome //{ // get { return pseudohome; } //} } public class SitEventArgs : EventArgs { public bool Sitting; public SitEventArgs(bool sitting) { this.Sitting = sitting; } } }