OSDN Git Service

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