2 // Radegast Metaverse Client
\r
3 // Copyright (c) 2009-2012, Radegast Development Team
\r
4 // All rights reserved.
\r
6 // Redistribution and use in source and binary forms, with or without
\r
7 // modification, are permitted provided that the following conditions are met:
\r
9 // * Redistributions of source code must retain the above copyright notice,
\r
10 // this list of conditions and the following disclaimer.
\r
11 // * Redistributions in binary form must reproduce the above copyright
\r
12 // notice, this list of conditions and the following disclaimer in the
\r
13 // documentation and/or other materials provided with the distribution.
\r
14 // * Neither the name of the application "Radegast", nor the names of its
\r
15 // contributors may be used to endorse or promote products derived from
\r
16 // this software without specific prior written permission.
\r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
32 using System.Collections.Generic;
\r
33 using System.Timers;
\r
34 using System.Threading;
\r
36 using OpenMetaverse;
\r
39 using Radegast.Netcom;
\r
43 public class KnownHeading
\r
45 public string ID { get; set; }
\r
46 public string Name { get; set; }
\r
47 public Quaternion Heading { get; set; }
\r
48 public KnownHeading(string id, string name, Quaternion heading)
\r
52 this.Heading = heading;
\r
55 public override string ToString()
\r
61 public class StateManager : IDisposable
\r
63 public Parcel Parcel { get; set; }
\r
65 private RadegastInstance instance;
\r
66 private GridClient client { get { return instance.Client; } }
\r
67 private RadegastNetcom netcom { get { return instance.Netcom; } }
\r
69 private bool typing = false;
\r
70 private bool away = false;
\r
71 private bool busy = false;
\r
72 private bool flying = false;
\r
73 private bool alwaysrun = false;
\r
74 private bool sitting = false;
\r
76 private bool following = false;
\r
77 private string followName = string.Empty;
\r
78 private float followDistance = 3.0f;
\r
79 private UUID followID;
\r
80 private bool displayEndWalk = false;
\r
82 private UUID awayAnimationID = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");
\r
83 private UUID busyAnimationID = new UUID("efcf670c2d188128973a034ebc806b67");
\r
84 private UUID typingAnimationID = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
\r
85 internal static Random rnd = new Random();
\r
86 private System.Threading.Timer lookAtTimer;
\r
89 /// Passes walk state
\r
91 /// <param name="walking">True if we are walking towards a targer</param>
\r
92 public delegate void WalkStateCanged(bool walking);
\r
95 /// Fires when we start or stop walking towards a target
\r
97 public event WalkStateCanged OnWalkStateCanged;
\r
100 /// Fires when avatar stands
\r
102 public event EventHandler<SitEventArgs> SitStateChanged;
\r
104 static List<KnownHeading> m_Headings;
\r
105 public static List<KnownHeading> KnownHeadings
\r
109 if (m_Headings == null)
\r
111 m_Headings = new List<KnownHeading>(16);
\r
112 m_Headings.Add(new KnownHeading("E", "East", new Quaternion(0.00000f, 0.00000f, 0.00000f, 1.00000f)));
\r
113 m_Headings.Add(new KnownHeading("ENE", "East by Northeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, 0.98079f)));
\r
114 m_Headings.Add(new KnownHeading("NE", "Northeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, 0.92388f)));
\r
115 m_Headings.Add(new KnownHeading("NNE", "North by Northeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, 0.83147f)));
\r
116 m_Headings.Add(new KnownHeading("N", "North", new Quaternion(0.00000f, 0.00000f, 0.70711f, 0.70711f)));
\r
117 m_Headings.Add(new KnownHeading("NNW", "North by Northwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, 0.55557f)));
\r
118 m_Headings.Add(new KnownHeading("NW", "Nortwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, 0.38268f)));
\r
119 m_Headings.Add(new KnownHeading("WNW", "West by Northwest", new Quaternion(0.00000f, 0.00000f, 0.98079f, 0.19509f)));
\r
120 m_Headings.Add(new KnownHeading("W", "West", new Quaternion(0.00000f, 0.00000f, 1.00000f, -0.00000f)));
\r
121 m_Headings.Add(new KnownHeading("WSW", "West by Southwest", new Quaternion(0.00000f, 0.00000f, 0.98078f, -0.19509f)));
\r
122 m_Headings.Add(new KnownHeading("SW", "Southwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, -0.38268f)));
\r
123 m_Headings.Add(new KnownHeading("SSW", "South by Southwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, -0.55557f)));
\r
124 m_Headings.Add(new KnownHeading("S", "South", new Quaternion(0.00000f, 0.00000f, 0.70711f, -0.70711f)));
\r
125 m_Headings.Add(new KnownHeading("SSE", "South by Southeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, -0.83147f)));
\r
126 m_Headings.Add(new KnownHeading("SE", "Southeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, -0.92388f)));
\r
127 m_Headings.Add(new KnownHeading("ESE", "East by Southeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, -0.98078f)));
\r
133 public static Vector3 RotToEuler(Quaternion r)
\r
135 Quaternion t = new Quaternion(r.X * r.X, r.Y * r.Y, r.Z * r.Z, r.W * r.W);
\r
137 float m = (t.X + t.Y + t.Z + t.W);
\r
138 if (Math.Abs(m) < 0.001) return Vector3.Zero;
\r
139 float n = 2 * (r.Y * r.W + r.X * r.Z);
\r
140 float p = m * m - n * n;
\r
143 return new Vector3(
\r
144 (float)Math.Atan2(2.0 * (r.X * r.W - r.Y * r.Z), (-t.X - t.Y + t.Z + t.W)),
\r
145 (float)Math.Atan2(n, Math.Sqrt(p)),
\r
146 (float)Math.Atan2(2.0 * (r.Z * r.W - r.X * r.Y), t.X - t.Y - t.Z + t.W)
\r
149 return new Vector3(
\r
151 (float)(Math.PI / 2d),
\r
152 (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Y)
\r
155 return new Vector3(
\r
157 -(float)(Math.PI / 2d),
\r
158 (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z)
\r
162 public static KnownHeading ClosestKnownHeading(int degrees)
\r
164 KnownHeading ret = KnownHeadings[0];
\r
165 int facing = (int)(57.2957795d * RotToEuler(KnownHeadings[0].Heading).Z);
\r
166 if (facing < 0) facing += 360;
\r
167 int minDistance = Math.Abs(degrees - facing);
\r
169 for (int i = 1; i < KnownHeadings.Count; i++)
\r
171 facing = (int)(57.2957795d * RotToEuler(KnownHeadings[i].Heading).Z);
\r
172 if (facing < 0) facing += 360;
\r
174 int distance = Math.Abs(degrees - facing);
\r
175 if (distance < minDistance)
\r
177 ret = KnownHeadings[i];
\r
178 minDistance = distance;
\r
185 public Dictionary<UUID, string> KnownAnimations;
\r
187 public StateManager(RadegastInstance instance)
\r
189 this.instance = instance;
\r
190 this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
\r
191 KnownAnimations = Animations.ToDictionary();
\r
192 autosit = new AutoSit(this.instance);
\r
193 pseudohome = new PseudoHome(this.instance);
\r
195 beamTimer = new System.Timers.Timer();
\r
196 beamTimer.Enabled = false;
\r
197 beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);
\r
200 netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);
\r
201 netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
\r
202 netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);
\r
203 RegisterClientEvents(client);
\r
207 private void RegisterClientEvents(GridClient client)
\r
209 client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
210 client.Objects.AvatarSitChanged += new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
\r
211 client.Self.AlertMessage += new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
\r
212 client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
213 client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
\r
214 client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
217 private void UnregisterClientEvents(GridClient client)
\r
219 client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
220 client.Objects.AvatarSitChanged -= new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
\r
221 client.Self.AlertMessage -= new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
\r
222 client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
223 client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
\r
224 client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
227 public void Dispose()
\r
229 netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);
\r
230 netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
\r
231 netcom.ChatReceived -= new EventHandler<ChatEventArgs>(netcom_ChatReceived);
\r
232 UnregisterClientEvents(client);
\r
233 beamTimer.Dispose();
\r
236 if (lookAtTimer != null)
\r
238 lookAtTimer.Dispose();
\r
239 lookAtTimer = null;
\r
242 if (walkTimer != null)
\r
244 walkTimer.Dispose();
\r
249 void instance_ClientChanged(object sender, ClientChangedEventArgs e)
\r
251 UnregisterClientEvents(e.OldClient);
\r
252 RegisterClientEvents(client);
\r
255 void Objects_AvatarSitChanged(object sender, AvatarSitChangedEventArgs e)
\r
257 if (e.Avatar.LocalID != client.Self.LocalID) return;
\r
259 this.sitting = e.SittingOn != 0;
\r
260 if (SitStateChanged != null)
\r
262 SitStateChanged(this, new SitEventArgs(this.sitting));
\r
267 /// Locates avatar in the current sim, or adjacents sims
\r
269 /// <param name="person">Avatar UUID</param>
\r
270 /// <param name="position">Position within sim</param>
\r
271 /// <returns>True if managed to find the avatar</returns>
\r
272 public bool TryFindAvatar(UUID person, out Vector3 position)
\r
275 if (!TryFindAvatar(person, out sim, out position)) return false;
\r
277 if (sim == client.Network.CurrentSim) return true;
\r
278 position = ToLocalPosition(sim.Handle, position);
\r
282 public Vector3 ToLocalPosition(ulong handle, Vector3 position)
\r
284 Vector3d diff = ToVector3D(handle, position) - client.Self.GlobalPosition;
\r
285 position = new Vector3((float) diff.X, (float) diff.Y, (float) diff.Z) - position;
\r
289 public static Vector3d ToVector3D(ulong handle, Vector3 pos)
\r
291 uint globalX, globalY;
\r
292 Utils.LongToUInts(handle, out globalX, out globalY);
\r
294 return new Vector3d(
\r
295 (double)globalX + (double)pos.X,
\r
296 (double)globalY + (double)pos.Y,
\r
301 /// Locates avatar in the current sim, or adjacents sims
\r
303 /// <param name="person">Avatar UUID</param>
\r
304 /// <param name="sim">Simulator avatar is in</param>
\r
305 /// <param name="position">Position within sim</param>
\r
306 /// <returns>True if managed to find the avatar</returns>
\r
307 public bool TryFindAvatar(UUID person, out Simulator sim, out Vector3 position)
\r
309 return TryFindPrim(person, out sim, out position, true);
\r
311 public bool TryFindPrim(UUID person, out Simulator sim, out Vector3 position, bool onlyAvatars)
\r
313 Simulator[] Simulators = null;
\r
314 lock (client.Network.Simulators)
\r
316 Simulators = client.Network.Simulators.ToArray();
\r
319 position = Vector3.Zero;
\r
321 Primitive avi = null;
\r
323 // First try the object tracker
\r
324 foreach (var s in Simulators)
\r
326 avi = s.ObjectsAvatars.Find((Avatar av) => { return av.ID == person; });
\r
333 if (avi == null && !onlyAvatars)
\r
335 foreach (var s in Simulators)
\r
337 avi = s.ObjectsPrimitives.Find((Primitive av) => { return av.ID == person; });
\r
347 if (avi.ParentID == 0)
\r
349 position = avi.Position;
\r
354 if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
\r
356 position = seat.Position + avi.Position * seat.Rotation;
\r
362 foreach (var s in Simulators)
\r
364 if (s.AvatarPositions.ContainsKey(person))
\r
366 position = s.AvatarPositions[person];
\r
373 if (position.Z > 0.1f)
\r
379 public bool TryLocatePrim(Primitive avi, out Simulator sim, out Vector3 position)
\r
381 Simulator[] Simulators = null;
\r
382 lock (client.Network.Simulators)
\r
384 Simulators = client.Network.Simulators.ToArray();
\r
387 sim = client.Network.CurrentSim;
\r
388 position = Vector3.Zero;
\r
390 foreach (var s in Simulators)
\r
392 if (s.Handle == avi.RegionHandle)
\r
401 if (avi.ParentID == 0)
\r
403 position = avi.Position;
\r
408 if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
\r
410 position = seat.Position + avi.Position*seat.Rotation;
\r
414 if (position.Z > 0.1f)
\r
421 /// Move to target position either by walking or by teleporting
\r
423 /// <param name="target">Sim local position of the target</param>
\r
424 /// <param name="useTP">Move using teleport</param>
\r
425 public void MoveTo(Vector3 target, bool useTP)
\r
427 MoveTo(client.Network.CurrentSim, target, useTP);
\r
431 /// Move to target position either by walking or by teleporting
\r
433 /// <param name="sim">Simulator in which the target is</param>
\r
434 /// <param name="target">Sim local position of the target</param>
\r
435 /// <param name="useTP">Move using teleport</param>
\r
436 public void MoveTo(Simulator sim, Vector3 target, bool useTP)
\r
438 SetSitting(false, UUID.Zero);
\r
442 client.Self.RequestTeleport(sim.Handle, target);
\r
446 displayEndWalk = true;
\r
447 client.Self.Movement.TurnToward(target);
\r
448 WalkTo(GlobalPosition(sim, target));
\r
453 public void SetRandomHeading()
\r
455 client.Self.Movement.UpdateFromHeading(Utils.TWO_PI * rnd.NextDouble(), true);
\r
459 void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
\r
461 if (e.Simulator == client.Network.CurrentSim)
\r
463 SetRandomHeading();
\r
467 void Network_SimChanged(object sender, SimChangedEventArgs e)
\r
470 pseudohome.ETGoHome();
\r
473 private UUID teleportEffect = UUID.Random();
\r
475 void Self_TeleportProgress(object sender, TeleportEventArgs e)
\r
477 if (!client.Network.Connected) return;
\r
479 if (e.Status == TeleportStatus.Progress)
\r
481 client.Self.SphereEffect(client.Self.GlobalPosition, Color4.White, 4f, teleportEffect);
\r
484 if (e.Status == TeleportStatus.Finished)
\r
486 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
\r
487 SetRandomHeading();
\r
490 if (e.Status == TeleportStatus.Failed)
\r
492 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
\r
496 void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
\r
498 typing = away = busy = walking = false;
\r
500 if (lookAtTimer != null)
\r
502 lookAtTimer.Dispose();
\r
503 lookAtTimer = null;
\r
508 void netcom_ClientConnected(object sender, EventArgs e)
\r
510 if (!instance.GlobalSettings.ContainsKey("draw_distance"))
\r
512 instance.GlobalSettings["draw_distance"] = 48;
\r
515 client.Self.Movement.Camera.Far = instance.GlobalSettings["draw_distance"];
\r
517 if (lookAtTimer == null)
\r
519 lookAtTimer = new System.Threading.Timer(new TimerCallback(lookAtTimerTick), null, Timeout.Infinite, Timeout.Infinite);
\r
523 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
\r
525 if (!e.Update.Avatar) return;
\r
526 if (!following) return;
\r
529 client.Network.CurrentSim.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av);
\r
530 if (av == null) return;
\r
532 if (av.ID == followID)
\r
534 Vector3 pos = AvatarPosition(client.Network.CurrentSim, av);
\r
540 void FollowUpdate(Vector3 pos)
\r
542 if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)
\r
544 Vector3 target = pos + Vector3.Normalize(client.Self.SimPosition - pos) * (followDistance - 1f);
\r
545 client.Self.AutoPilotCancel();
\r
546 Vector3d glb = GlobalPosition(client.Network.CurrentSim, target);
\r
547 client.Self.AutoPilot(glb.X, glb.Y, glb.Z);
\r
551 client.Self.AutoPilotCancel();
\r
552 client.Self.Movement.TurnToward(pos);
\r
556 public Quaternion AvatarRotation(Simulator sim, UUID avID)
\r
558 Quaternion rot = Quaternion.Identity;
\r
559 Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
\r
564 if (av.ParentID == 0)
\r
571 if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
\r
573 rot = prim.Rotation + av.Rotation;
\r
581 public Vector3 AvatarPosition(Simulator sim, UUID avID)
\r
583 Vector3 pos = Vector3.Zero;
\r
584 Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
\r
587 return AvatarPosition(sim, av);
\r
592 if (sim.AvatarPositions.TryGetValue(avID, out coarse))
\r
594 if (coarse.Z > 0.01)
\r
601 public Vector3 AvatarPosition(Simulator sim, Avatar av)
\r
603 Vector3 pos = Vector3.Zero;
\r
605 if (av.ParentID == 0)
\r
612 if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
\r
614 pos = prim.Position + av.Position;
\r
621 public void Follow(string name, UUID id)
\r
625 following = followID != UUID.Zero;
\r
631 Vector3 target = AvatarPosition(client.Network.CurrentSim, id);
\r
632 if (Vector3.Zero != target)
\r
634 client.Self.Movement.TurnToward(target);
\r
635 FollowUpdate(target);
\r
641 public void StopFollowing()
\r
644 followName = string.Empty;
\r
645 followID = UUID.Zero;
\r
648 #region Look at effect
\r
649 private int lastLookAtEffect = 0;
\r
650 private UUID lookAtEffect = UUID.Random();
\r
653 /// Set eye focus 3m in front of us
\r
655 public void LookInFront()
\r
657 if (!client.Network.Connected || instance.GlobalSettings["disable_look_at"]) return;
\r
659 client.Self.LookAtEffect(client.Self.AgentID, client.Self.AgentID,
\r
660 new Vector3d(new Vector3(3, 0, 0) * Quaternion.Identity),
\r
661 LookAtType.Idle, lookAtEffect);
\r
664 void lookAtTimerTick(object state)
\r
669 void netcom_ChatReceived(object sender, ChatEventArgs e)
\r
671 //somehow it can be too early (when Radegast is loaded from running bot)
\r
672 if (instance.GlobalSettings==null) return;
\r
673 if (!instance.GlobalSettings["disable_look_at"]
\r
674 && e.SourceID != client.Self.AgentID
\r
675 && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))
\r
677 // change focus max every 4 seconds
\r
678 if (Environment.TickCount - lastLookAtEffect > 4000)
\r
680 lastLookAtEffect = Environment.TickCount;
\r
681 client.Self.LookAtEffect(client.Self.AgentID, e.SourceID, Vector3d.Zero, LookAtType.Respond, lookAtEffect);
\r
682 // keep looking at the speaker for 10 seconds
\r
683 if (lookAtTimer != null)
\r
685 lookAtTimer.Change(10000, Timeout.Infinite);
\r
690 #endregion Look at effect
\r
692 #region Walking (move to)
\r
693 private bool walking = false;
\r
694 private System.Threading.Timer walkTimer;
\r
695 private int walkChekInterval = 500;
\r
696 private Vector3d walkToTarget;
\r
697 int lastDistance = 0;
\r
698 int lastDistanceChanged = 0;
\r
700 public void WalkTo(Primitive prim)
\r
702 WalkTo(GlobalPosition(prim));
\r
704 public double WaitUntilPosition(Vector3d pos, TimeSpan maxWait, double howClose)
\r
707 DateTime until = DateTime.Now + maxWait;
\r
708 while (until > DateTime.Now)
\r
710 double dist = Vector3d.Distance(client.Self.GlobalPosition, pos);
\r
711 if (howClose >= dist) return dist;
\r
714 return Vector3d.Distance(client.Self.GlobalPosition, pos);
\r
718 public void WalkTo(Vector3d globalPos)
\r
720 walkToTarget = globalPos;
\r
725 followName = string.Empty;
\r
728 if (walkTimer == null)
\r
730 walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);
\r
733 lastDistanceChanged = System.Environment.TickCount;
\r
734 client.Self.AutoPilotCancel();
\r
736 client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);
\r
737 FireWalkStateCanged();
\r
740 void walkTimerElapsed(object sender)
\r
743 double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);
\r
752 if (lastDistance != (int)distance)
\r
754 lastDistanceChanged = System.Environment.TickCount;
\r
755 lastDistance = (int)distance;
\r
757 else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)
\r
759 // Our distance to the target has not changed in 10s, give up
\r
763 if (walkTimer != null) walkTimer.Change(walkChekInterval, Timeout.Infinite);
\r
767 void Self_AlertMessage(object sender, AlertMessageEventArgs e)
\r
769 if (e.Message.Contains("Autopilot cancel"))
\r
778 void FireWalkStateCanged()
\r
780 if (OnWalkStateCanged != null)
\r
782 try { OnWalkStateCanged(walking); }
\r
783 catch (Exception) { }
\r
787 public void EndWalking()
\r
792 Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);
\r
793 walkTimer.Dispose();
\r
795 client.Self.AutoPilotCancel();
\r
797 if (displayEndWalk)
\r
799 displayEndWalk = false;
\r
800 string msg = "Finished walking";
\r
802 if (walkToTarget != Vector3d.Zero)
\r
804 System.Threading.Thread.Sleep(1000);
\r
805 msg += string.Format(" {0:0} meters from destination", Vector3d.Distance(client.Self.GlobalPosition, walkToTarget));
\r
806 walkToTarget = Vector3d.Zero;
\r
809 instance.TabConsole.DisplayNotificationInChat(msg);
\r
812 FireWalkStateCanged();
\r
817 public void SetTyping(bool typing)
\r
819 if (!client.Network.Connected) return;
\r
821 Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();
\r
822 typingAnim.Add(typingAnimationID, typing);
\r
824 client.Self.Animate(typingAnim, false);
\r
827 client.Self.Chat(string.Empty, 0, ChatType.StartTyping);
\r
829 client.Self.Chat(string.Empty, 0, ChatType.StopTyping);
\r
831 this.typing = typing;
\r
834 public void SetAway(bool away)
\r
836 Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();
\r
837 awayAnim.Add(awayAnimationID, away);
\r
839 client.Self.Animate(awayAnim, true);
\r
840 if (UseMoveControl) client.Self.Movement.Away = away;
\r
844 public void SetBusy(bool busy)
\r
846 Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();
\r
847 busyAnim.Add(busyAnimationID, busy);
\r
849 client.Self.Animate(busyAnim, true);
\r
853 public void SetFlying(bool flying)
\r
855 this.flying = client.Self.Movement.Fly = flying;
\r
858 public void SetAlwaysRun(bool alwaysrun)
\r
860 this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;
\r
863 public void SetSitting(bool sitting, UUID target)
\r
865 this.sitting = sitting;
\r
869 client.Self.RequestSit(target, Vector3.Zero);
\r
874 if (!instance.RLV.RestictionActive("unsit"))
\r
876 client.Self.Stand();
\r
880 instance.TabConsole.DisplayNotificationInChat("Unsit prevented by RLV");
\r
881 this.sitting = true;
\r
886 if (SitStateChanged != null)
\r
888 SitStateChanged(this, new SitEventArgs(this.sitting));
\r
893 StopAllAnimations();
\r
897 public void StopAllAnimations()
\r
899 Dictionary<UUID, bool> stop = new Dictionary<UUID, bool>();
\r
901 client.Self.SignaledAnimations.ForEach((UUID anim) =>
\r
903 if (!KnownAnimations.ContainsKey(anim))
\r
905 stop.Add(anim, false);
\r
909 if (stop.Count > 0)
\r
911 client.Self.Animate(stop, true);
\r
915 static public Vector3d GlobalPosition(Simulator sim, Vector3 pos)
\r
917 uint globalX, globalY;
\r
918 Utils.LongToUInts(sim.Handle, out globalX, out globalY);
\r
920 return new Vector3d(
\r
921 (double)globalX + (double)pos.X,
\r
922 (double)globalY + (double)pos.Y,
\r
926 public Vector3d GlobalPosition(Primitive prim)
\r
928 return GlobalPosition(client.Network.CurrentSim, prim.Position);
\r
931 private System.Timers.Timer beamTimer;
\r
932 private List<Vector3d> beamTarget;
\r
933 private Random beamRandom = new Random();
\r
934 private UUID pointID;
\r
935 private UUID sphereID;
\r
936 private List<UUID> beamID;
\r
937 private int numBeans;
\r
938 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
939 private Primitive targetPrim;
\r
941 public void UnSetPointing()
\r
943 beamTimer.Enabled = false;
\r
944 if (pointID != UUID.Zero)
\r
946 client.Self.PointAtEffect(client.Self.AgentID, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);
\r
947 pointID = UUID.Zero;
\r
950 if (beamID != null)
\r
952 foreach (UUID id in beamID)
\r
954 client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);
\r
959 if (sphereID != UUID.Zero)
\r
961 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);
\r
962 sphereID = UUID.Zero;
\r
967 void beamTimer_Elapsed(object sender, EventArgs e)
\r
969 if (beamID == null) return;
\r
973 client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);
\r
975 for (i = 0; i < numBeans; i++)
\r
977 UUID newBeam = UUID.Random();
\r
982 scatter = GlobalPosition(targetPrim);
\r
986 Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);
\r
987 Vector3d cross = direction % new Vector3d(0, 0, 1);
\r
989 scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);
\r
991 client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);
\r
994 for (int j = 1; j < numBeans; j++)
\r
996 UUID newBeam = UUID.Random();
\r
998 Vector3d cross = new Vector3d(0, 0, 1);
\r
1000 scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);
\r
1002 client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);
\r
1005 catch (Exception) { };
\r
1009 public void SetPointing(Primitive prim, int numBeans)
\r
1012 client.Self.Movement.TurnToward(prim.Position);
\r
1013 pointID = UUID.Random();
\r
1014 sphereID = UUID.Random();
\r
1015 beamID = new List<UUID>();
\r
1016 beamTarget = new List<Vector3d>();
\r
1017 targetPrim = prim;
\r
1018 this.numBeans = numBeans;
\r
1020 client.Self.PointAtEffect(client.Self.AgentID, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);
\r
1022 for (int i = 0; i < numBeans; i++)
\r
1024 UUID newBeam = UUID.Random();
\r
1025 beamID.Add(newBeam);
\r
1026 beamTarget.Add(Vector3d.Zero);
\r
1029 for (int i = 1; i < numBeans; i++)
\r
1031 UUID newBeam = UUID.Random();
\r
1032 beamID.Add(newBeam);
\r
1033 beamTarget.Add(Vector3d.Zero);
\r
1036 beamTimer.Interval = 1000;
\r
1037 beamTimer.Enabled = true;
\r
1040 public UUID TypingAnimationID
\r
1042 get { return typingAnimationID; }
\r
1043 set { typingAnimationID = value; }
\r
1046 public UUID AwayAnimationID
\r
1048 get { return awayAnimationID; }
\r
1049 set { awayAnimationID = value; }
\r
1052 public UUID BusyAnimationID
\r
1054 get { return busyAnimationID; }
\r
1055 set { busyAnimationID = value; }
\r
1058 public bool IsTyping
\r
1060 get { return typing; }
\r
1063 public bool IsAway
\r
1067 if (UseMoveControl) return client.Self.Movement.Away;
\r
1072 public bool IsBusy
\r
1074 get { return busy; }
\r
1077 public bool IsFlying
\r
1079 get { return client.Self.Movement.Fly; }
\r
1082 public bool IsSitting
\r
1086 if (client.Self.Movement.SitOnGround || client.Self.SittingOn != 0) return true;
\r
1088 Logger.Log("out of sync sitting", Helpers.LogLevel.Debug);
\r
1095 public bool IsPointing
\r
1097 get { return pointID != UUID.Zero; }
\r
1100 public bool IsFollowing
\r
1102 get { return following; }
\r
1105 public string FollowName
\r
1107 get { return followName; }
\r
1108 set { followName = value; }
\r
1111 public float FollowDistance
\r
1113 get { return followDistance; }
\r
1114 set { followDistance = value; }
\r
1117 public bool IsWalking
\r
1119 get { return walking; }
\r
1122 private AutoSit autosit;
\r
1123 public AutoSit AutoSit
\r
1125 get { return autosit; }
\r
1128 private PseudoHome pseudohome;
\r
1131 /// Experimental Option that sometimes the Client has more authority than state mananger
\r
1133 public static bool UseMoveControl;
\r
1135 public PseudoHome PseudoHome
\r
1137 get { return pseudohome; }
\r
1141 public class SitEventArgs : EventArgs
\r
1143 public bool Sitting;
\r
1145 public SitEventArgs(bool sitting)
\r
1147 this.Sitting = sitting;
\r