2 // Radegast Metaverse Client
3 // Copyright (c) 2009, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
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.
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.
32 using System.Collections.Generic;
34 using System.Threading;
36 using Radegast.Netcom;
40 public class StateManager : IDisposable
42 private RadegastInstance instance;
43 private GridClient client { get { return instance.Client; } }
44 private RadegastNetcom netcom { get { return instance.Netcom; } }
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;
53 private bool following = false;
54 private string followName = string.Empty;
55 private float followDistance = 3.0f;
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;
66 /// <param name="walking">True if we are walking towards a targer</param>
67 public delegate void WalkStateCanged(bool walking);
70 /// Fires when we start or stop walking towards a target
72 public event WalkStateCanged OnWalkStateCanged;
75 public StateManager(RadegastInstance instance)
77 this.instance = instance;
78 this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
80 beamTimer = new System.Timers.Timer();
81 beamTimer.Enabled = false;
82 beamTimer.Elapsed += new ElapsedEventHandler(beamTimer_Elapsed);
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);
92 private void RegisterClientEvents(GridClient client)
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);
100 private void UnregisterClientEvents(GridClient client)
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);
108 void instance_ClientChanged(object sender, ClientChangedEventArgs e)
110 UnregisterClientEvents(e.OldClient);
111 RegisterClientEvents(client);
114 public void Dispose()
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);
123 if (lookAtTimer != null)
125 lookAtTimer.Dispose();
129 if (walkTimer != null)
136 public void SetRandomHeading()
138 client.Self.Movement.UpdateFromHeading(Utils.TWO_PI * rnd.NextDouble(), true);
142 void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
144 if (e.Simulator == client.Network.CurrentSim)
150 private UUID teleportEffect = UUID.Random();
152 void Self_TeleportProgress(object sender, TeleportEventArgs e)
154 if (!client.Network.Connected) return;
156 if (e.Status == TeleportStatus.Progress)
158 client.Self.SphereEffect(client.Self.GlobalPosition, Color4.White, 4f, teleportEffect);
161 if (e.Status == TeleportStatus.Finished)
163 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
167 if (e.Status == TeleportStatus.Failed)
169 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0f, teleportEffect);
173 void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
175 typing = away = busy = walking = false;
177 if (lookAtTimer != null)
179 lookAtTimer.Dispose();
185 void netcom_ClientConnected(object sender, EventArgs e)
187 client.Self.Movement.Camera.Far = 256f;
188 effectSource = client.Self.AgentID;
190 if (lookAtTimer == null)
191 lookAtTimer = new System.Threading.Timer(new TimerCallback(lookAtTimerTick), null, Timeout.Infinite, Timeout.Infinite);
194 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
196 if (!e.Update.Avatar) return;
197 if (!following) return;
200 client.Network.CurrentSim.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av);
201 if (av == null) return;
203 if (av.Name == followName)
207 if (av.ParentID == 0)
214 client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(av.ParentID, out prim);
217 pos = client.Self.SimPosition;
219 pos = prim.Position + av.Position;
222 if (Vector3.Distance(pos, client.Self.SimPosition) > followDistance)
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);
229 client.Self.AutoPilotCancel();
230 client.Self.AutoPilot(x, y, pos.Z);
235 #region Look at effect
236 private int lastLookAtEffect = 0;
237 private UUID lookAtEffect = UUID.Random();
240 /// Set eye focus 3m in front of us
242 public void LookInFront()
244 if (!client.Network.Connected) return;
246 client.Self.LookAtEffect(client.Self.AgentID, client.Self.AgentID,
247 new Vector3d(new Vector3(3, 0, 0) * Quaternion.Identity),
248 LookAtType.Idle, lookAtEffect);
251 void lookAtTimerTick(object state)
256 void netcom_ChatReceived(object sender, ChatEventArgs e)
258 if (e.SourceID != client.Self.AgentID && (e.SourceType == ChatSourceType.Agent || e.Type == ChatType.StartTyping))
260 // change focus max every 4 seconds
261 if (Environment.TickCount - lastLookAtEffect > 4000)
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)
268 lookAtTimer.Change(10000, Timeout.Infinite);
273 #endregion Look at effect
275 public void Follow(string name)
278 following = !string.IsNullOrEmpty(followName);
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;
294 public void WalkTo(Primitive prim)
296 WalkTo(GlobalPosition(prim));
299 public void WalkTo(Vector3d globalPos)
301 walkToTarget = globalPos;
306 followName = string.Empty;
309 if (walkTimer == null)
311 walkTimer = new System.Threading.Timer(new TimerCallback(walkTimerElapsed), null, walkChekInterval, Timeout.Infinite);
314 lastDistanceChanged = System.Environment.TickCount;
315 client.Self.AutoPilotCancel();
317 client.Self.AutoPilot(walkToTarget.X, walkToTarget.Y, walkToTarget.Z);
318 FireWalkStateCanged();
321 void walkTimerElapsed(object sender)
324 double distance = Vector3d.Distance(client.Self.GlobalPosition, walkToTarget);
333 if (lastDistance != (int)distance)
335 lastDistanceChanged = System.Environment.TickCount;
336 lastDistance = (int)distance;
338 else if ((System.Environment.TickCount - lastDistanceChanged) > 10000)
340 // Our distance to the target has not changed in 10s, give up
344 walkTimer.Change(walkChekInterval, Timeout.Infinite);
348 void Self_AlertMessage(object sender, AlertMessageEventArgs e)
350 if (e.Message.Contains("Autopilot cancel"))
359 void FireWalkStateCanged()
361 if (OnWalkStateCanged != null)
363 try { OnWalkStateCanged(walking); }
364 catch (Exception) { }
368 public void EndWalking()
373 Logger.Log("Finished walking.", Helpers.LogLevel.Debug, client);
376 client.Self.AutoPilotCancel();
377 FireWalkStateCanged();
382 public void SetTyping(bool typing)
384 Dictionary<UUID, bool> typingAnim = new Dictionary<UUID, bool>();
385 typingAnim.Add(typingAnimationID, typing);
387 client.Self.Animate(typingAnim, false);
390 client.Self.Chat(string.Empty, 0, ChatType.StartTyping);
392 client.Self.Chat(string.Empty, 0, ChatType.StopTyping);
394 this.typing = typing;
397 public void SetAway(bool away)
399 Dictionary<UUID, bool> awayAnim = new Dictionary<UUID, bool>();
400 awayAnim.Add(awayAnimationID, away);
402 client.Self.Animate(awayAnim, true);
406 public void SetBusy(bool busy)
408 Dictionary<UUID, bool> busyAnim = new Dictionary<UUID, bool>();
409 busyAnim.Add(busyAnimationID, busy);
411 client.Self.Animate(busyAnim, true);
415 public void SetFlying(bool flying)
417 this.flying = client.Self.Movement.Fly = flying;
420 public void SetAlwaysRun(bool alwaysrun)
422 this.alwaysrun = client.Self.Movement.AlwaysRun = alwaysrun;
425 public void SetSitting(bool sitting, UUID target)
427 this.sitting = sitting;
431 client.Self.RequestSit(target, Vector3.Zero);
440 public Vector3d GlobalPosition(Simulator sim, Vector3 pos)
442 uint globalX, globalY;
443 Utils.LongToUInts(sim.Handle, out globalX, out globalY);
446 (double)globalX + (double)pos.X,
447 (double)globalY + (double)pos.Y,
451 public Vector3d GlobalPosition(Primitive prim)
453 return GlobalPosition(client.Network.CurrentSim, prim.Position);
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;
467 public void UnSetPointing()
469 beamTimer.Enabled = false;
470 if (pointID != UUID.Zero)
472 client.Self.PointAtEffect(effectSource, UUID.Zero, Vector3d.Zero, PointAtType.None, pointID);
478 foreach (UUID id in beamID)
480 client.Self.BeamEffect(UUID.Zero, UUID.Zero, Vector3d.Zero, new Color4(255, 255, 255, 255), 0, id);
485 if (sphereID != UUID.Zero)
487 client.Self.SphereEffect(Vector3d.Zero, Color4.White, 0, sphereID);
488 sphereID = UUID.Zero;
493 void beamTimer_Elapsed(object sender, EventArgs e)
495 if (beamID == null) return;
499 client.Self.SphereEffect(GlobalPosition(targetPrim), beamColors[beamRandom.Next(0, 3)], 0.85f, sphereID);
501 for (i = 0; i < numBeans; i++)
503 UUID newBeam = UUID.Random();
508 scatter = GlobalPosition(targetPrim);
512 Vector3d direction = client.Self.GlobalPosition - GlobalPosition(targetPrim);
513 Vector3d cross = direction % new Vector3d(0, 0, 1);
515 scatter = GlobalPosition(targetPrim) + cross * (i * 0.2d) * (i % 2 == 0 ? 1 : -1);
517 client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[i]);
520 for (int j = 1; j < numBeans; j++)
522 UUID newBeam = UUID.Random();
524 Vector3d cross = new Vector3d(0, 0, 1);
526 scatter = GlobalPosition(targetPrim) + cross * (j * 0.2d) * (j % 2 == 0 ? 1 : -1);
528 client.Self.BeamEffect(effectSource, UUID.Zero, scatter, beamColors[beamRandom.Next(0, 3)], 1.0f, beamID[j + i - 1]);
531 catch (Exception) { };
535 public void SetPointing(Primitive prim, int numBeans)
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>();
544 this.numBeans = numBeans;
546 client.Self.PointAtEffect(effectSource, prim.ID, Vector3d.Zero, PointAtType.Select, pointID);
548 for (int i = 0; i < numBeans; i++)
550 UUID newBeam = UUID.Random();
552 beamTarget.Add(Vector3d.Zero);
555 for (int i = 1; i < numBeans; i++)
557 UUID newBeam = UUID.Random();
559 beamTarget.Add(Vector3d.Zero);
562 beamTimer.Interval = 1000;
563 beamTimer.Enabled = true;
566 public UUID TypingAnimationID
568 get { return typingAnimationID; }
569 set { typingAnimationID = value; }
572 public UUID AwayAnimationID
574 get { return awayAnimationID; }
575 set { awayAnimationID = value; }
578 public UUID BusyAnimationID
580 get { return busyAnimationID; }
581 set { busyAnimationID = value; }
584 public UUID EffectSource
586 get { return effectSource; }
587 set { effectSource = value; }
592 get { return typing; }
607 get { return flying; }
610 public bool IsSitting
612 get { return sitting; }
615 public bool IsPointing
617 get { return pointID != UUID.Zero; }
620 public bool IsFollowing
622 get { return following; }
625 public string FollowName
627 get { return followName; }
628 set { followName = value; }
631 public float FollowDistance
633 get { return followDistance; }
634 set { followDistance = value; }
637 public bool IsWalking
639 get { return walking; }