OSDN Git Service

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