OSDN Git Service

Git and line-endings don't like each other
[radegast/radegast.git] / Radegast / Core / StateManager.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2012, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.Timers;
34 using System.Threading;
35
36 using OpenMetaverse;
37
38 using Radegast.Bot;
39 using Radegast.Netcom;
40
41 namespace Radegast
42 {
43     public class KnownHeading
44     {
45         public string ID { get; set; }
46         public string Name { get; set; }
47         public Quaternion Heading { get; set; }
48         public KnownHeading(string id, string name, Quaternion heading)
49         {
50             this.ID = id;
51             this.Name = name;
52             this.Heading = heading;
53         }
54
55         public override string ToString()
56         {
57             return Name;
58         }
59     }
60
61     public class StateManager : IDisposable
62     {
63         public Parcel Parcel { get; set; }
64
65         private RadegastInstance instance;
66         private GridClient client { get { return instance.Client; } }
67         private RadegastNetcom netcom { get { return instance.Netcom; } }
68
69         private bool typing = false;
70         private bool away = false;
71         private bool busy = false;
72         private bool flying = false;
73         private bool alwaysrun = false;
74         private bool sitting = false;
75
76         private bool following = false;
77         private string followName = string.Empty;
78         private float followDistance = 3.0f;
79         private UUID followID;
80         private bool displayEndWalk = false;
81
82         private UUID awayAnimationID = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");
83         private UUID busyAnimationID = new UUID("efcf670c2d188128973a034ebc806b67");
84         private UUID typingAnimationID = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
85         internal static Random rnd = new Random();
86         private System.Threading.Timer lookAtTimer;
87
88         /// <summary>
89         /// Passes walk state
90         /// </summary>
91         /// <param name="walking">True if we are walking towards a targer</param>
92         public delegate void WalkStateCanged(bool walking);
93
94         /// <summary>
95         /// Fires when we start or stop walking towards a target
96         /// </summary>
97         public event WalkStateCanged OnWalkStateCanged;
98
99         /// <summary>
100         /// Fires when avatar stands
101         /// </summary>
102         public event EventHandler<SitEventArgs> SitStateChanged;
103
104         static List<KnownHeading> m_Headings;
105         public static List<KnownHeading> KnownHeadings
106         {
107             get
108             {
109                 if (m_Headings == null)
110                 {
111                     m_Headings = new List<KnownHeading>(16);
112                     m_Headings.Add(new KnownHeading("E", "East", new Quaternion(0.00000f, 0.00000f, 0.00000f, 1.00000f)));
113                     m_Headings.Add(new KnownHeading("ENE", "East by Northeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, 0.98079f)));
114                     m_Headings.Add(new KnownHeading("NE", "Northeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, 0.92388f)));
115                     m_Headings.Add(new KnownHeading("NNE", "North by Northeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, 0.83147f)));
116                     m_Headings.Add(new KnownHeading("N", "North", new Quaternion(0.00000f, 0.00000f, 0.70711f, 0.70711f)));
117                     m_Headings.Add(new KnownHeading("NNW", "North by Northwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, 0.55557f)));
118                     m_Headings.Add(new KnownHeading("NW", "Nortwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, 0.38268f)));
119                     m_Headings.Add(new KnownHeading("WNW", "West by Northwest", new Quaternion(0.00000f, 0.00000f, 0.98079f, 0.19509f)));
120                     m_Headings.Add(new KnownHeading("W", "West", new Quaternion(0.00000f, 0.00000f, 1.00000f, -0.00000f)));
121                     m_Headings.Add(new KnownHeading("WSW", "West by Southwest", new Quaternion(0.00000f, 0.00000f, 0.98078f, -0.19509f)));
122                     m_Headings.Add(new KnownHeading("SW", "Southwest", new Quaternion(0.00000f, 0.00000f, 0.92388f, -0.38268f)));
123                     m_Headings.Add(new KnownHeading("SSW", "South by Southwest", new Quaternion(0.00000f, 0.00000f, 0.83147f, -0.55557f)));
124                     m_Headings.Add(new KnownHeading("S", "South", new Quaternion(0.00000f, 0.00000f, 0.70711f, -0.70711f)));
125                     m_Headings.Add(new KnownHeading("SSE", "South by Southeast", new Quaternion(0.00000f, 0.00000f, 0.55557f, -0.83147f)));
126                     m_Headings.Add(new KnownHeading("SE", "Southeast", new Quaternion(0.00000f, 0.00000f, 0.38268f, -0.92388f)));
127                     m_Headings.Add(new KnownHeading("ESE", "East by Southeast", new Quaternion(0.00000f, 0.00000f, 0.19509f, -0.98078f)));
128                 }
129                 return m_Headings;
130             }
131         }
132
133         public static Vector3 RotToEuler(Quaternion r)
134         {
135             Quaternion t = new Quaternion(r.X * r.X, r.Y * r.Y, r.Z * r.Z, r.W * r.W);
136
137             float m = (t.X + t.Y + t.Z + t.W);
138             if (Math.Abs(m) < 0.001) return Vector3.Zero;
139             float n = 2 * (r.Y * r.W + r.X * r.Z);
140             float p = m * m - n * n;
141
142             if (p > 0)
143                 return new Vector3(
144                     (float)Math.Atan2(2.0 * (r.X * r.W - r.Y * r.Z), (-t.X - t.Y + t.Z + t.W)),
145                     (float)Math.Atan2(n, Math.Sqrt(p)),
146                     (float)Math.Atan2(2.0 * (r.Z * r.W - r.X * r.Y), t.X - t.Y - t.Z + t.W)
147                     );
148             else if (n > 0)
149                 return new Vector3(
150                     0f,
151                     (float)(Math.PI / 2d),
152                     (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Y)
153                     );
154             else
155                 return new Vector3(
156                     0f,
157                     -(float)(Math.PI / 2d),
158                     (float)Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z)
159                     );
160         }
161
162         public static KnownHeading ClosestKnownHeading(int degrees)
163         {
164             KnownHeading ret = KnownHeadings[0];
165             int facing = (int)(57.2957795d * RotToEuler(KnownHeadings[0].Heading).Z);
166             if (facing < 0) facing += 360;
167             int minDistance = Math.Abs(degrees - facing);
168
169             for (int i = 1; i < KnownHeadings.Count; i++)
170             {
171                 facing = (int)(57.2957795d * RotToEuler(KnownHeadings[i].Heading).Z);
172                 if (facing < 0) facing += 360;
173
174                 int distance = Math.Abs(degrees - facing);
175                 if (distance < minDistance)
176                 {
177                     ret = KnownHeadings[i];
178                     minDistance = distance;
179                 }
180             }
181
182             return ret;
183         }
184
185         public Dictionary<UUID, string> KnownAnimations;
186
187         public StateManager(RadegastInstance instance)
188         {
189             this.instance = instance;
190             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
191             KnownAnimations = Animations.ToDictionary();
192             autosit = new AutoSit(this.instance);
193             pseudohome = new PseudoHome(this.instance);
194
195             beamTimer = new System.Timers.Timer();
196             beamTimer.Enabled = false;
197             beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);
198
199             // Callbacks
200             netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);
201             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
202             netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);
203             RegisterClientEvents(client);
204         }
205
206
207         private void RegisterClientEvents(GridClient client)
208         {
209             client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
210             client.Objects.AvatarSitChanged += new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
211             client.Self.AlertMessage += new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
212             client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
213             client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
214             client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);
215         }
216
217         private void UnregisterClientEvents(GridClient client)
218         {
219             client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
220             client.Objects.AvatarSitChanged -= new EventHandler<AvatarSitChangedEventArgs>(Objects_AvatarSitChanged);
221             client.Self.AlertMessage -= new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
222             client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
223             client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
224             client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);
225         }
226
227         public void Dispose()
228         {
229             netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);
230             netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
231             netcom.ChatReceived -= new EventHandler<ChatEventArgs>(netcom_ChatReceived);
232             UnregisterClientEvents(client);
233             beamTimer.Dispose();
234             beamTimer = null;
235
236             if (lookAtTimer != null)
237             {
238                 lookAtTimer.Dispose();
239                 lookAtTimer = null;
240             }
241
242             if (walkTimer != null)
243             {
244                 walkTimer.Dispose();
245                 walkTimer = null;
246             }
247         }
248
249         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
250         {
251             UnregisterClientEvents(e.OldClient);
252             RegisterClientEvents(client);
253         }
254
255         void Objects_AvatarSitChanged(object sender, AvatarSitChangedEventArgs e)
256         {
257             if (e.Avatar.LocalID != client.Self.LocalID) return;
258
259             this.sitting = e.SittingOn != 0;
260             if (SitStateChanged != null)
261             {
262                 SitStateChanged(this, new SitEventArgs(this.sitting));
263             }
264         }
265
266         /// <summary>
267         /// Locates avatar in the current sim, or adjacents sims
268         /// </summary>
269         /// <param name="person">Avatar UUID</param>
270         /// <param name="position">Position within sim</param>
271         /// <returns>True if managed to find the avatar</returns>
272         public bool TryFindAvatar(UUID person, out Vector3 position)
273         {
274             Simulator sim;
275             if (!TryFindAvatar(person, out sim, out position)) return false;
276             // same sim?
277             if (sim == client.Network.CurrentSim) return true;
278             position = ToLocalPosition(sim.Handle, position);
279             return true;
280         }
281
282         public Vector3 ToLocalPosition(ulong handle, Vector3 position)
283         {
284             Vector3d diff = ToVector3D(handle, position) - client.Self.GlobalPosition;
285             position = new Vector3((float) diff.X, (float) diff.Y, (float) diff.Z) - position;
286             return position;
287         }
288
289         public static Vector3d ToVector3D(ulong handle, Vector3 pos)
290         {
291             uint globalX, globalY;
292             Utils.LongToUInts(handle, out globalX, out globalY);
293
294             return new Vector3d(
295                 (double)globalX + (double)pos.X,
296                 (double)globalY + (double)pos.Y,
297                 (double)pos.Z);
298         }
299
300         /// <summary>
301         /// Locates avatar in the current sim, or adjacents sims
302         /// </summary>
303         /// <param name="person">Avatar UUID</param>
304         /// <param name="sim">Simulator avatar is in</param>
305         /// <param name="position">Position within sim</param>
306         /// <returns>True if managed to find the avatar</returns>
307         public bool TryFindAvatar(UUID person, out Simulator sim, out Vector3 position)
308         {
309             return TryFindPrim(person, out sim, out position, true);
310         }
311         public bool TryFindPrim(UUID person, out Simulator sim, out Vector3 position, bool onlyAvatars)
312         {
313             Simulator[] Simulators = null;
314             lock (client.Network.Simulators)
315             {
316                 Simulators = client.Network.Simulators.ToArray();
317             }
318             sim = null;
319             position = Vector3.Zero;
320
321             Primitive avi = null;
322
323             // First try the object tracker
324             foreach (var s in Simulators)
325             {
326                 avi = s.ObjectsAvatars.Find((Avatar av) => { return av.ID == person; });
327                 if (avi != null)
328                 {
329                     sim = s;
330                     break;
331                 }
332             }
333             if (avi == null && !onlyAvatars)
334             {
335                 foreach (var s in Simulators)
336                 {
337                     avi = s.ObjectsPrimitives.Find((Primitive av) => { return av.ID == person; });
338                     if (avi != null)
339                     {
340                         sim = s;
341                         break;
342                     }
343                 }
344             }
345             if (avi != null)
346             {
347                 if (avi.ParentID == 0)
348                 {
349                     position = avi.Position;
350                 }
351                 else
352                 {
353                     Primitive seat;
354                     if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
355                     {
356                         position = seat.Position + avi.Position * seat.Rotation;
357                     }
358                 }
359             }
360             else
361             {
362                 foreach (var s in Simulators)
363                 {
364                     if (s.AvatarPositions.ContainsKey(person))
365                     {
366                         position = s.AvatarPositions[person];
367                         sim = s;
368                         break;
369                     }
370                 }
371             }
372
373             if (position.Z > 0.1f)
374                 return true;
375             else
376                 return false;
377         }
378
379         public bool TryLocatePrim(Primitive avi, out Simulator sim, out Vector3 position)
380         {
381             Simulator[] Simulators = null;
382             lock (client.Network.Simulators)
383             {
384                 Simulators = client.Network.Simulators.ToArray();
385             }
386
387             sim = client.Network.CurrentSim;
388             position = Vector3.Zero;
389             {
390                 foreach (var s in Simulators)
391                 {
392                     if (s.Handle == avi.RegionHandle)
393                     {
394                         sim = s;
395                         break;
396                     }
397                 }
398             }
399             if (avi != null)
400             {
401                 if (avi.ParentID == 0)
402                 {
403                     position = avi.Position;
404                 }
405                 else
406                 {
407                     Primitive seat;
408                     if (sim.ObjectsPrimitives.TryGetValue(avi.ParentID, out seat))
409                     {
410                         position = seat.Position + avi.Position*seat.Rotation;
411                     }
412                 }
413             }
414             if (position.Z > 0.1f)
415                 return true;
416             else
417                 return false;
418         }
419
420         /// <summary>
421         /// Move to target position either by walking or by teleporting
422         /// </summary>
423         /// <param name="target">Sim local position of the target</param>
424         /// <param name="useTP">Move using teleport</param>
425         public void MoveTo(Vector3 target, bool useTP)
426         {
427             MoveTo(client.Network.CurrentSim, target, useTP);
428         }
429
430         /// <summary>
431         /// Move to target position either by walking or by teleporting
432         /// </summary>
433         /// <param name="sim">Simulator in which the target is</param>
434         /// <param name="target">Sim local position of the target</param>
435         /// <param name="useTP">Move using teleport</param>
436         public void MoveTo(Simulator sim, Vector3 target, bool useTP)
437         {
438             SetSitting(false, UUID.Zero);
439
440             if (useTP)
441             {
442                 client.Self.RequestTeleport(sim.Handle, target);
443             }
444             else
445             {
446                 displayEndWalk = true;
447                 client.Self.Movement.TurnToward(target);
448                 WalkTo(GlobalPosition(sim, target));
449             }
450         }
451
452
453         public void SetRandomHeading()
454         {
455             client.Self.Movement.UpdateFromHeading(Utils.TWO_PI * rnd.NextDouble(), true);
456             LookInFront();
457         }
458
459         void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
460         {
461             if (e.Simulator == client.Network.CurrentSim)
462             {
463                 SetRandomHeading();
464             }
465         }
466
467         void Network_SimChanged(object sender, SimChangedEventArgs e)
468         {
469             autosit.TrySit();
470             pseudohome.ETGoHome();
471         }
472
473         private UUID teleportEffect = UUID.Random();
474
475         void Self_TeleportProgress(object sender, TeleportEventArgs e)
476         {
477             if (!client.Network.Connected) return;
478
479             if (e.Status == TeleportStatus.Progress)
480             {
481                 client.Self.SphereEffect(client.Self.GlobalPosition, Color4.White, 4f, teleportEffect);
482             }
483
484             if (e.Status == TeleportStatus.Finished)
485             {
486                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
487                 SetRandomHeading();
488             }
489
490             if (e.Status == TeleportStatus.Failed)
491             {
492                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
493             }
494         }
495
496         void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
497         {
498             typing = away = busy = walking = false;
499
500             if (lookAtTimer != null)
501             {
502                 lookAtTimer.Dispose();
503                 lookAtTimer = null;
504             }
505
506         }
507
508         void netcom_ClientConnected(object sender, EventArgs e)
509         {
510             if (!instance.GlobalSettings.ContainsKey("draw_distance"))
511             {
512                 instance.GlobalSettings["draw_distance"] = 48;
513             }
514
515             client.Self.Movement.Camera.Far = instance.GlobalSettings["draw_distance"];
516
517             if (lookAtTimer == null)
518             {
519                 lookAtTimer = new System.Threading.Timer(new TimerCallback(lookAtTimerTick), null, Timeout.Infinite, Timeout.Infinite);
520             }
521         }
522
523         void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
524         {
525             if (!e.Update.Avatar) return;
526             if (!following) return;
527
528             Avatar av;
529             client.Network.CurrentSim.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av);
530             if (av == null) return;
531
532             if (av.ID == followID)
533             {
534                 Vector3 pos = AvatarPosition(client.Network.CurrentSim, av);
535
536                 FollowUpdate(pos);
537             }
538         }
539
540         void FollowUpdate(Vector3 pos)
541         {
542             if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)
543             {
544                 Vector3 target = pos + Vector3.Normalize(client.Self.SimPosition - pos) * (followDistance - 1f);
545                 client.Self.AutoPilotCancel();
546                 Vector3d glb = GlobalPosition(client.Network.CurrentSim, target);
547                 client.Self.AutoPilot(glb.X, glb.Y, glb.Z);
548             }
549             else
550             {
551                 client.Self.AutoPilotCancel();
552                 client.Self.Movement.TurnToward(pos);
553             }
554         }
555
556         public Quaternion AvatarRotation(Simulator sim, UUID avID)
557         {
558             Quaternion rot = Quaternion.Identity;
559             Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
560
561             if (av == null)
562                 return rot;
563
564             if (av.ParentID == 0)
565             {
566                 rot = av.Rotation;
567             }
568             else
569             {
570                 Primitive prim;
571                 if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
572                 {
573                     rot = prim.Rotation + av.Rotation;
574                 }
575             }
576
577             return rot;
578         }
579
580
581         public Vector3 AvatarPosition(Simulator sim, UUID avID)
582         {
583             Vector3 pos = Vector3.Zero;
584             Avatar av = sim.ObjectsAvatars.Find((Avatar a) => { return a.ID == avID; });
585             if (av != null)
586             {
587                 return AvatarPosition(sim, av);
588             }
589             else
590             {
591                 Vector3 coarse;
592                 if (sim.AvatarPositions.TryGetValue(avID, out coarse))
593                 {
594                     if (coarse.Z > 0.01)
595                         return coarse;
596                 }
597             }
598             return pos;
599         }
600
601         public Vector3 AvatarPosition(Simulator sim, Avatar av)
602         {
603             Vector3 pos = Vector3.Zero;
604
605             if (av.ParentID == 0)
606             {
607                 pos = av.Position;
608             }
609             else
610             {
611                 Primitive prim;
612                 if (sim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim))
613                 {
614                     pos = prim.Position + av.Position;
615                 }
616             }
617
618             return pos;
619         }
620
621         public void Follow(string name, UUID id)
622         {
623             followName = name;
624             followID = id;
625             following = followID != UUID.Zero;
626
627             if (following)
628             {
629                 walking = false;
630
631                 Vector3 target = AvatarPosition(client.Network.CurrentSim, id);
632                 if (Vector3.Zero != target)
633                 {
634                     client.Self.Movement.TurnToward(target);
635                     FollowUpdate(target);
636                 }
637
638             }
639         }
640
641         public void StopFollowing()
642         {
643             following = false;
644             followName = string.Empty;
645             followID = UUID.Zero;
646         }
647
648         #region Look at effect
649         private int lastLookAtEffect = 0;
650         private UUID lookAtEffect = UUID.Random();
651
652         /// <summary>
653         /// Set eye focus 3m in front of us
654         /// </summary>
655         public void LookInFront()
656         {
657             if (!client.Network.Connected || instance.GlobalSettings["disable_look_at"]) return;
658
659             client.Self.LookAtEffect(client.Self.AgentID, client.Self.AgentID,
660                 new Vector3d(new Vector3(3, 0, 0) * Quaternion.Identity),
661                 LookAtType.Idle, lookAtEffect);
662         }
663
664         void lookAtTimerTick(object state)
665         {
666             LookInFront();
667         }
668
669         void netcom_ChatReceived(object sender, ChatEventArgs e)
670         {
671             //somehow it can be too early (when Radegast is loaded from running bot)
672             if (instance.GlobalSettings==null) return;
673             if (!instance.GlobalSettings["disable_look_at"]
674                 && e.SourceID != client.Self.AgentID
675                 && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))
676             {
677                 // change focus max every 4 seconds
678                 if (Environment.TickCount - lastLookAtEffect > 4000)
679                 {
680                     lastLookAtEffect = Environment.TickCount;
681                     client.Self.LookAtEffect(client.Self.AgentID, e.SourceID, Vector3d.Zero, LookAtType.Respond, lookAtEffect);
682                     // keep looking at the speaker for 10 seconds
683                     if (lookAtTimer != null)
684                     {
685                         lookAtTimer.Change(10000, Timeout.Infinite);
686                     }
687                 }
688             }
689         }
690         #endregion Look at effect
691
692         #region Walking (move to)
693         private bool walking = false;
694         private System.Threading.Timer walkTimer;
695         private int walkChekInterval = 500;
696         private Vector3d walkToTarget;
697         int lastDistance = 0;
698         int lastDistanceChanged = 0;
699
700         public void WalkTo(Primitive prim)
701         {
702             WalkTo(GlobalPosition(prim));
703         }
704         public double WaitUntilPosition(Vector3d pos, TimeSpan maxWait, double howClose)
705         {
706              
707             DateTime until = DateTime.Now + maxWait;
708             while (until > DateTime.Now)
709             {
710                 double dist = Vector3d.Distance(client.Self.GlobalPosition, pos);
711                 if (howClose >= dist) return dist;
712                 Thread.Sleep(250);
713             }
714             return Vector3d.Distance(client.Self.GlobalPosition, pos);
715             
716         }
717
718         public void WalkTo(Vector3d globalPos)
719         {
720             walkToTarget = globalPos;
721
722             if (following)
723             {
724                 following = false;
725                 followName = string.Empty;
726             }
727
728             if (walkTimer == null)
729             {
730                 walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);
731             }
732
733             lastDistanceChanged = System.Environment.TickCount;
734             client.Self.AutoPilotCancel();
735             walking = true;
736             client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);
737             FireWalkStateCanged();
738         }
739
740         void walkTimerElapsed(object sender)
741         {
742
743             double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);
744
745             if (distance < 2d)
746             {
747                 // We're there
748                 EndWalking();
749             }
750             else
751             {
752                 if (lastDistance != (int)distance)
753                 {
754                     lastDistanceChanged = System.Environment.TickCount;
755                     lastDistance = (int)distance;
756                 }
757                 else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)
758                 {
759                     // Our distance to the target has not changed in 10s, give up
760                     EndWalking();
761                     return;
762                 }
763                 if (walkTimer != null) walkTimer.Change(walkChekInterval, Timeout.Infinite);
764             }
765         }
766
767         void Self_AlertMessage(object sender, AlertMessageEventArgs e)
768         {
769             if (e.Message.Contains("Autopilot cancel"))
770             {
771                 if (walking)
772                 {
773                     EndWalking();
774                 }
775             }
776         }
777
778         void FireWalkStateCanged()
779         {
780             if (OnWalkStateCanged != null)
781             {
782                 try { OnWalkStateCanged(walking); }
783                 catch (Exception) { }
784             }
785         }
786
787         public void EndWalking()
788         {
789             if (walking)
790             {
791                 walking = false;
792                 Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);
793                 walkTimer.Dispose();
794                 walkTimer = null;
795                 client.Self.AutoPilotCancel();
796                 
797                 if (displayEndWalk)
798                 {
799                     displayEndWalk = false;
800                     string msg = "Finished walking";
801
802                     if (walkToTarget != Vector3d.Zero)
803                     {
804                         System.Threading.Thread.Sleep(1000);
805                         msg += string.Format(" {0:0} meters from destination", Vector3d.Distance(client.Self.GlobalPosition, walkToTarget));
806                         walkToTarget = Vector3d.Zero;
807                     }
808
809                     instance.TabConsole.DisplayNotificationInChat(msg);
810                 }
811
812                 FireWalkStateCanged();
813             }
814         }
815         #endregion
816
817         public void SetTyping(bool typing)
818         {
819             if (!client.Network.Connected) return;
820
821             Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();
822             typingAnim.Add(typingAnimationID, typing);
823
824             client.Self.Animate(typingAnim, false);
825
826             if (typing)
827                 client.Self.Chat(string.Empty, 0, ChatType.StartTyping);
828             else
829                 client.Self.Chat(string.Empty, 0, ChatType.StopTyping);
830
831             this.typing = typing;
832         }
833
834         public void SetAway(bool away)
835         {
836             Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();
837             awayAnim.Add(awayAnimationID, away);
838
839             client.Self.Animate(awayAnim, true);
840             if (UseMoveControl) client.Self.Movement.Away = away;
841             this.away = away;
842         }
843
844         public void SetBusy(bool busy)
845         {
846             Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();
847             busyAnim.Add(busyAnimationID, busy);
848
849             client.Self.Animate(busyAnim, true);
850             this.busy = busy;
851         }
852
853         public void SetFlying(bool flying)
854         {
855             this.flying = client.Self.Movement.Fly = flying;
856         }
857
858         public void SetAlwaysRun(bool alwaysrun)
859         {
860             this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;
861         }
862
863         public void SetSitting(bool sitting, UUID target)
864         {
865             this.sitting = sitting;
866
867             if (sitting)
868             {
869                 client.Self.RequestSit(target, Vector3.Zero);
870                 client.Self.Sit();
871             }
872             else
873             {
874                 if (!instance.RLV.RestictionActive("unsit"))
875                 {
876                     client.Self.Stand();
877                 }
878                 else
879                 {
880                     instance.TabConsole.DisplayNotificationInChat("Unsit prevented by RLV");
881                     this.sitting = true;
882                     return;
883                 }
884             }
885
886             if (SitStateChanged != null)
887             {
888                 SitStateChanged(this, new SitEventArgs(this.sitting));
889             }
890
891             if (!this.sitting)
892             {
893                 StopAllAnimations();
894             }
895         }
896
897         public void StopAllAnimations()
898         {
899             Dictionary<UUID, bool> stop = new Dictionary<UUID, bool>();
900
901             client.Self.SignaledAnimations.ForEach((UUID anim) =>
902             {
903                 if (!KnownAnimations.ContainsKey(anim))
904                 {
905                     stop.Add(anim, false);
906                 }
907             });
908
909             if (stop.Count > 0)
910             {
911                 client.Self.Animate(stop, true);
912             }
913         }
914
915         static public Vector3d GlobalPosition(Simulator sim, Vector3 pos)
916         {
917             uint globalX, globalY;
918             Utils.LongToUInts(sim.Handle, out globalX, out globalY);
919
920             return new Vector3d(
921                 (double)globalX + (double)pos.X,
922                 (double)globalY + (double)pos.Y,
923                 (double)pos.Z);
924         }
925
926         public Vector3d GlobalPosition(Primitive prim)
927         {
928             return GlobalPosition(client.Network.CurrentSim, prim.Position);
929         }
930
931         private System.Timers.Timer beamTimer;
932         private List<Vector3d> beamTarget;
933         private Random beamRandom = new Random();
934         private UUID pointID;
935         private UUID sphereID;
936         private List<UUID> beamID;
937         private int numBeans;
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) };
939         private Primitive targetPrim;
940
941         public void UnSetPointing()
942         {
943             beamTimer.Enabled = false;
944             if (pointID != UUID.Zero)
945             {
946                 client.Self.PointAtEffect(client.Self.AgentID, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);
947                 pointID = UUID.Zero;
948             }
949
950             if (beamID != null)
951             {
952                 foreach (UUID id in beamID)
953                 {
954                     client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);
955                 }
956                 beamID = null;
957             }
958
959             if (sphereID != UUID.Zero)
960             {
961                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);
962                 sphereID = UUID.Zero;
963             }
964
965         }
966
967         void beamTimer_Elapsed(object sender, EventArgs e)
968         {
969             if (beamID == null) return;
970
971             try
972             {
973                 client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);
974                 int i = 0;
975                 for (i = 0; i < numBeans; i++)
976                 {
977                     UUID newBeam = UUID.Random();
978                     Vector3d scatter;
979
980                     if (i == 0)
981                     {
982                         scatter = GlobalPosition(targetPrim);
983                     }
984                     else
985                     {
986                         Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);
987                         Vector3d cross = direction % new Vector3d(0, 0, 1);
988                         cross.Normalize();
989                         scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);
990                     }
991                     client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);
992                 }
993
994                 for (int j = 1; j < numBeans; j++)
995                 {
996                     UUID newBeam = UUID.Random();
997                     Vector3d scatter;
998                     Vector3d cross = new Vector3d(0, 0, 1);
999                     cross.Normalize();
1000                     scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);
1001
1002                     client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);
1003                 }
1004             }
1005             catch (Exception) { };
1006
1007         }
1008
1009         public void SetPointing(Primitive prim, int numBeans)
1010         {
1011             UnSetPointing();
1012             client.Self.Movement.TurnToward(prim.Position);
1013             pointID = UUID.Random();
1014             sphereID = UUID.Random();
1015             beamID = new List<UUID>();
1016             beamTarget = new List<Vector3d>();
1017             targetPrim = prim;
1018             this.numBeans = numBeans;
1019
1020             client.Self.PointAtEffect(client.Self.AgentID, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);
1021
1022             for (int i = 0; i < numBeans; i++)
1023             {
1024                 UUID newBeam = UUID.Random();
1025                 beamID.Add(newBeam);
1026                 beamTarget.Add(Vector3d.Zero);
1027             }
1028
1029             for (int i = 1; i < numBeans; i++)
1030             {
1031                 UUID newBeam = UUID.Random();
1032                 beamID.Add(newBeam);
1033                 beamTarget.Add(Vector3d.Zero);
1034             }
1035
1036             beamTimer.Interval = 1000;
1037             beamTimer.Enabled = true;
1038         }
1039
1040         public UUID TypingAnimationID
1041         {
1042             get { return typingAnimationID; }
1043             set { typingAnimationID = value; }
1044         }
1045
1046         public UUID AwayAnimationID
1047         {
1048             get { return awayAnimationID; }
1049             set { awayAnimationID = value; }
1050         }
1051
1052         public UUID BusyAnimationID
1053         {
1054             get { return busyAnimationID; }
1055             set { busyAnimationID = value; }
1056         }
1057
1058         public bool IsTyping
1059         {
1060             get { return typing; }
1061         }
1062
1063         public bool IsAway
1064         {
1065             get
1066             {
1067                 if (UseMoveControl) return client.Self.Movement.Away;
1068                 return away;
1069             }
1070         }
1071
1072         public bool IsBusy
1073         {
1074             get { return busy; }
1075         }
1076
1077         public bool IsFlying
1078         {
1079             get { return client.Self.Movement.Fly; }
1080         }
1081
1082         public bool IsSitting
1083         {
1084             get
1085             {
1086                 if (client.Self.Movement.SitOnGround || client.Self.SittingOn != 0) return true;
1087                 if (sitting) {
1088                     Logger.Log("out of sync sitting", Helpers.LogLevel.Debug);
1089                     sitting = false;
1090                 }
1091                 return false;
1092             }
1093         }
1094
1095         public bool IsPointing
1096         {
1097             get { return pointID != UUID.Zero; }
1098         }
1099
1100         public bool IsFollowing
1101         {
1102             get { return following; }
1103         }
1104
1105         public string FollowName
1106         {
1107             get { return followName; }
1108             set { followName = value; }
1109         }
1110
1111         public float FollowDistance
1112         {
1113             get { return followDistance; }
1114             set { followDistance = value; }
1115         }
1116
1117         public bool IsWalking
1118         {
1119             get { return walking; }
1120         }
1121
1122         private AutoSit autosit;
1123         public AutoSit AutoSit
1124         {
1125             get { return autosit; }
1126         }
1127
1128         private PseudoHome pseudohome;
1129
1130         /// <summary>
1131         /// Experimental Option that sometimes the Client has more authority than state mananger
1132         /// </summary>
1133         public static bool UseMoveControl;
1134
1135         public PseudoHome PseudoHome
1136         {
1137             get { return pseudohome; }
1138         }
1139     }
1140
1141     public class SitEventArgs : EventArgs
1142     {
1143         public bool Sitting;
1144
1145         public SitEventArgs(bool sitting)
1146         {
1147             this.Sitting = sitting;
1148         }
1149     }
1150 }