OSDN Git Service

Set eol style property
[radegast/radegast.git] / Radegast / Core / StateManager.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.Timers;
34 using System.Threading;
35 using OpenMetaverse;
36 using Radegast.Netcom;
37
38 namespace Radegast
39 {
40     public class StateManager : IDisposable
41     {
42         private RadegastInstance instance;
43         private GridClient client { get { return instance.Client; } }
44         private RadegastNetcom netcom { get { return instance.Netcom; } }
45
46         private bool typing = false;
47         private bool away = false;
48         private bool busy = false;
49         private bool flying = false;
50         private bool alwaysrun = false;
51         private bool sitting = false;
52
53         private bool following = false;
54         private string followName = string.Empty;
55         private float followDistance = 3.0f;
56
57         private UUID awayAnimationID = new UUID("fd037134-85d4-f241-72c6-4f42164fedee");
58         private UUID busyAnimationID = new UUID("efcf670c2d188128973a034ebc806b67");
59         private UUID typingAnimationID = new UUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
60         internal static Random rnd = new Random();
61         private System.Threading.Timer lookAtTimer;
62
63         /// <summary>
64         /// Passes walk state
65         /// </summary>
66         /// <param name="walking">True if we are walking towards a targer</param>
67         public delegate void WalkStateCanged(bool walking);
68
69         /// <summary>
70         /// Fires when we start or stop walking towards a target
71         /// </summary>
72         public event WalkStateCanged OnWalkStateCanged;
73
74
75         public StateManager(RadegastInstance instance)
76         {
77             this.instance = instance;
78             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
79
80             beamTimer = new System.Timers.Timer();
81             beamTimer.Enabled = false;
82             beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);
83
84             // Callbacks
85             netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);
86             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
87             netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);
88             RegisterClientEvents(client);
89         }
90
91
92         private void RegisterClientEvents(GridClient client)
93         {
94             client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
95             client.Self.AlertMessage += new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
96             client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
97             client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
98         }
99
100         private void UnregisterClientEvents(GridClient client)
101         {
102             client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
103             client.Self.AlertMessage -= new EventHandler<AlertMessageEventArgs>(Self_AlertMessage);
104             client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
105             client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
106         }
107
108         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
109         {
110             UnregisterClientEvents(e.OldClient);
111             RegisterClientEvents(client);
112         }
113
114         public void Dispose()
115         {
116             netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);
117             netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
118             netcom.ChatReceived -= new EventHandler<ChatEventArgs>(netcom_ChatReceived);
119             UnregisterClientEvents(client);
120             beamTimer.Dispose();
121             beamTimer = null;
122
123             if (lookAtTimer != null)
124             {
125                 lookAtTimer.Dispose();
126                 lookAtTimer = null;
127             }
128
129             if (walkTimer != null)
130             {
131                 walkTimer.Dispose();
132                 walkTimer = null;
133             }
134         }
135
136         public void SetRandomHeading()
137         {
138             client.Self.Movement.UpdateFromHeading(Utils.TWO_PI * rnd.NextDouble(), true);
139             LookInFront();
140         }
141
142         void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
143         {
144             if (e.Simulator == client.Network.CurrentSim)
145             {
146                 SetRandomHeading();
147             }
148         }
149
150         private UUID teleportEffect = UUID.Random();
151
152         void Self_TeleportProgress(object sender, TeleportEventArgs e)
153         {
154             if (!client.Network.Connected) return;
155
156             if (e.Status == TeleportStatus.Progress)
157             {
158                 client.Self.SphereEffect(client.Self.GlobalPosition, Color4.White, 4f, teleportEffect);
159             }
160
161             if (e.Status == TeleportStatus.Finished)
162             {
163                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
164                 SetRandomHeading();
165             }
166
167             if (e.Status == TeleportStatus.Failed)
168             {
169                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
170             }
171         }
172
173         void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
174         {
175             typing = away = busy = walking = false;
176
177             if (lookAtTimer != null)
178             {
179                 lookAtTimer.Dispose();
180                 lookAtTimer = null;
181             }
182
183         }
184
185         void netcom_ClientConnected(object sender, EventArgs e)
186         {
187             client.Self.Movement.Camera.Far = 256f;
188             effectSource = client.Self.AgentID;
189
190             if (lookAtTimer == null)
191                 lookAtTimer = new System.Threading.Timer(new TimerCallback(lookAtTimerTick), null, Timeout.Infinite, Timeout.Infinite);
192         }
193
194         void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
195         {
196             if (!e.Update.Avatar) return;
197             if (!following) return;
198
199             Avatar av;
200             client.Network.CurrentSim.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av);
201             if (av == null) return;
202
203             if (av.Name == followName)
204             {
205                 Vector3 pos;
206
207                 if (av.ParentID == 0)
208                 {
209                     pos = av.Position;
210                 }
211                 else
212                 {
213                     Primitive prim;
214                     client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim);
215
216                     if (prim == null)
217                         pos = client.Self.SimPosition;
218                     else
219                         pos = prim.Position + av.Position;
220                 }
221
222                 if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)
223                 {
224                     int followRegionX = (int)(e.Simulator.Handle >> 32);
225                     int followRegionY = (int)(e.Simulator.Handle & 0xFFFFFFFF);
226                     ulong x = (ulong)(pos.X + followRegionX);
227                     ulong y = (ulong)(pos.Y + followRegionY);
228
229                     client.Self.AutoPilotCancel();
230                     client.Self.AutoPilot(x, y, pos.Z);
231                 }
232             }
233         }
234
235         #region Look at effect
236         private int lastLookAtEffect = 0;
237         private UUID lookAtEffect = UUID.Random();
238
239         /// <summary>
240         /// Set eye focus 3m in front of us
241         /// </summary>
242         public void LookInFront()
243         {
244             if (!client.Network.Connected) return;
245
246             client.Self.LookAtEffect(client.Self.AgentID, client.Self.AgentID,
247                 new Vector3d(new Vector3(3, 0, 0) * Quaternion.Identity),
248                 LookAtType.Idle, lookAtEffect);
249         }
250
251         void lookAtTimerTick(object state)
252         {
253             LookInFront();
254         }
255
256         void netcom_ChatReceived(object sender, ChatEventArgs e)
257         {
258             if (e.SourceID != client.Self.AgentID && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))
259             {
260                 // change focus max every 4 seconds
261                 if (Environment.TickCount - lastLookAtEffect > 4000)
262                 {
263                     lastLookAtEffect = Environment.TickCount;
264                     client.Self.LookAtEffect(client.Self.AgentID, e.SourceID, Vector3d.Zero, LookAtType.Respond, lookAtEffect);
265                     // keep looking at the speaker for 10 seconds
266                     if (lookAtTimer != null)
267                     {
268                         lookAtTimer.Change(10000, Timeout.Infinite);
269                     }
270                 }
271             }
272         }
273         #endregion Look at effect
274
275         public void Follow(string name)
276         {
277             followName = name;
278             following = !string.IsNullOrEmpty(followName);
279
280             if (following)
281             {
282                 walking = false;
283             }
284         }
285
286         #region Walking (move to)
287         private bool walking = false;
288         private System.Threading.Timer walkTimer;
289         private int walkChekInterval = 500;
290         private Vector3d walkToTarget;
291         int lastDistance = 0;
292         int lastDistanceChanged = 0;
293
294         public void WalkTo(Primitive prim)
295         {
296             WalkTo(GlobalPosition(prim));
297         }
298
299         public void WalkTo(Vector3d globalPos)
300         {
301             walkToTarget = globalPos;
302
303             if (following)
304             {
305                 following = false;
306                 followName = string.Empty;
307             }
308
309             if (walkTimer == null)
310             {
311                 walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);
312             }
313
314             lastDistanceChanged = System.Environment.TickCount;
315             client.Self.AutoPilotCancel();
316             walking = true;
317             client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);
318             FireWalkStateCanged();
319         }
320
321         void walkTimerElapsed(object sender)
322         {
323
324             double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);
325
326             if (distance < 2d)
327             {
328                 // We're there
329                 EndWalking();
330             }
331             else
332             {
333                 if (lastDistance != (int)distance)
334                 {
335                     lastDistanceChanged = System.Environment.TickCount;
336                     lastDistance = (int)distance;
337                 }
338                 else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)
339                 {
340                     // Our distance to the target has not changed in 10s, give up
341                     EndWalking();
342                     return;
343                 }
344                 walkTimer.Change(walkChekInterval, Timeout.Infinite);
345             }
346         }
347
348         void Self_AlertMessage(object sender, AlertMessageEventArgs e)
349         {
350             if (e.Message.Contains("Autopilot cancel"))
351             {
352                 if (walking)
353                 {
354                     EndWalking();
355                 }
356             }
357         }
358
359         void FireWalkStateCanged()
360         {
361             if (OnWalkStateCanged != null)
362             {
363                 try { OnWalkStateCanged(walking); }
364                 catch (Exception) { }
365             }
366         }
367
368         public void EndWalking()
369         {
370             if (walking)
371             {
372                 walking = false;
373                 Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);
374                 walkTimer.Dispose();
375                 walkTimer = null;
376                 client.Self.AutoPilotCancel();
377                 FireWalkStateCanged();
378             }
379         }
380         #endregion
381
382         public void SetTyping(bool typing)
383         {
384             Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();
385             typingAnim.Add(typingAnimationID, typing);
386
387             client.Self.Animate(typingAnim, false);
388
389             if (typing)
390                 client.Self.Chat(string.Empty, 0, ChatType.StartTyping);
391             else
392                 client.Self.Chat(string.Empty, 0, ChatType.StopTyping);
393
394             this.typing = typing;
395         }
396
397         public void SetAway(bool away)
398         {
399             Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();
400             awayAnim.Add(awayAnimationID, away);
401
402             client.Self.Animate(awayAnim, true);
403             this.away = away;
404         }
405
406         public void SetBusy(bool busy)
407         {
408             Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();
409             busyAnim.Add(busyAnimationID, busy);
410
411             client.Self.Animate(busyAnim, true);
412             this.busy = busy;
413         }
414
415         public void SetFlying(bool flying)
416         {
417             this.flying = client.Self.Movement.Fly = flying;
418         }
419
420         public void SetAlwaysRun(bool alwaysrun)
421         {
422             this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;
423         }
424
425         public void SetSitting(bool sitting, UUID target)
426         {
427             this.sitting = sitting;
428
429             if (sitting)
430             {
431                 client.Self.RequestSit(target, Vector3.Zero);
432                 client.Self.Sit();
433             }
434             else
435             {
436                 client.Self.Stand();
437             }
438         }
439
440         public Vector3d GlobalPosition(Simulator sim, Vector3 pos)
441         {
442             uint globalX, globalY;
443             Utils.LongToUInts(sim.Handle, out globalX, out globalY);
444
445             return new Vector3d(
446                 (double)globalX + (double)pos.X,
447                 (double)globalY + (double)pos.Y,
448                 (double)pos.Z);
449         }
450
451         public Vector3d GlobalPosition(Primitive prim)
452         {
453             return GlobalPosition(client.Network.CurrentSim, prim.Position);
454         }
455
456         private System.Timers.Timer beamTimer;
457         private List<Vector3d> beamTarget;
458         private Random beamRandom = new Random();
459         private UUID pointID;
460         private UUID sphereID;
461         private List<UUID> beamID;
462         private int numBeans;
463         private Color4[] beamColors = new Color4[3] { new Color4(0, 255, 0, 255), new Color4(255, 0, 0, 255), new Color4(0, 0, 255, 255) };
464         private Primitive targetPrim;
465         private UUID effectSource;
466
467         public void UnSetPointing()
468         {
469             beamTimer.Enabled = false;
470             if (pointID != UUID.Zero)
471             {
472                 client.Self.PointAtEffect(effectSource, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);
473                 pointID = UUID.Zero;
474             }
475
476             if (beamID != null)
477             {
478                 foreach (UUID id in beamID)
479                 {
480                     client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);
481                 }
482                 beamID = null;
483             }
484
485             if (sphereID != UUID.Zero)
486             {
487                 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);
488                 sphereID = UUID.Zero;
489             }
490
491         }
492
493         void beamTimer_Elapsed(object sender, EventArgs e)
494         {
495             if (beamID == null) return;
496
497             try
498             {
499                 client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);
500                 int i = 0;
501                 for (i = 0; i < numBeans; i++)
502                 {
503                     UUID newBeam = UUID.Random();
504                     Vector3d scatter;
505
506                     if (i == 0)
507                     {
508                         scatter = GlobalPosition(targetPrim);
509                     }
510                     else
511                     {
512                         Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);
513                         Vector3d cross = direction % new Vector3d(0, 0, 1);
514                         cross.Normalize();
515                         scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);
516                     }
517                     client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);
518                 }
519
520                 for (int j = 1; j < numBeans; j++)
521                 {
522                     UUID newBeam = UUID.Random();
523                     Vector3d scatter;
524                     Vector3d cross = new Vector3d(0, 0, 1);
525                     cross.Normalize();
526                     scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);
527
528                     client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);
529                 }
530             }
531             catch (Exception) { };
532
533         }
534
535         public void SetPointing(Primitive prim, int numBeans)
536         {
537             UnSetPointing();
538             client.Self.Movement.TurnToward(prim.Position);
539             pointID = UUID.Random();
540             sphereID = UUID.Random();
541             beamID = new List<UUID>();
542             beamTarget = new List<Vector3d>();
543             targetPrim = prim;
544             this.numBeans = numBeans;
545
546             client.Self.PointAtEffect(effectSource, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);
547
548             for (int i = 0; i < numBeans; i++)
549             {
550                 UUID newBeam = UUID.Random();
551                 beamID.Add(newBeam);
552                 beamTarget.Add(Vector3d.Zero);
553             }
554
555             for (int i = 1; i < numBeans; i++)
556             {
557                 UUID newBeam = UUID.Random();
558                 beamID.Add(newBeam);
559                 beamTarget.Add(Vector3d.Zero);
560             }
561
562             beamTimer.Interval = 1000;
563             beamTimer.Enabled = true;
564         }
565
566         public UUID TypingAnimationID
567         {
568             get { return typingAnimationID; }
569             set { typingAnimationID = value; }
570         }
571
572         public UUID AwayAnimationID
573         {
574             get { return awayAnimationID; }
575             set { awayAnimationID = value; }
576         }
577
578         public UUID BusyAnimationID
579         {
580             get { return busyAnimationID; }
581             set { busyAnimationID = value; }
582         }
583
584         public UUID EffectSource
585         {
586             get { return effectSource; }
587             set { effectSource = value; }
588         }
589
590         public bool IsTyping
591         {
592             get { return typing; }
593         }
594
595         public bool IsAway
596         {
597             get { return away; }
598         }
599
600         public bool IsBusy
601         {
602             get { return busy; }
603         }
604
605         public bool IsFlying
606         {
607             get { return flying; }
608         }
609
610         public bool IsSitting
611         {
612             get { return sitting; }
613         }
614
615         public bool IsPointing
616         {
617             get { return pointID != UUID.Zero; }
618         }
619
620         public bool IsFollowing
621         {
622             get { return following; }
623         }
624
625         public string FollowName
626         {
627             get { return followName; }
628             set { followName = value; }
629         }
630
631         public float FollowDistance
632         {
633             get { return followDistance; }
634             set { followDistance = value; }
635         }
636
637         public bool IsWalking
638         {
639             get { return walking; }
640         }
641     }
642 }