OSDN Git Service

Experimental Option that sometimes the Client has more authority than state manager
[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             //somehow it can be too early (when Radegast is loaded from running bot)\r
672             if (instance.GlobalSettings==null) return;\r
673             if (!instance.GlobalSettings["disable_look_at"]\r
674                 && e.SourceID != client.Self.AgentID\r
675                 && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))\r
676             {\r
677                 // change focus max every 4 seconds\r
678                 if (Environment.TickCount - lastLookAtEffect > 4000)\r
679                 {\r
680                     lastLookAtEffect = Environment.TickCount;\r
681                     client.Self.LookAtEffect(client.Self.AgentID, e.SourceID, Vector3d.Zero, LookAtType.Respond, lookAtEffect);\r
682                     // keep looking at the speaker for 10 seconds\r
683                     if (lookAtTimer != null)\r
684                     {\r
685                         lookAtTimer.Change(10000, Timeout.Infinite);\r
686                     }\r
687                 }\r
688             }\r
689         }\r
690         #endregion Look at effect\r
691 \r
692         #region Walking (move to)\r
693         private bool walking = false;\r
694         private System.Threading.Timer walkTimer;\r
695         private int walkChekInterval = 500;\r
696         private Vector3d walkToTarget;\r
697         int lastDistance = 0;\r
698         int lastDistanceChanged = 0;\r
699 \r
700         public void WalkTo(Primitive prim)\r
701         {\r
702             WalkTo(GlobalPosition(prim));\r
703         }\r
704         public double WaitUntilPosition(Vector3d pos, TimeSpan maxWait, double howClose)\r
705         {\r
706              \r
707             DateTime until = DateTime.Now + maxWait;\r
708             while (until > DateTime.Now)\r
709             {\r
710                 double dist = Vector3d.Distance(client.Self.GlobalPosition, pos);\r
711                 if (howClose >= dist) return dist;\r
712                 Thread.Sleep(250);\r
713             }\r
714             return Vector3d.Distance(client.Self.GlobalPosition, pos);\r
715             \r
716         }\r
717 \r
718         public void WalkTo(Vector3d globalPos)\r
719         {\r
720             walkToTarget = globalPos;\r
721 \r
722             if (following)\r
723             {\r
724                 following = false;\r
725                 followName = string.Empty;\r
726             }\r
727 \r
728             if (walkTimer == null)\r
729             {\r
730                 walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);\r
731             }\r
732 \r
733             lastDistanceChanged = System.Environment.TickCount;\r
734             client.Self.AutoPilotCancel();\r
735             walking = true;\r
736             client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);\r
737             FireWalkStateCanged();\r
738         }\r
739 \r
740         void walkTimerElapsed(object sender)\r
741         {\r
742 \r
743             double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);\r
744 \r
745             if (distance < 2d)\r
746             {\r
747                 // We're there\r
748                 EndWalking();\r
749             }\r
750             else\r
751             {\r
752                 if (lastDistance != (int)distance)\r
753                 {\r
754                     lastDistanceChanged = System.Environment.TickCount;\r
755                     lastDistance = (int)distance;\r
756                 }\r
757                 else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)\r
758                 {\r
759                     // Our distance to the target has not changed in 10s, give up\r
760                     EndWalking();\r
761                     return;\r
762                 }\r
763                 if (walkTimer != null) walkTimer.Change(walkChekInterval, Timeout.Infinite);\r
764             }\r
765         }\r
766 \r
767         void Self_AlertMessage(object sender, AlertMessageEventArgs e)\r
768         {\r
769             if (e.Message.Contains("Autopilot cancel"))\r
770             {\r
771                 if (walking)\r
772                 {\r
773                     EndWalking();\r
774                 }\r
775             }\r
776         }\r
777 \r
778         void FireWalkStateCanged()\r
779         {\r
780             if (OnWalkStateCanged != null)\r
781             {\r
782                 try { OnWalkStateCanged(walking); }\r
783                 catch (Exception) { }\r
784             }\r
785         }\r
786 \r
787         public void EndWalking()\r
788         {\r
789             if (walking)\r
790             {\r
791                 walking = false;\r
792                 Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);\r
793                 walkTimer.Dispose();\r
794                 walkTimer = null;\r
795                 client.Self.AutoPilotCancel();\r
796                 \r
797                 if (displayEndWalk)\r
798                 {\r
799                     displayEndWalk = false;\r
800                     string msg = "Finished walking";\r
801 \r
802                     if (walkToTarget != Vector3d.Zero)\r
803                     {\r
804                         System.Threading.Thread.Sleep(1000);\r
805                         msg += string.Format(" {0:0} meters from destination", Vector3d.Distance(client.Self.GlobalPosition, walkToTarget));\r
806                         walkToTarget = Vector3d.Zero;\r
807                     }\r
808 \r
809                     instance.TabConsole.DisplayNotificationInChat(msg);\r
810                 }\r
811 \r
812                 FireWalkStateCanged();\r
813             }\r
814         }\r
815         #endregion\r
816 \r
817         public void SetTyping(bool typing)\r
818         {\r
819             if (!client.Network.Connected) return;\r
820 \r
821             Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();\r
822             typingAnim.Add(typingAnimationID, typing);\r
823 \r
824             client.Self.Animate(typingAnim, false);\r
825 \r
826             if (typing)\r
827                 client.Self.Chat(string.Empty, 0, ChatType.StartTyping);\r
828             else\r
829                 client.Self.Chat(string.Empty, 0, ChatType.StopTyping);\r
830 \r
831             this.typing = typing;\r
832         }\r
833 \r
834         public void SetAway(bool away)\r
835         {\r
836             Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();\r
837             awayAnim.Add(awayAnimationID, away);\r
838 \r
839             client.Self.Animate(awayAnim, true);\r
840             if (UseMoveControl) client.Self.Movement.Away = away;\r
841             this.away = away;\r
842         }\r
843 \r
844         public void SetBusy(bool busy)\r
845         {\r
846             Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();\r
847             busyAnim.Add(busyAnimationID, busy);\r
848 \r
849             client.Self.Animate(busyAnim, true);\r
850             this.busy = busy;\r
851         }\r
852 \r
853         public void SetFlying(bool flying)\r
854         {\r
855             this.flying = client.Self.Movement.Fly = flying;\r
856         }\r
857 \r
858         public void SetAlwaysRun(bool alwaysrun)\r
859         {\r
860             this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;\r
861         }\r
862 \r
863         public void SetSitting(bool sitting, UUID target)\r
864         {\r
865             this.sitting = sitting;\r
866 \r
867             if (sitting)\r
868             {\r
869                 client.Self.RequestSit(target, Vector3.Zero);\r
870                 client.Self.Sit();\r
871             }\r
872             else\r
873             {\r
874                 if (!instance.RLV.RestictionActive("unsit"))\r
875                 {\r
876                     client.Self.Stand();\r
877                 }\r
878                 else\r
879                 {\r
880                     instance.TabConsole.DisplayNotificationInChat("Unsit prevented by RLV");\r
881                     this.sitting = true;\r
882                     return;\r
883                 }\r
884             }\r
885 \r
886             if (SitStateChanged != null)\r
887             {\r
888                 SitStateChanged(this, new SitEventArgs(this.sitting));\r
889             }\r
890 \r
891             if (!this.sitting)\r
892             {\r
893                 StopAllAnimations();\r
894             }\r
895         }\r
896 \r
897         public void StopAllAnimations()\r
898         {\r
899             Dictionary<UUID, bool> stop = new Dictionary<UUID, bool>();\r
900 \r
901             client.Self.SignaledAnimations.ForEach((UUID anim) =>\r
902             {\r
903                 if (!KnownAnimations.ContainsKey(anim))\r
904                 {\r
905                     stop.Add(anim, false);\r
906                 }\r
907             });\r
908 \r
909             if (stop.Count > 0)\r
910             {\r
911                 client.Self.Animate(stop, true);\r
912             }\r
913         }\r
914 \r
915         static public Vector3d GlobalPosition(Simulator sim, Vector3 pos)\r
916         {\r
917             uint globalX, globalY;\r
918             Utils.LongToUInts(sim.Handle, out globalX, out globalY);\r
919 \r
920             return new Vector3d(\r
921                 (double)globalX + (double)pos.X,\r
922                 (double)globalY + (double)pos.Y,\r
923                 (double)pos.Z);\r
924         }\r
925 \r
926         public Vector3d GlobalPosition(Primitive prim)\r
927         {\r
928             return GlobalPosition(client.Network.CurrentSim, prim.Position);\r
929         }\r
930 \r
931         private System.Timers.Timer beamTimer;\r
932         private List<Vector3d> beamTarget;\r
933         private Random beamRandom = new Random();\r
934         private UUID pointID;\r
935         private UUID sphereID;\r
936         private List<UUID> beamID;\r
937         private int numBeans;\r
938         private Color4[] beamColors = new Color4[3] { new Color4(0, 255, 0, 255), new Color4(255, 0, 0, 255), new Color4(0, 0, 255, 255) };\r
939         private Primitive targetPrim;\r
940 \r
941         public void UnSetPointing()\r
942         {\r
943             beamTimer.Enabled = false;\r
944             if (pointID != UUID.Zero)\r
945             {\r
946                 client.Self.PointAtEffect(client.Self.AgentID, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);\r
947                 pointID = UUID.Zero;\r
948             }\r
949 \r
950             if (beamID != null)\r
951             {\r
952                 foreach (UUID id in beamID)\r
953                 {\r
954                     client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);\r
955                 }\r
956                 beamID = null;\r
957             }\r
958 \r
959             if (sphereID != UUID.Zero)\r
960             {\r
961                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);\r
962                 sphereID = UUID.Zero;\r
963             }\r
964 \r
965         }\r
966 \r
967         void beamTimer_Elapsed(object sender, EventArgs e)\r
968         {\r
969             if (beamID == null) return;\r
970 \r
971             try\r
972             {\r
973                 client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);\r
974                 int i = 0;\r
975                 for (i = 0; i < numBeans; i++)\r
976                 {\r
977                     UUID newBeam = UUID.Random();\r
978                     Vector3d scatter;\r
979 \r
980                     if (i == 0)\r
981                     {\r
982                         scatter = GlobalPosition(targetPrim);\r
983                     }\r
984                     else\r
985                     {\r
986                         Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);\r
987                         Vector3d cross = direction % new Vector3d(0, 0, 1);\r
988                         cross.Normalize();\r
989                         scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);\r
990                     }\r
991                     client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);\r
992                 }\r
993 \r
994                 for (int j = 1; j < numBeans; j++)\r
995                 {\r
996                     UUID newBeam = UUID.Random();\r
997                     Vector3d scatter;\r
998                     Vector3d cross = new Vector3d(0, 0, 1);\r
999                     cross.Normalize();\r
1000                     scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);\r
1001 \r
1002                     client.Self.BeamEffect(client.Self.AgentID, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);\r
1003                 }\r
1004             }\r
1005             catch (Exception) { };\r
1006 \r
1007         }\r
1008 \r
1009         public void SetPointing(Primitive prim, int numBeans)\r
1010         {\r
1011             UnSetPointing();\r
1012             client.Self.Movement.TurnToward(prim.Position);\r
1013             pointID = UUID.Random();\r
1014             sphereID = UUID.Random();\r
1015             beamID = new List<UUID>();\r
1016             beamTarget = new List<Vector3d>();\r
1017             targetPrim = prim;\r
1018             this.numBeans = numBeans;\r
1019 \r
1020             client.Self.PointAtEffect(client.Self.AgentID, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);\r
1021 \r
1022             for (int i = 0; i < numBeans; i++)\r
1023             {\r
1024                 UUID newBeam = UUID.Random();\r
1025                 beamID.Add(newBeam);\r
1026                 beamTarget.Add(Vector3d.Zero);\r
1027             }\r
1028 \r
1029             for (int i = 1; i < numBeans; i++)\r
1030             {\r
1031                 UUID newBeam = UUID.Random();\r
1032                 beamID.Add(newBeam);\r
1033                 beamTarget.Add(Vector3d.Zero);\r
1034             }\r
1035 \r
1036             beamTimer.Interval = 1000;\r
1037             beamTimer.Enabled = true;\r
1038         }\r
1039 \r
1040         public UUID TypingAnimationID\r
1041         {\r
1042             get { return typingAnimationID; }\r
1043             set { typingAnimationID = value; }\r
1044         }\r
1045 \r
1046         public UUID AwayAnimationID\r
1047         {\r
1048             get { return awayAnimationID; }\r
1049             set { awayAnimationID = value; }\r
1050         }\r
1051 \r
1052         public UUID BusyAnimationID\r
1053         {\r
1054             get { return busyAnimationID; }\r
1055             set { busyAnimationID = value; }\r
1056         }\r
1057 \r
1058         public bool IsTyping\r
1059         {\r
1060             get { return typing; }\r
1061         }\r
1062 \r
1063         public bool IsAway\r
1064         {\r
1065             get\r
1066             {\r
1067                 if (UseMoveControl) return client.Self.Movement.Away;\r
1068                 return away;\r
1069             }\r
1070         }\r
1071 \r
1072         public bool IsBusy\r
1073         {\r
1074             get { return busy; }\r
1075         }\r
1076 \r
1077         public bool IsFlying\r
1078         {\r
1079             get { return client.Self.Movement.Fly; }\r
1080         }\r
1081 \r
1082         public bool IsSitting\r
1083         {\r
1084             get\r
1085             {\r
1086                 if (client.Self.Movement.SitOnGround || client.Self.SittingOn != 0) return true;\r
1087                 if (sitting) {\r
1088                     Logger.Log("out of sync sitting", Helpers.LogLevel.Debug);\r
1089                     sitting = false;\r
1090                 }\r
1091                 return false;\r
1092             }\r
1093         }\r
1094 \r
1095         public bool IsPointing\r
1096         {\r
1097             get { return pointID != UUID.Zero; }\r
1098         }\r
1099 \r
1100         public bool IsFollowing\r
1101         {\r
1102             get { return following; }\r
1103         }\r
1104 \r
1105         public string FollowName\r
1106         {\r
1107             get { return followName; }\r
1108             set { followName = value; }\r
1109         }\r
1110 \r
1111         public float FollowDistance\r
1112         {\r
1113             get { return followDistance; }\r
1114             set { followDistance = value; }\r
1115         }\r
1116 \r
1117         public bool IsWalking\r
1118         {\r
1119             get { return walking; }\r
1120         }\r
1121 \r
1122         private AutoSit autosit;\r
1123         public AutoSit AutoSit\r
1124         {\r
1125             get { return autosit; }\r
1126         }\r
1127 \r
1128         private PseudoHome pseudohome;\r
1129 \r
1130         /// <summary>\r
1131         /// Experimental Option that sometimes the Client has more authority than state mananger\r
1132         /// </summary>\r
1133         public static bool UseMoveControl;\r
1134 \r
1135         public PseudoHome PseudoHome\r
1136         {\r
1137             get { return pseudohome; }\r
1138         }\r
1139     }\r
1140 \r
1141     public class SitEventArgs : EventArgs\r
1142     {\r
1143         public bool Sitting;\r
1144 \r
1145         public SitEventArgs(bool sitting)\r
1146         {\r
1147             this.Sitting = sitting;\r
1148         }\r
1149     }\r
1150 }\r