OSDN Git Service

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