* Added Primitive.TreeSpecies and Primitive.ScratchPad * Converted Primitive.SoundFlags to the new SoundFlags enum * Added a Utils.BytesToString() overload that accepts index and count parameters * Added Utils.FloatToUInt16() [Simian] * Lots of changes in Simian to use the new unified ISceneProvider.ObjectAddOrUpdate() function * Update flags are checked to determine the minimum sized packet that needs to be sent out for an update. ImprovedTerseObjectUpdate is working, and started work on ObjectUpdateCached (updates using this will currently not send) * Adding three new variables to SimulationObject to store attachment-related state git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2478 52acb1d6-8a22-11de-b505-999d5b087335
518 lines
24 KiB
C#
518 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using ExtensionLoader;
|
|
using OpenMetaverse;
|
|
using OpenMetaverse.Packets;
|
|
using OpenMetaverse.Rendering;
|
|
|
|
namespace Simian.Extensions
|
|
{
|
|
public class Movement : IExtension<Simian>
|
|
{
|
|
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
|
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
|
const float GRAVITY = 9.8f; //meters/sec
|
|
const float WALK_SPEED = 3f; //meters/sec
|
|
const float RUN_SPEED = 5f; //meters/sec
|
|
const float FLY_SPEED = 10f; //meters/sec
|
|
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
|
const float FALL_FORGIVENESS = .25f; //fall buffer in meters
|
|
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
|
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec
|
|
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
|
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
|
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
|
|
|
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
|
static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
|
|
|
|
const float SQRT_TWO = 1.41421356f;
|
|
|
|
Simian server;
|
|
Timer updateTimer;
|
|
long lastTick;
|
|
|
|
public int LastTick
|
|
{
|
|
get { return (int) Interlocked.Read(ref lastTick); }
|
|
set { Interlocked.Exchange(ref lastTick, value); }
|
|
}
|
|
|
|
public Movement()
|
|
{
|
|
}
|
|
|
|
public void Start(Simian server)
|
|
{
|
|
this.server = server;
|
|
|
|
server.Scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
|
|
|
server.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
|
|
server.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
|
|
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
|
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
|
|
|
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
|
LastTick = Environment.TickCount;
|
|
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (updateTimer != null)
|
|
{
|
|
updateTimer.Dispose();
|
|
updateTimer = null;
|
|
}
|
|
}
|
|
|
|
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
|
|
{
|
|
bool forceMeshing = false;
|
|
bool forceTransform = false;
|
|
|
|
if ((update & UpdateFlags.Scale) != 0 ||
|
|
(update & UpdateFlags.Position) != 0 ||
|
|
(update & UpdateFlags.Rotation) != 0)
|
|
{
|
|
forceTransform = true;
|
|
}
|
|
|
|
if ((update & UpdateFlags.PrimData) != 0)
|
|
{
|
|
forceMeshing = true;
|
|
}
|
|
|
|
// TODO: This doesn't update children prims when their parents move
|
|
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
|
|
}
|
|
|
|
void UpdateTimer_Elapsed(object sender)
|
|
{
|
|
int tick = Environment.TickCount;
|
|
float seconds = (float)((tick - LastTick) / 1000f);
|
|
LastTick = tick;
|
|
|
|
server.Scene.ForEachAgent(
|
|
delegate(Agent agent)
|
|
{
|
|
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
|
|
return;
|
|
|
|
bool animsChanged = false;
|
|
|
|
// Create forward and left vectors from the current avatar rotation
|
|
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
|
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
|
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
|
|
|
// Check control flags
|
|
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
|
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
|
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
|
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
|
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
|
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
|
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
|
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
|
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
|
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
|
|
|
// direction in which the avatar is trying to move
|
|
Vector3 move = Vector3.Zero;
|
|
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
|
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
|
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
|
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
|
if (heldUp) { move.Z += 1; }
|
|
if (heldDown) { move.Z -= 1; }
|
|
|
|
// is the avatar trying to move?
|
|
bool moving = move != Vector3.Zero;
|
|
bool jumping = agent.TickJump != 0;
|
|
|
|
// 2-dimensional speed multipler
|
|
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
|
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
|
speed /= SQRT_TWO;
|
|
|
|
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
|
float oldFloor = server.Scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
|
|
|
agentPosition += (move * speed);
|
|
float newFloor = server.Scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
|
|
|
if (!flying && newFloor != oldFloor)
|
|
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
|
|
|
//HACK: distance from avatar center to the bottom of its feet
|
|
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
|
|
|
|
float lowerLimit = newFloor + distanceFromFloor;
|
|
|
|
//"bridge" physics
|
|
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
|
|
{
|
|
//start ray at our feet
|
|
Vector3 rayStart = new Vector3(
|
|
agent.Avatar.Prim.Position.X,
|
|
agent.Avatar.Prim.Position.Y,
|
|
agent.Avatar.Prim.Position.Z - distanceFromFloor
|
|
);
|
|
|
|
//end ray at 0.01m below our feet
|
|
Vector3 rayEnd = new Vector3(
|
|
rayStart.X,
|
|
rayStart.Y,
|
|
rayStart.Z - 0.01f
|
|
);
|
|
|
|
server.Scene.ForEachObject(delegate(SimulationObject obj)
|
|
{
|
|
//HACK: check nearby objects (what did you expect, octree?)
|
|
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
|
|
{
|
|
Vector3 collision = server.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
|
|
|
|
if (collision != rayEnd) //we collided!
|
|
{
|
|
//check if we are any higher than before
|
|
float height = collision.Z + distanceFromFloor;
|
|
if (height > lowerLimit) lowerLimit = height;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Z acceleration resulting from gravity
|
|
float gravity = 0f;
|
|
|
|
float waterChestHeight = server.Scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
|
|
|
|
if (flying)
|
|
{
|
|
agent.TickFall = 0;
|
|
agent.TickJump = 0;
|
|
|
|
//velocity falloff while flying
|
|
agent.Avatar.Prim.Velocity.X *= 0.66f;
|
|
agent.Avatar.Prim.Velocity.Y *= 0.66f;
|
|
agent.Avatar.Prim.Velocity.Z *= 0.33f;
|
|
|
|
if (agent.Avatar.Prim.Position.Z == lowerLimit)
|
|
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
|
|
|
if (move.X != 0 || move.Y != 0)
|
|
{ //flying horizontally
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
|
animsChanged = true;
|
|
}
|
|
else if (move.Z > 0)
|
|
{ //flying straight up
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
|
animsChanged = true;
|
|
}
|
|
else if (move.Z < 0)
|
|
{ //flying straight down
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
|
animsChanged = true;
|
|
}
|
|
else
|
|
{ //hovering in the air
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
|
|
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
|
|
{ //falling, floating, or landing from a jump
|
|
|
|
if (agent.Avatar.Prim.Position.Z > server.Scene.WaterHeight)
|
|
{ //above water
|
|
|
|
//override controls while drifting
|
|
move = Vector3.Zero;
|
|
|
|
//keep most of our horizontal inertia
|
|
agent.Avatar.Prim.Velocity.X *= 0.975f;
|
|
agent.Avatar.Prim.Velocity.Y *= 0.975f;
|
|
|
|
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
|
|
|
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
|
{ //just started falling
|
|
agent.TickFall = Environment.TickCount;
|
|
}
|
|
else
|
|
{
|
|
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
|
|
|
if (!jumping)
|
|
{ //falling
|
|
if (fallElapsed > FALL_DELAY)
|
|
{ //falling long enough to trigger the animation
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
|
|
{ //at the water line
|
|
|
|
gravity = 0f;
|
|
agent.Avatar.Prim.Velocity *= 0.5f;
|
|
agent.Avatar.Prim.Velocity.Z = 0f;
|
|
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
|
|
|
|
if (move.Z > 0)
|
|
{
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
|
animsChanged = true;
|
|
}
|
|
else if (move.X != 0 || move.Y != 0)
|
|
{
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
|
|
animsChanged = true;
|
|
}
|
|
else
|
|
{
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
else
|
|
{ //underwater
|
|
|
|
gravity = 0f; //buoyant
|
|
agent.Avatar.Prim.Velocity *= 0.5f * seconds;
|
|
agent.Avatar.Prim.Velocity.Z += 0.75f * seconds;
|
|
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
else
|
|
{ //on the ground
|
|
|
|
agent.TickFall = 0;
|
|
|
|
//friction
|
|
agent.Avatar.Prim.Acceleration *= 0.2f;
|
|
agent.Avatar.Prim.Velocity *= 0.2f;
|
|
|
|
agent.Avatar.Prim.Position.Z = lowerLimit;
|
|
|
|
if (move.Z > 0)
|
|
{ //jumping
|
|
if (!jumping)
|
|
{ //begin prejump
|
|
move.Z = 0; //override Z control
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
|
animsChanged = true;
|
|
|
|
agent.TickJump = Environment.TickCount;
|
|
}
|
|
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
|
{ //start actual jump
|
|
|
|
if (agent.TickJump == -1)
|
|
{
|
|
//already jumping! end current jump
|
|
agent.TickJump = 0;
|
|
return;
|
|
}
|
|
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
|
animsChanged = true;
|
|
|
|
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
|
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
|
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
|
|
|
|
agent.TickJump = -1; //flag that we are currently jumping
|
|
}
|
|
else move.Z = 0; //override Z control
|
|
}
|
|
|
|
else
|
|
{ //not jumping
|
|
|
|
agent.TickJump = 0;
|
|
|
|
if (move.X != 0 || move.Y != 0)
|
|
{ //not walking
|
|
|
|
if (move.Z < 0)
|
|
{ //crouchwalking
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
|
animsChanged = true;
|
|
}
|
|
else if (agent.Running)
|
|
{ //running
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
|
animsChanged = true;
|
|
}
|
|
else
|
|
{ //walking
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
else
|
|
{ //walking
|
|
if (move.Z < 0)
|
|
{ //crouching
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
|
animsChanged = true;
|
|
}
|
|
else
|
|
{ //standing
|
|
if (server.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
|
animsChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (animsChanged)
|
|
server.Avatars.SendAnimations(agent);
|
|
|
|
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
|
|
|
// static acceleration when any control is held, otherwise none
|
|
if (moving)
|
|
{
|
|
agent.Avatar.Prim.Acceleration = move * speed;
|
|
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
|
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
|
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
|
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
|
}
|
|
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
|
|
|
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
|
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
|
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
|
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
|
agent.Avatar.Prim.Velocity.Z = maxVel;
|
|
|
|
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
|
|
|
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
|
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
|
|
|
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
|
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
|
|
|
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
|
}
|
|
);
|
|
}
|
|
|
|
void AgentRequestSitHandler(Packet packet, Agent agent)
|
|
{
|
|
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
|
|
|
|
SimulationObject obj;
|
|
if (server.Scene.TryGetObject(request.TargetObject.TargetID, out obj))
|
|
{
|
|
agent.RequestedSitTarget = request.TargetObject.TargetID;
|
|
agent.RequestedSitOffset = request.TargetObject.Offset;
|
|
|
|
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
|
|
response.SitObject.ID = request.TargetObject.TargetID;
|
|
response.SitTransform.AutoPilot = true;
|
|
response.SitTransform.CameraAtOffset = Vector3.Zero;
|
|
response.SitTransform.CameraEyeOffset = Vector3.Zero;
|
|
response.SitTransform.ForceMouselook = false;
|
|
response.SitTransform.SitPosition = request.TargetObject.Offset;
|
|
response.SitTransform.SitRotation = obj.SitRotation;
|
|
|
|
server.UDP.SendPacket(agent.ID, response, PacketCategory.State);
|
|
}
|
|
else
|
|
{
|
|
//TODO: send error
|
|
}
|
|
}
|
|
|
|
void AgentSitHandler(Packet packet, Agent agent)
|
|
{
|
|
AgentSitPacket sit = (AgentSitPacket)packet;
|
|
|
|
if (agent.RequestedSitTarget != UUID.Zero)
|
|
{
|
|
SimulationObject obj;
|
|
SimulationObject avObj;
|
|
if (server.Scene.TryGetObject(agent.RequestedSitTarget, out obj) && server.Scene.TryGetObject(agent.ID, out avObj))
|
|
{
|
|
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
|
|
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
|
|
agent.Avatar.Prim.Position = new Vector3(
|
|
obj.Prim.Scale.X * 0.5f,
|
|
obj.Prim.Scale.Z * 0.5f,
|
|
agent.Avatar.Prim.Scale.Z * 0.33f);
|
|
|
|
server.Scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
|
|
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
|
|
server.Avatars.SetDefaultAnimation(agent, Animations.SIT);
|
|
server.Avatars.SendAnimations(agent);
|
|
}
|
|
else
|
|
{
|
|
//TODO: send error
|
|
}
|
|
|
|
agent.RequestedSitTarget = UUID.Zero;
|
|
agent.RequestedSitOffset = Vector3.Zero;
|
|
}
|
|
}
|
|
|
|
void AgentUpdateHandler(Packet packet, Agent agent)
|
|
{
|
|
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
|
|
|
SimulationObject obj;
|
|
if (server.Scene.TryGetObject(agent.ID, out obj))
|
|
{
|
|
if (agent.Avatar.Prim.ParentID == 0)
|
|
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
|
|
|
|
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
|
agent.State = (AgentState)update.AgentData.State;
|
|
agent.HideTitle = update.AgentData.Flags != 0;
|
|
|
|
// Check for standing up
|
|
SimulationObject parent;
|
|
if (server.Scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) &&
|
|
agent.Avatar.Prim.ParentID > 0 &&
|
|
(agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP)
|
|
{
|
|
agent.Avatar.Prim.Position = parent.Prim.Position
|
|
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
|
|
+ Vector3.UnitZ;
|
|
|
|
agent.Avatar.Prim.ParentID = 0;
|
|
|
|
server.Avatars.SetDefaultAnimation(agent, Animations.STAND);
|
|
server.Avatars.SendAnimations(agent);
|
|
|
|
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
|
|
}
|
|
|
|
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
|
|
}
|
|
}
|
|
|
|
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
|
{
|
|
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
|
|
|
agent.Running = run.AgentData.AlwaysRun;
|
|
}
|
|
}
|
|
}
|