/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.Remoting.Lifetime;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Timers;
using Nini.Config;
using log4net;
using OpenMetaverse;
using OpenMetaverse.Assets;
using OpenMetaverse.StructuredData; // LitJson is hidden on this
using OpenMetaverse.Packets;
using OpenMetaverse.Rendering;
using OpenSim;
using OpenSim.Framework;
using OpenSim.Region.CoreModules;
using OpenSim.Region.CoreModules.World.Land;
using OpenSim.Region.CoreModules.World.Terrain;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.Framework.Scenes.Animation;
using OpenSim.Region.Framework.Scenes.Scripting;
using OpenSim.Region.PhysicsModules.SharedBase;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api.Plugins;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Region.ScriptEngine.Interfaces;
using OpenSim.Region.ScriptEngine.Shared.Api.Interfaces;
using OpenSim.Services.Interfaces;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using PrimType = OpenSim.Region.Framework.Scenes.PrimType;
using AssetLandmark = OpenSim.Framework.AssetLandmark;
using RegionFlags = OpenSim.Framework.RegionFlags;
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
using System.Reflection;
using Timer = System.Timers.Timer;
using System.Linq;
using PermissionMask = OpenSim.Framework.PermissionMask;
namespace OpenSim.Region.ScriptEngine.Shared.Api
{
// MUST be a ref type
public class UserInfoCacheEntry
{
public int time;
public UserAccount account;
public PresenceInfo pinfo;
}
///
/// Contains all LSL ll-functions. This class will be in Default AppDomain.
///
public class LSL_Api : MarshalByRefObject, ILSL_Api, IScriptApi
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public int LlRequestAgentDataCacheTimeoutMs { get; set; }
protected IScriptEngine m_ScriptEngine;
protected SceneObjectPart m_host;
///
/// The item that hosts this script
///
protected TaskInventoryItem m_item;
protected bool throwErrorOnNotImplemented = false;
protected AsyncCommandManager AsyncCommands = null;
protected float m_ScriptDistanceFactor = 1.0f;
protected float m_MinTimerInterval = 0.5f;
protected float m_recoilScaleFactor = 0.0f;
protected double m_timer = Util.GetTimeStampMS();
protected bool m_waitingForScriptAnswer = false;
protected bool m_automaticLinkPermission = false;
protected IMessageTransferModule m_TransferModule = null;
protected int m_notecardLineReadCharsMax = 255;
protected int m_scriptConsoleChannel = 0;
protected bool m_scriptConsoleChannelEnabled = false;
protected bool m_debuggerSafe = false;
protected IUrlModule m_UrlModule = null;
protected Dictionary m_userInfoCache = new Dictionary();
protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp.
protected string m_internalObjectHost = "lsl.opensim.local";
protected bool m_restrictEmail = false;
protected ISoundModule m_SoundModule = null;
protected float m_avatarHeightCorrection = 0.2f;
protected bool m_useSimpleBoxesInGetBoundingBox = false;
protected bool m_addStatsInGetBoundingBox = false;
//LSL Avatar Bounding Box (lABB), lower (1) and upper (2),
//standing (Std), Groundsitting (Grs), Sitting (Sit),
//along X, Y and Z axes, constants (0) and coefficients (1)
protected float m_lABB1StdX0 = -0.275f;
protected float m_lABB2StdX0 = 0.275f;
protected float m_lABB1StdY0 = -0.35f;
protected float m_lABB2StdY0 = 0.35f;
protected float m_lABB1StdZ0 = -0.1f;
protected float m_lABB1StdZ1 = -0.5f;
protected float m_lABB2StdZ0 = 0.1f;
protected float m_lABB2StdZ1 = 0.5f;
protected float m_lABB1GrsX0 = -0.3875f;
protected float m_lABB2GrsX0 = 0.3875f;
protected float m_lABB1GrsY0 = -0.5f;
protected float m_lABB2GrsY0 = 0.5f;
protected float m_lABB1GrsZ0 = -0.05f;
protected float m_lABB1GrsZ1 = -0.375f;
protected float m_lABB2GrsZ0 = 0.5f;
protected float m_lABB2GrsZ1 = 0.0f;
protected float m_lABB1SitX0 = -0.5875f;
protected float m_lABB2SitX0 = 0.1875f;
protected float m_lABB1SitY0 = -0.35f;
protected float m_lABB2SitY0 = 0.35f;
protected float m_lABB1SitZ0 = -0.35f;
protected float m_lABB1SitZ1 = -0.375f;
protected float m_lABB2SitZ0 = -0.25f;
protected float m_lABB2SitZ1 = 0.25f;
protected float m_primSafetyCoeffX = 2.414214f;
protected float m_primSafetyCoeffY = 2.414214f;
protected float m_primSafetyCoeffZ = 1.618034f;
protected bool m_useCastRayV3 = false;
protected float m_floatToleranceInCastRay = 0.00001f;
protected float m_floatTolerance2InCastRay = 0.001f;
protected DetailLevel m_primLodInCastRay = DetailLevel.Medium;
protected DetailLevel m_sculptLodInCastRay = DetailLevel.Medium;
protected DetailLevel m_meshLodInCastRay = DetailLevel.Highest;
protected DetailLevel m_avatarLodInCastRay = DetailLevel.Medium;
protected int m_maxHitsInCastRay = 16;
protected int m_maxHitsPerPrimInCastRay = 16;
protected int m_maxHitsPerObjectInCastRay = 16;
protected bool m_detectExitsInCastRay = false;
protected bool m_doAttachmentsInCastRay = false;
protected int m_msThrottleInCastRay = 200;
protected int m_msPerRegionInCastRay = 40;
protected int m_msPerAvatarInCastRay = 10;
protected int m_msMinInCastRay = 2;
protected int m_msMaxInCastRay = 40;
protected static List m_castRayCalls = new List();
protected bool m_useMeshCacheInCastRay = true;
protected static Dictionary m_cachedMeshes = new Dictionary();
// protected Timer m_ShoutSayTimer;
protected int m_SayShoutCount = 0;
DateTime m_lastSayShoutCheck;
private Dictionary MovementAnimationsForLSL =
new Dictionary {
{"CROUCH", "Crouching"},
{"CROUCHWALK", "CrouchWalking"},
{"FALLDOWN", "Falling Down"},
{"FLY", "Flying"},
{"FLYSLOW", "FlyingSlow"},
{"HOVER", "Hovering"},
{"HOVER_UP", "Hovering Up"},
{"HOVER_DOWN", "Hovering Down"},
{"JUMP", "Jumping"},
{"LAND", "Landing"},
{"PREJUMP", "PreJumping"},
{"RUN", "Running"},
{"SIT","Sitting"},
{"SITGROUND","Sitting on Ground"},
{"STAND", "Standing"},
{"STANDUP", "Standing Up"},
{"STRIDE","Striding"},
{"SOFT_LAND", "Soft Landing"},
{"TURNLEFT", "Turning Left"},
{"TURNRIGHT", "Turning Right"},
{"WALK", "Walking"}
};
//An array of HTTP/1.1 headers that are not allowed to be used
//as custom headers by llHTTPRequest.
private string[] HttpStandardHeaders =
{
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language",
"Accept-Ranges", "Age", "Allow", "Authorization", "Cache-Control",
"Connection", "Content-Encoding", "Content-Language",
"Content-Length", "Content-Location", "Content-MD5",
"Content-Range", "Content-Type", "Date", "ETag", "Expect",
"Expires", "From", "Host", "If-Match", "If-Modified-Since",
"If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified",
"Location", "Max-Forwards", "Pragma", "Proxy-Authenticate",
"Proxy-Authorization", "Range", "Referer", "Retry-After", "Server",
"TE", "Trailer", "Transfer-Encoding", "Upgrade", "User-Agent",
"Vary", "Via", "Warning", "WWW-Authenticate"
};
public void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item)
{
m_lastSayShoutCheck = DateTime.UtcNow;
m_ScriptEngine = scriptEngine;
m_host = host;
m_item = item;
m_debuggerSafe = m_ScriptEngine.Config.GetBoolean("DebuggerSafe", false);
LoadConfig();
m_TransferModule =
m_ScriptEngine.World.RequestModuleInterface();
m_UrlModule = m_ScriptEngine.World.RequestModuleInterface();
m_SoundModule = m_ScriptEngine.World.RequestModuleInterface();
AsyncCommands = new AsyncCommandManager(m_ScriptEngine);
}
///
/// Load configuration items that affect script, object and run-time behavior. */
///
private void LoadConfig()
{
LlRequestAgentDataCacheTimeoutMs = 20000;
IConfig seConfig = m_ScriptEngine.Config;
if (seConfig != null)
{
m_MinTimerInterval =
seConfig.GetFloat("MinTimerInterval", m_MinTimerInterval);
m_automaticLinkPermission =
seConfig.GetBoolean("AutomaticLinkPermission", m_automaticLinkPermission);
m_notecardLineReadCharsMax =
seConfig.GetInt("NotecardLineReadCharsMax", m_notecardLineReadCharsMax);
// Rezzing an object with a velocity can create recoil. This feature seems to have been
// removed from recent versions of SL. The code computes recoil (vel*mass) and scales
// it by this factor. May be zero to turn off recoil all together.
m_recoilScaleFactor = m_ScriptEngine.Config.GetFloat("RecoilScaleFactor", m_recoilScaleFactor);
}
if (m_notecardLineReadCharsMax > 65535)
m_notecardLineReadCharsMax = 65535;
// load limits for particular subsystems.
IConfigSource seConfigSource = m_ScriptEngine.ConfigSource;
if (seConfigSource != null)
{
IConfig lslConfig = seConfigSource.Configs["LL-Functions"];
if (lslConfig != null)
{
m_restrictEmail = lslConfig.GetBoolean("RestrictEmail", m_restrictEmail);
m_avatarHeightCorrection = lslConfig.GetFloat("AvatarHeightCorrection", m_avatarHeightCorrection);
m_useSimpleBoxesInGetBoundingBox = lslConfig.GetBoolean("UseSimpleBoxesInGetBoundingBox", m_useSimpleBoxesInGetBoundingBox);
m_addStatsInGetBoundingBox = lslConfig.GetBoolean("AddStatsInGetBoundingBox", m_addStatsInGetBoundingBox);
m_lABB1StdX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingXconst", m_lABB1StdX0);
m_lABB2StdX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingXconst", m_lABB2StdX0);
m_lABB1StdY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingYconst", m_lABB1StdY0);
m_lABB2StdY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingYconst", m_lABB2StdY0);
m_lABB1StdZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingZconst", m_lABB1StdZ0);
m_lABB1StdZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingZcoeff", m_lABB1StdZ1);
m_lABB2StdZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingZconst", m_lABB2StdZ0);
m_lABB2StdZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingZcoeff", m_lABB2StdZ1);
m_lABB1GrsX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingXconst", m_lABB1GrsX0);
m_lABB2GrsX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingXconst", m_lABB2GrsX0);
m_lABB1GrsY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingYconst", m_lABB1GrsY0);
m_lABB2GrsY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingYconst", m_lABB2GrsY0);
m_lABB1GrsZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingZconst", m_lABB1GrsZ0);
m_lABB1GrsZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingZcoeff", m_lABB1GrsZ1);
m_lABB2GrsZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingZconst", m_lABB2GrsZ0);
m_lABB2GrsZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingZcoeff", m_lABB2GrsZ1);
m_lABB1SitX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingXconst", m_lABB1SitX0);
m_lABB2SitX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingXconst", m_lABB2SitX0);
m_lABB1SitY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingYconst", m_lABB1SitY0);
m_lABB2SitY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingYconst", m_lABB2SitY0);
m_lABB1SitZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZconst", m_lABB1SitZ0);
m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1);
m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0);
m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1);
m_primSafetyCoeffX = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientX", m_primSafetyCoeffX);
m_primSafetyCoeffY = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientY", m_primSafetyCoeffY);
m_primSafetyCoeffZ = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientZ", m_primSafetyCoeffZ);
m_useCastRayV3 = lslConfig.GetBoolean("UseLlCastRayV3", m_useCastRayV3);
m_floatToleranceInCastRay = lslConfig.GetFloat("FloatToleranceInLlCastRay", m_floatToleranceInCastRay);
m_floatTolerance2InCastRay = lslConfig.GetFloat("FloatTolerance2InLlCastRay", m_floatTolerance2InCastRay);
m_primLodInCastRay = (DetailLevel)lslConfig.GetInt("PrimDetailLevelInLlCastRay", (int)m_primLodInCastRay);
m_sculptLodInCastRay = (DetailLevel)lslConfig.GetInt("SculptDetailLevelInLlCastRay", (int)m_sculptLodInCastRay);
m_meshLodInCastRay = (DetailLevel)lslConfig.GetInt("MeshDetailLevelInLlCastRay", (int)m_meshLodInCastRay);
m_avatarLodInCastRay = (DetailLevel)lslConfig.GetInt("AvatarDetailLevelInLlCastRay", (int)m_avatarLodInCastRay);
m_maxHitsInCastRay = lslConfig.GetInt("MaxHitsInLlCastRay", m_maxHitsInCastRay);
m_maxHitsPerPrimInCastRay = lslConfig.GetInt("MaxHitsPerPrimInLlCastRay", m_maxHitsPerPrimInCastRay);
m_maxHitsPerObjectInCastRay = lslConfig.GetInt("MaxHitsPerObjectInLlCastRay", m_maxHitsPerObjectInCastRay);
m_detectExitsInCastRay = lslConfig.GetBoolean("DetectExitHitsInLlCastRay", m_detectExitsInCastRay);
m_doAttachmentsInCastRay = lslConfig.GetBoolean("DoAttachmentsInLlCastRay", m_doAttachmentsInCastRay);
m_msThrottleInCastRay = lslConfig.GetInt("ThrottleTimeInMsInLlCastRay", m_msThrottleInCastRay);
m_msPerRegionInCastRay = lslConfig.GetInt("AvailableTimeInMsPerRegionInLlCastRay", m_msPerRegionInCastRay);
m_msPerAvatarInCastRay = lslConfig.GetInt("AvailableTimeInMsPerAvatarInLlCastRay", m_msPerAvatarInCastRay);
m_msMinInCastRay = lslConfig.GetInt("RequiredAvailableTimeInMsInLlCastRay", m_msMinInCastRay);
m_msMaxInCastRay = lslConfig.GetInt("MaximumAvailableTimeInMsInLlCastRay", m_msMaxInCastRay);
m_useMeshCacheInCastRay = lslConfig.GetBoolean("UseMeshCacheInLlCastRay", m_useMeshCacheInCastRay);
}
IConfig smtpConfig = seConfigSource.Configs["SMTP"];
if (smtpConfig != null)
{
// there's an smtp config, so load in the snooze time.
EMAIL_PAUSE_TIME = smtpConfig.GetInt("email_pause_time", EMAIL_PAUSE_TIME);
m_internalObjectHost = smtpConfig.GetString("internal_object_host", m_internalObjectHost);
}
}
//// m_sleepMsOnEmail = EMAIL_PAUSE_TIME * 1000;
}
public override Object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromMinutes(0);
// lease.RenewOnCallTime = TimeSpan.FromSeconds(10.0);
// lease.SponsorshipTimeout = TimeSpan.FromMinutes(1.0);
}
return lease;
}
protected SceneObjectPart MonitoringObject()
{
UUID m = m_host.ParentGroup.MonitoringObject;
if (m == UUID.Zero)
return null;
SceneObjectPart p = m_ScriptEngine.World.GetSceneObjectPart(m);
if (p == null)
m_host.ParentGroup.MonitoringObject = UUID.Zero;
return p;
}
protected virtual void Sleep(int delay)
{
if (m_item == null) // Some unit tests don't set this
{
Thread.Sleep(delay);
return;
}
m_ScriptEngine.SleepScript(m_item.ItemID, delay);
}
///
/// Check for co-operative termination.
///
/// If called with 0, then just the check is performed with no wait.
public Scene World
{
get { return m_ScriptEngine.World; }
}
[DebuggerNonUserCode]
public void state(string newState)
{
m_ScriptEngine.SetState(m_item.ItemID, newState);
}
///
/// Reset the named script. The script must be present
/// in the same prim.
///
[DebuggerNonUserCode]
public void llResetScript()
{
// We need to tell the URL module, if we hav one, to release
// the allocated URLs
if (m_UrlModule != null)
m_UrlModule.ScriptRemoved(m_item.ItemID);
m_ScriptEngine.ApiResetScript(m_item.ItemID);
}
public void llResetOtherScript(string name)
{
UUID item;
if ((item = GetScriptByName(name)) == UUID.Zero)
{
Error("llResetOtherScript", "Can't find script '" + name + "'");
return;
}
if(item == m_item.ItemID)
llResetScript();
else
{
m_ScriptEngine.ResetScript(item);
}
}
public LSL_Integer llGetScriptState(string name)
{
UUID item;
if ((item = GetScriptByName(name)) != UUID.Zero)
{
return m_ScriptEngine.GetScriptState(item) ?1:0;
}
Error("llGetScriptState", "Can't find script '" + name + "'");
// If we didn't find it, then it's safe to
// assume it is not running.
return 0;
}
public void llSetScriptState(string name, int run)
{
UUID item;
// These functions are supposed to be robust,
// so get the state one step at a time.
if ((item = GetScriptByName(name)) != UUID.Zero)
{
m_ScriptEngine.SetScriptState(item, run == 0 ? false : true, item == m_item.ItemID);
}
else
{
Error("llSetScriptState", "Can't find script '" + name + "'");
}
}
public List GetLinkAvatars(int linkType)
{
List ret = new List();
if (m_host == null || m_host.ParentGroup == null || m_host.ParentGroup.IsDeleted)
return ret;
// List avs = m_host.ParentGroup.GetLinkedAvatars();
// this needs check
List avs = m_host.ParentGroup.GetSittingAvatars();
switch (linkType)
{
case ScriptBaseClass.LINK_SET:
return avs;
case ScriptBaseClass.LINK_ROOT:
return ret;
case ScriptBaseClass.LINK_ALL_OTHERS:
return avs;
case ScriptBaseClass.LINK_ALL_CHILDREN:
return avs;
case ScriptBaseClass.LINK_THIS:
return ret;
default:
if (linkType < 0)
return ret;
int partCount = m_host.ParentGroup.GetPartCount();
if (linkType <= partCount)
{
return ret;
}
else
{
linkType = linkType - partCount;
if (linkType > avs.Count)
{
return ret;
}
else
{
ret.Add(avs[linkType-1]);
return ret;
}
}
}
}
///
/// Get a given link entity from a linkset (linked objects and any sitting avatars).
///
///
/// If there are any ScenePresence's in the linkset (i.e. because they are sat upon one of the prims), then
/// these are counted as extra entities that correspond to linknums beyond the number of prims in the linkset.
/// The ScenePresences receive linknums in the order in which they sat.
///
///
/// The link entity. null if not found.
///
///
///
/// Can be either a non-negative integer or ScriptBaseClass.LINK_THIS (-4).
/// If ScriptBaseClass.LINK_THIS then the entity containing the script is returned.
/// If the linkset has one entity and a linknum of zero is given, then the single entity is returned. If any
/// positive integer is given in this case then null is returned.
/// If the linkset has more than one entity and a linknum greater than zero but equal to or less than the number
/// of entities, then the entity which corresponds to that linknum is returned.
/// Otherwise, if a positive linknum is given which is greater than the number of entities in the linkset, then
/// null is returned.
///
public ISceneEntity GetLinkEntity(SceneObjectPart part, int linknum)
{
if (linknum < 0)
{
if (linknum == ScriptBaseClass.LINK_THIS)
return part;
else
return null;
}
int actualPrimCount = part.ParentGroup.PrimCount;
List sittingAvatars = part.ParentGroup.GetSittingAvatars();
int adjustedPrimCount = actualPrimCount + sittingAvatars.Count;
// Special case for a single prim. In this case the linknum is zero. However, this will not match a single
// prim that has any avatars sat upon it (in which case the root prim is link 1).
if (linknum == 0)
{
if (actualPrimCount == 1 && sittingAvatars.Count == 0)
return part;
return null;
}
// Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but
// here we must match 1 (ScriptBaseClass.LINK_ROOT).
else if (linknum == ScriptBaseClass.LINK_ROOT && actualPrimCount == 1)
{
if (sittingAvatars.Count > 0)
return part.ParentGroup.RootPart;
else
return null;
}
else if (linknum <= adjustedPrimCount)
{
if (linknum <= actualPrimCount)
{
return part.ParentGroup.GetLinkNumPart(linknum);
}
else
{
return sittingAvatars[linknum - actualPrimCount - 1];
}
}
else
{
return null;
}
}
public List GetLinkParts(int linkType)
{
return GetLinkParts(m_host, linkType);
}
public static List GetLinkParts(SceneObjectPart part, int linkType)
{
List ret = new List();
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return ret;
switch (linkType)
{
case ScriptBaseClass.LINK_SET:
return new List(part.ParentGroup.Parts);
case ScriptBaseClass.LINK_ROOT:
ret.Add(part.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_ALL_OTHERS:
ret = new List(part.ParentGroup.Parts);
if (ret.Contains(part))
ret.Remove(part);
return ret;
case ScriptBaseClass.LINK_ALL_CHILDREN:
ret = new List(part.ParentGroup.Parts);
if (ret.Contains(part.ParentGroup.RootPart))
ret.Remove(part.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_THIS:
ret.Add(part);
return ret;
default:
if (linkType < 0)
return ret;
SceneObjectPart target = part.ParentGroup.GetLinkNumPart(linkType);
if (target == null)
return ret;
ret.Add(target);
return ret;
}
}
public List GetLinkEntities(int linkType)
{
return GetLinkEntities(m_host, linkType);
}
public List GetLinkEntities(SceneObjectPart part, int linkType)
{
List ret;
switch (linkType)
{
case ScriptBaseClass.LINK_SET:
return new List(part.ParentGroup.Parts);
case ScriptBaseClass.LINK_ROOT:
return new List() { part.ParentGroup.RootPart };
case ScriptBaseClass.LINK_ALL_OTHERS:
ret = new List(part.ParentGroup.Parts);
if (ret.Contains(part))
ret.Remove(part);
return ret;
case ScriptBaseClass.LINK_ALL_CHILDREN:
ret = new List(part.ParentGroup.Parts);
if (ret.Contains(part.ParentGroup.RootPart))
ret.Remove(part.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_THIS:
return new List() { part };
default:
if (linkType < 0)
return new List();
ISceneEntity target = GetLinkEntity(part, linkType);
if (target == null)
return new List();
return new List() { target };
}
}
//These are the implementations of the various ll-functions used by the LSL scripts.
public LSL_Float llSin(double f)
{
return (double)Math.Sin(f);
}
public LSL_Float llCos(double f)
{
return (double)Math.Cos(f);
}
public LSL_Float llTan(double f)
{
return (double)Math.Tan(f);
}
public LSL_Float llAtan2(double x, double y)
{
return (double)Math.Atan2(x, y);
}
public LSL_Float llSqrt(double f)
{
return (double)Math.Sqrt(f);
}
public LSL_Float llPow(double fbase, double fexponent)
{
return (double)Math.Pow(fbase, fexponent);
}
public LSL_Integer llAbs(int i)
{
// changed to replicate LSL behaviour whereby minimum int value is returned untouched.
if (i == Int32.MinValue)
return i;
else
return (int)Math.Abs(i);
}
public LSL_Float llFabs(double f)
{
return (double)Math.Abs(f);
}
public LSL_Float llFrand(double mag)
{
lock (Util.RandomClass)
{
return Util.RandomClass.NextDouble() * mag;
}
}
public LSL_Integer llFloor(double f)
{
return (int)Math.Floor(f);
}
public LSL_Integer llCeil(double f)
{
return (int)Math.Ceiling(f);
}
// Xantor 01/May/2008 fixed midpointrounding (2.5 becomes 3.0 instead of 2.0, default = ToEven)
public LSL_Integer llRound(double f)
{
return (int)Math.Round(f, MidpointRounding.AwayFromZero);
}
//This next group are vector operations involving squaring and square root. ckrinke
public LSL_Float llVecMag(LSL_Vector v)
{
return LSL_Vector.Mag(v);
}
public LSL_Vector llVecNorm(LSL_Vector v)
{
return LSL_Vector.Norm(v);
}
private double VecDist(LSL_Vector a, LSL_Vector b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
double dz = a.z - b.z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
public LSL_Float llVecDist(LSL_Vector a, LSL_Vector b)
{
return VecDist(a, b);
}
//Now we start getting into quaternions which means sin/cos, matrices and vectors. ckrinke
// Utility function for llRot2Euler
public LSL_Vector llRot2Euler(LSL_Rotation q1)
{
LSL_Vector eul = new LSL_Vector();
double sqw = q1.s*q1.s;
double sqx = q1.x*q1.x;
double sqy = q1.z*q1.z;
double sqz = q1.y*q1.y;
double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
double test = q1.x*q1.z + q1.y*q1.s;
if (test > 0.4999*unit) { // singularity at north pole
eul.z = 2 * Math.Atan2(q1.x,q1.s);
eul.y = Math.PI/2;
eul.x = 0;
return eul;
}
if (test < -0.4999*unit) { // singularity at south pole
eul.z = -2 * Math.Atan2(q1.x,q1.s);
eul.y = -Math.PI/2;
eul.x = 0;
return eul;
}
eul.z = Math.Atan2(2*q1.z*q1.s-2*q1.x*q1.y , sqx - sqy - sqz + sqw);
eul.y = Math.Asin(2*test/unit);
eul.x = Math.Atan2(2*q1.x*q1.s-2*q1.z*q1.y , -sqx + sqy - sqz + sqw);
return eul;
}
/* From wiki:
The Euler angle vector (in radians) is converted to a rotation by doing the rotations around the 3 axes
in Z, Y, X order. So llEuler2Rot(<1.0, 2.0, 3.0> * DEG_TO_RAD) generates a rotation by taking the zero rotation,
a vector pointing along the X axis, first rotating it 3 degrees around the global Z axis, then rotating the resulting
vector 2 degrees around the global Y axis, and finally rotating that 1 degree around the global X axis.
*/
/* How we arrived at this llEuler2Rot
*
* Experiment in SL to determine conventions:
* llEuler2Rot()=<1,0,0,0>
* llEuler2Rot(<0,PI,0>)=<0,1,0,0>
* llEuler2Rot(<0,0,PI>)=<0,0,1,0>
*
* Important facts about Quaternions
* - multiplication is non-commutative (a*b != b*a)
* - http://en.wikipedia.org/wiki/Quaternion#Basis_multiplication
*
* Above SL experiment gives (c1,c2,c3,s1,s2,s3 as defined in our llEuler2Rot):
* Qx = c1+i*s1
* Qy = c2+j*s2;
* Qz = c3+k*s3;
*
* Rotations applied in order (from above) Z, Y, X
* Q = (Qz * Qy) * Qx
* ((c1+i*s1)*(c2+j*s2))*(c3+k*s3)
* (c1*c2+i*s1*c2+j*c1*s2+ij*s1*s2)*(c3+k*s3)
* (c1*c2+i*s1*c2+j*c1*s2+k*s1*s2)*(c3+k*s3)
* c1*c2*c3+i*s1*c2*c3+j*c1*s2*c3+k*s1*s2*c3+k*c1*c2*s3+ik*s1*c2*s3+jk*c1*s2*s3+kk*s1*s2*s3
* c1*c2*c3+i*s1*c2*c3+j*c1*s2*c3+k*s1*s2*c3+k*c1*c2*s3 -j*s1*c2*s3 +i*c1*s2*s3 -s1*s2*s3
* regroup: x=i*(s1*c2*c3+c1*s2*s3)
* y=j*(c1*s2*c3-s1*c2*s3)
* z=k*(s1*s2*c3+c1*c2*s3)
* s= c1*c2*c3-s1*s2*s3
*
* This implementation agrees with the functions found here:
* http://lslwiki.net/lslwiki/wakka.php?wakka=LibraryRotationFunctions
* And with the results in SL.
*
* It's also possible to calculate llEuler2Rot by direct multiplication of
* the Qz, Qy, and Qx vectors (as above - and done in the "accurate" function
* from the wiki).
* Apparently in some cases this is better from a numerical precision perspective?
*/
public LSL_Rotation llEuler2Rot(LSL_Vector v)
{
double x,y,z,s;
v.x *= 0.5;
v.y *= 0.5;
v.z *= 0.5;
double c1 = Math.Cos(v.x);
double c2 = Math.Cos(v.y);
double c1c2 = c1 * c2;
double s1 = Math.Sin(v.x);
double s2 = Math.Sin(v.y);
double s1s2 = s1 * s2;
double c1s2 = c1 * s2;
double s1c2 = s1 * c2;
double c3 = Math.Cos(v.z);
double s3 = Math.Sin(v.z);
x = s1c2 * c3 + c1s2 * s3;
y = c1s2 * c3 - s1c2 * s3;
z = s1s2 * c3 + c1c2 * s3;
s = c1c2 * c3 - s1s2 * s3;
return new LSL_Rotation(x, y, z, s);
}
public LSL_Rotation llAxes2Rot(LSL_Vector fwd, LSL_Vector left, LSL_Vector up)
{
double s;
double tr = fwd.x + left.y + up.z + 1.0;
if (tr >= 1.0)
{
s = 0.5 / Math.Sqrt(tr);
return new LSL_Rotation(
(left.z - up.y) * s,
(up.x - fwd.z) * s,
(fwd.y - left.x) * s,
0.25 / s);
}
else
{
double max = (left.y > up.z) ? left.y : up.z;
if (max < fwd.x)
{
s = Math.Sqrt(fwd.x - (left.y + up.z) + 1.0);
double x = s * 0.5;
s = 0.5 / s;
return new LSL_Rotation(
x,
(fwd.y + left.x) * s,
(up.x + fwd.z) * s,
(left.z - up.y) * s);
}
else if (max == left.y)
{
s = Math.Sqrt(left.y - (up.z + fwd.x) + 1.0);
double y = s * 0.5;
s = 0.5 / s;
return new LSL_Rotation(
(fwd.y + left.x) * s,
y,
(left.z + up.y) * s,
(up.x - fwd.z) * s);
}
else
{
s = Math.Sqrt(up.z - (fwd.x + left.y) + 1.0);
double z = s * 0.5;
s = 0.5 / s;
return new LSL_Rotation(
(up.x + fwd.z) * s,
(left.z + up.y) * s,
z,
(fwd.y - left.x) * s);
}
}
}
public LSL_Vector llRot2Fwd(LSL_Rotation r)
{
double x, y, z, m;
m = r.x * r.x + r.y * r.y + r.z * r.z + r.s * r.s;
// m is always greater than zero
// if m is not equal to 1 then Rotation needs to be normalized
if (Math.Abs(1.0 - m) > 0.000001) // allow a little slop here for calculation precision
{
m = 1.0 / Math.Sqrt(m);
r.x *= m;
r.y *= m;
r.z *= m;
r.s *= m;
}
// Fast Algebric Calculations instead of Vectors & Quaternions Product
x = r.x * r.x - r.y * r.y - r.z * r.z + r.s * r.s;
y = 2 * (r.x * r.y + r.z * r.s);
z = 2 * (r.x * r.z - r.y * r.s);
return (new LSL_Vector(x, y, z));
}
public LSL_Vector llRot2Left(LSL_Rotation r)
{
double x, y, z, m;
m = r.x * r.x + r.y * r.y + r.z * r.z + r.s * r.s;
// m is always greater than zero
// if m is not equal to 1 then Rotation needs to be normalized
if (Math.Abs(1.0 - m) > 0.000001) // allow a little slop here for calculation precision
{
m = 1.0 / Math.Sqrt(m);
r.x *= m;
r.y *= m;
r.z *= m;
r.s *= m;
}
// Fast Algebric Calculations instead of Vectors & Quaternions Product
x = 2 * (r.x * r.y - r.z * r.s);
y = -r.x * r.x + r.y * r.y - r.z * r.z + r.s * r.s;
z = 2 * (r.x * r.s + r.y * r.z);
return (new LSL_Vector(x, y, z));
}
public LSL_Vector llRot2Up(LSL_Rotation r)
{
double x, y, z, m;
m = r.x * r.x + r.y * r.y + r.z * r.z + r.s * r.s;
// m is always greater than zero
// if m is not equal to 1 then Rotation needs to be normalized
if (Math.Abs(1.0 - m) > 0.000001) // allow a little slop here for calculation precision
{
m = 1.0 / Math.Sqrt(m);
r.x *= m;
r.y *= m;
r.z *= m;
r.s *= m;
}
// Fast Algebric Calculations instead of Vectors & Quaternions Product
x = 2 * (r.x * r.z + r.y * r.s);
y = 2 * (-r.x * r.s + r.y * r.z);
z = -r.x * r.x - r.y * r.y + r.z * r.z + r.s * r.s;
return (new LSL_Vector(x, y, z));
}
public LSL_Rotation llRotBetween(LSL_Vector a, LSL_Vector b)
{
//A and B should both be normalized
/* This method is more accurate than the SL one, and thus causes problems
for scripts that deal with the SL inaccuracy around 180-degrees -.- .._.
double dotProduct = LSL_Vector.Dot(a, b);
LSL_Vector crossProduct = LSL_Vector.Cross(a, b);
double magProduct = LSL_Vector.Mag(a) * LSL_Vector.Mag(b);
double angle = Math.Acos(dotProduct / magProduct);
LSL_Vector axis = LSL_Vector.Norm(crossProduct);
double s = Math.Sin(angle / 2);
double x = axis.x * s;
double y = axis.y * s;
double z = axis.z * s;
double w = Math.Cos(angle / 2);
if (Double.IsNaN(x) || Double.IsNaN(y) || Double.IsNaN(z) || Double.IsNaN(w))
return new LSL_Rotation(0.0f, 0.0f, 0.0f, 1.0f);
return new LSL_Rotation((float)x, (float)y, (float)z, (float)w);
*/
// This method mimics the 180 errors found in SL
// See www.euclideanspace.com... angleBetween
LSL_Vector vec_a = a;
LSL_Vector vec_b = b;
// Eliminate zero length
LSL_Float vec_a_mag = LSL_Vector.Mag(vec_a);
LSL_Float vec_b_mag = LSL_Vector.Mag(vec_b);
if (vec_a_mag < 0.00001 ||
vec_b_mag < 0.00001)
{
return new LSL_Rotation(0.0f, 0.0f, 0.0f, 1.0f);
}
// Normalize
vec_a = llVecNorm(vec_a);
vec_b = llVecNorm(vec_b);
// Calculate axis and rotation angle
LSL_Vector axis = vec_a % vec_b;
LSL_Float cos_theta = vec_a * vec_b;
// Check if parallel
if (cos_theta > 0.99999)
{
return new LSL_Rotation(0.0f, 0.0f, 0.0f, 1.0f);
}
// Check if anti-parallel
else if (cos_theta < -0.99999)
{
LSL_Vector orthog_axis = new LSL_Vector(1.0, 0.0, 0.0) - (vec_a.x / (vec_a * vec_a) * vec_a);
if (LSL_Vector.Mag(orthog_axis) < 0.000001) orthog_axis = new LSL_Vector(0.0, 0.0, 1.0);
return new LSL_Rotation((float)orthog_axis.x, (float)orthog_axis.y, (float)orthog_axis.z, 0.0);
}
else // other rotation
{
LSL_Float theta = (LSL_Float)Math.Acos(cos_theta) * 0.5f;
axis = llVecNorm(axis);
double x, y, z, s, t;
s = Math.Cos(theta);
t = Math.Sin(theta);
x = axis.x * t;
y = axis.y * t;
z = axis.z * t;
return new LSL_Rotation(x,y,z,s);
}
}
public void llWhisper(int channelID, string text)
{
if (text.Length > 1023)
text = text.Substring(0, 1023);
World.SimChat(Utils.StringToBytes(text),
ChatTypeEnum.Whisper, channelID, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.DeliverMessage(ChatTypeEnum.Whisper, channelID, m_host.Name, m_host.UUID, text);
}
private void CheckSayShoutTime()
{
DateTime now = DateTime.UtcNow;
if ((now - m_lastSayShoutCheck).Ticks > 10000000) // 1sec
{
m_lastSayShoutCheck = now;
m_SayShoutCount = 0;
}
else
m_SayShoutCount++;
}
public void llSay(int channelID, string text)
{
if (channelID == 0)
// m_SayShoutCount++;
CheckSayShoutTime();
//// if (m_SayShoutCount >= 11)
//// ScriptSleep(2000);
if (m_scriptConsoleChannelEnabled && (channelID == m_scriptConsoleChannel))
{
Console.WriteLine(text);
}
else
{
if (text.Length > 1023)
text = text.Substring(0, 1023);
World.SimChat(Utils.StringToBytes(text),
ChatTypeEnum.Say, channelID, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.DeliverMessage(ChatTypeEnum.Say, channelID, m_host.Name, m_host.UUID, text);
}
}
public void llShout(int channelID, string text)
{
if (channelID == 0)
// m_SayShoutCount++;
CheckSayShoutTime();
//// if (m_SayShoutCount >= 11)
//// ScriptSleep(2000);
if (text.Length > 1023)
text = text.Substring(0, 1023);
World.SimChat(Utils.StringToBytes(text),
ChatTypeEnum.Shout, channelID, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, true);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.DeliverMessage(ChatTypeEnum.Shout, channelID, m_host.Name, m_host.UUID, text);
}
public void llRegionSay(int channelID, string text)
{
if (channelID == 0)
{
Error("llRegionSay", "Cannot use on channel 0");
return;
}
if (text.Length > 1023)
text = text.Substring(0, 1023);
// debug channel is also sent to avatars
if (channelID == ScriptBaseClass.DEBUG_CHANNEL)
{
World.SimChat(Utils.StringToBytes(text),
ChatTypeEnum.Shout, channelID, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, true);
}
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
}
public void llRegionSayTo(string target, int channel, string msg)
{
if (msg.Length > 1023)
msg = msg.Substring(0, 1023);
if (channel == ScriptBaseClass.DEBUG_CHANNEL)
return;
UUID TargetID;
UUID.TryParse(target, out TargetID);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.DeliverMessageTo(TargetID, channel, m_host.AbsolutePosition, m_host.Name, m_host.UUID, msg);
}
public LSL_Integer llListen(int channelID, string name, string ID, string msg)
{
UUID keyID;
UUID.TryParse(ID, out keyID);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
return wComm.Listen(m_host.LocalId, m_item.ItemID, m_host.UUID, channelID, name, keyID, msg);
else
return -1;
}
public void llListenControl(int number, int active)
{
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.ListenControl(m_item.ItemID, number, active);
}
public void llListenRemove(int number)
{
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.ListenRemove(m_item.ItemID, number);
}
public void llSensor(string name, string id, int type, double range, double arc)
{
UUID keyID = UUID.Zero;
UUID.TryParse(id, out keyID);
AsyncCommands.SensorRepeatPlugin.SenseOnce(m_host.LocalId, m_item.ItemID, name, keyID, type, range, arc, m_host);
}
public void llSensorRepeat(string name, string id, int type, double range, double arc, double rate)
{
UUID keyID = UUID.Zero;
UUID.TryParse(id, out keyID);
AsyncCommands.SensorRepeatPlugin.SetSenseRepeatEvent(m_host.LocalId, m_item.ItemID, name, keyID, type, range, arc, rate, m_host);
}
public void llSensorRemove()
{
AsyncCommands.SensorRepeatPlugin.UnSetSenseRepeaterEvents(m_host.LocalId, m_item.ItemID);
}
public string resolveName(UUID objecUUID)
{
// try avatar username surname
UserAccount account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, objecUUID);
if (account != null)
{
string avatarname = account.Name;
return avatarname;
}
// try an scene object
SceneObjectPart SOP = World.GetSceneObjectPart(objecUUID);
if (SOP != null)
{
string objectname = SOP.Name;
return objectname;
}
EntityBase SensedObject;
World.Entities.TryGetValue(objecUUID, out SensedObject);
if (SensedObject == null)
{
IGroupsModule groups = World.RequestModuleInterface();
if (groups != null)
{
GroupRecord gr = groups.GetGroupRecord(objecUUID);
if (gr != null)
return gr.GroupName;
}
return String.Empty;
}
return SensedObject.Name;
}
public LSL_String llDetectedName(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Name;
}
public LSL_String llDetectedKey(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Key.ToString();
}
public LSL_String llDetectedOwner(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Owner.ToString();
}
public LSL_Integer llDetectedType(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return 0;
return new LSL_Integer(detectedParams.Type);
}
public LSL_Vector llDetectedPos(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.Position;
}
public LSL_Vector llDetectedVel(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.Velocity;
}
public LSL_Vector llDetectedGrab(int number)
{
DetectParams parms = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (parms == null)
return new LSL_Vector(0, 0, 0);
return parms.OffsetPos;
}
public LSL_Rotation llDetectedRot(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return new LSL_Rotation();
return detectedParams.Rotation;
}
public LSL_Integer llDetectedGroup(int number)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (detectedParams == null)
return new LSL_Integer(0);
if (m_host.GroupID == detectedParams.Group)
return new LSL_Integer(1);
return new LSL_Integer(0);
}
public LSL_Integer llDetectedLinkNumber(int number)
{
DetectParams parms = m_ScriptEngine.GetDetectParams(m_item.ItemID, number);
if (parms == null)
return new LSL_Integer(0);
return new LSL_Integer(parms.LinkNum);
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchBinormal for details
///
public LSL_Vector llDetectedTouchBinormal(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.TouchBinormal;
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchFace for details
///
public LSL_Integer llDetectedTouchFace(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Integer(-1);
return new LSL_Integer(detectedParams.TouchFace);
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchNormal for details
///
public LSL_Vector llDetectedTouchNormal(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.TouchNormal;
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchPos for details
///
public LSL_Vector llDetectedTouchPos(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.TouchPos;
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchST for details
///
public LSL_Vector llDetectedTouchST(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Vector(-1.0, -1.0, 0.0);
return detectedParams.TouchST;
}
///
/// See http://wiki.secondlife.com/wiki/LlDetectedTouchUV for details
///
public LSL_Vector llDetectedTouchUV(int index)
{
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, index);
if (detectedParams == null)
return new LSL_Vector(-1.0, -1.0, 0.0);
return detectedParams.TouchUV;
}
[DebuggerNonUserCode]
public virtual void llDie()
{
if (!m_host.ParentGroup.IsAttachment) throw new SelfDeleteException();
}
public LSL_Float llGround(LSL_Vector offset)
{
Vector3 pos = m_host.GetWorldPosition() + (Vector3)offset;
//Get the slope normal. This gives us the equation of the plane tangent to the slope.
LSL_Vector vsn = llGroundNormal(offset);
// Clamp to valid position
if (pos.X < 0)
pos.X = 0;
else if (pos.X >= World.Heightmap.Width)
pos.X = World.Heightmap.Width - 1;
if (pos.Y < 0)
pos.Y = 0;
else if (pos.Y >= World.Heightmap.Height)
pos.Y = World.Heightmap.Height - 1;
//Get the height for the integer coordinates from the Heightmap
float baseheight = (float)World.Heightmap[(int)pos.X, (int)pos.Y];
//Calculate the difference between the actual coordinates and the integer coordinates
float xdiff = pos.X - (float)((int)pos.X);
float ydiff = pos.Y - (float)((int)pos.Y);
//Use the equation of the tangent plane to adjust the height to account for slope
return (((vsn.x * xdiff) + (vsn.y * ydiff)) / (-1 * vsn.z)) + baseheight;
}
public LSL_Float llCloud(LSL_Vector offset)
{
float cloudCover = 0f;
ICloudModule module = World.RequestModuleInterface();
if (module != null)
{
Vector3 pos = m_host.GetWorldPosition();
int x = (int)(pos.X + offset.x);
int y = (int)(pos.Y + offset.y);
cloudCover = module.CloudCover(x, y, 0);
}
return cloudCover;
}
public LSL_Vector llWind(LSL_Vector offset)
{
LSL_Vector wind = new LSL_Vector(0, 0, 0);
IWindModule module = World.RequestModuleInterface();
if (module != null)
{
Vector3 pos = m_host.GetWorldPosition();
int x = (int)(pos.X + offset.x);
int y = (int)(pos.Y + offset.y);
Vector3 windSpeed = module.WindSpeed(x, y, 0);
wind.x = windSpeed.X;
wind.y = windSpeed.Y;
}
return wind;
}
public void llSetStatus(int status, int value)
{
if (m_host == null || m_host.ParentGroup == null || m_host.ParentGroup.IsDeleted)
return;
int statusrotationaxis = 0;
if ((status & ScriptBaseClass.STATUS_PHYSICS) == ScriptBaseClass.STATUS_PHYSICS)
{
if (value != 0)
{
SceneObjectGroup group = m_host.ParentGroup;
bool allow = true;
int maxprims = World.m_linksetPhysCapacity;
bool checkShape = (maxprims > 0 && group.PrimCount > maxprims);
foreach (SceneObjectPart part in group.Parts)
{
if (part.PhysicsShapeType == (byte)PhysicsShapeType.None)
continue;
if (part.Scale.X > World.m_maxPhys || part.Scale.Y > World.m_maxPhys || part.Scale.Z > World.m_maxPhys)
{
allow = false;
break;
}
if (checkShape)
{
if (--maxprims < 0)
{
allow = false;
break;
}
}
}
if (!allow)
return;
if (m_host.ParentGroup.RootPart.PhysActor != null &&
m_host.ParentGroup.RootPart.PhysActor.IsPhysical)
return;
m_host.ScriptSetPhysicsStatus(true);
}
else
{
m_host.ScriptSetPhysicsStatus(false);
}
}
if ((status & ScriptBaseClass.STATUS_PHANTOM) == ScriptBaseClass.STATUS_PHANTOM)
{
m_host.ParentGroup.ScriptSetPhantomStatus(value != 0);
}
if ((status & ScriptBaseClass.STATUS_CAST_SHADOWS) == ScriptBaseClass.STATUS_CAST_SHADOWS)
{
m_host.AddFlag(PrimFlags.CastShadows);
}
if ((status & ScriptBaseClass.STATUS_ROTATE_X) == ScriptBaseClass.STATUS_ROTATE_X)
{
statusrotationaxis |= ScriptBaseClass.STATUS_ROTATE_X;
}
if ((status & ScriptBaseClass.STATUS_ROTATE_Y) == ScriptBaseClass.STATUS_ROTATE_Y)
{
statusrotationaxis |= ScriptBaseClass.STATUS_ROTATE_Y;
}
if ((status & ScriptBaseClass.STATUS_ROTATE_Z) == ScriptBaseClass.STATUS_ROTATE_Z)
{
statusrotationaxis |= ScriptBaseClass.STATUS_ROTATE_Z;
}
if ((status & ScriptBaseClass.STATUS_BLOCK_GRAB) == ScriptBaseClass.STATUS_BLOCK_GRAB)
m_host.BlockGrab = value != 0;
if ((status & ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT) == ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT)
m_host.ParentGroup.BlockGrabOverride = value != 0;
if ((status & ScriptBaseClass.STATUS_DIE_AT_EDGE) == ScriptBaseClass.STATUS_DIE_AT_EDGE)
{
if (value != 0)
m_host.SetDieAtEdge(true);
else
m_host.SetDieAtEdge(false);
}
if ((status & ScriptBaseClass.STATUS_RETURN_AT_EDGE) == ScriptBaseClass.STATUS_RETURN_AT_EDGE)
{
if (value != 0)
m_host.SetReturnAtEdge(true);
else
m_host.SetReturnAtEdge(false);
}
if ((status & ScriptBaseClass.STATUS_SANDBOX) == ScriptBaseClass.STATUS_SANDBOX)
{
if (value != 0)
m_host.SetStatusSandbox(true);
else
m_host.SetStatusSandbox(false);
}
if (statusrotationaxis != 0)
{
m_host.SetAxisRotation(statusrotationaxis, value);
}
}
private bool IsPhysical()
{
return ((m_host.GetEffectiveObjectFlags() & (uint)PrimFlags.Physics) == (uint)PrimFlags.Physics);
}
public LSL_Integer llGetStatus(int status)
{
// m_log.Debug(m_host.ToString() + " status is " + m_host.GetEffectiveObjectFlags().ToString());
switch (status)
{
case ScriptBaseClass.STATUS_PHYSICS:
return IsPhysical() ? 1 : 0;
case ScriptBaseClass.STATUS_PHANTOM:
if ((m_host.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) == (uint)PrimFlags.Phantom)
{
return 1;
}
return 0;
case ScriptBaseClass.STATUS_CAST_SHADOWS:
if ((m_host.GetEffectiveObjectFlags() & (uint)PrimFlags.CastShadows) == (uint)PrimFlags.CastShadows)
{
return 1;
}
return 0;
case ScriptBaseClass.STATUS_BLOCK_GRAB:
return m_host.BlockGrab ? 1 : 0;
case ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT:
return m_host.ParentGroup.BlockGrabOverride ? 1 : 0;
case ScriptBaseClass.STATUS_DIE_AT_EDGE:
if (m_host.GetDieAtEdge())
return 1;
else
return 0;
case ScriptBaseClass.STATUS_RETURN_AT_EDGE:
if (m_host.GetReturnAtEdge())
return 1;
else
return 0;
case ScriptBaseClass.STATUS_ROTATE_X:
// if (m_host.GetAxisRotation(2) != 0)
if (m_host.GetAxisRotation((int)SceneObjectGroup.axisSelect.STATUS_ROTATE_X) != 0)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_ROTATE_Y:
if (m_host.GetAxisRotation((int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Y) != 0)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_ROTATE_Z:
if (m_host.GetAxisRotation((int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Z) != 0)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_SANDBOX:
if (m_host.GetStatusSandbox())
return 1;
else
return 0;
}
return 0;
}
public LSL_Integer llScaleByFactor(double scaling_factor)
{
SceneObjectGroup group = m_host.ParentGroup;
if(scaling_factor < 1e-6)
return ScriptBaseClass.FALSE;
if(scaling_factor > 1e6)
return ScriptBaseClass.FALSE;
if (group == null || group.IsDeleted || group.inTransit)
return ScriptBaseClass.FALSE;
if (group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical)
return ScriptBaseClass.FALSE;
if (group.RootPart.KeyframeMotion != null)
return ScriptBaseClass.FALSE;
if(group.GroupResize(scaling_factor))
return ScriptBaseClass.TRUE;
else
return ScriptBaseClass.FALSE;
}
public LSL_Float llGetMaxScaleFactor()
{
SceneObjectGroup group = m_host.ParentGroup;
if (group == null || group.IsDeleted || group.inTransit)
return 1.0f;
return (LSL_Float)group.GetMaxGroupResizeScale();
}
public LSL_Float llGetMinScaleFactor()
{
SceneObjectGroup group = m_host.ParentGroup;
if (group == null || group.IsDeleted || group.inTransit)
return 1.0f;
return (LSL_Float)group.GetMinGroupResizeScale();
}
public void llSetScale(LSL_Vector scale)
{
SetScale(m_host, scale);
}
protected void SetScale(SceneObjectPart part, LSL_Vector scale)
{
// TODO: this needs to trigger a persistance save as well
if (part == null || part.ParentGroup.IsDeleted)
return;
// First we need to check whether or not we need to clamp the size of a physics-enabled prim
PhysicsActor pa = part.ParentGroup.RootPart.PhysActor;
if (pa != null && pa.IsPhysical)
{
scale.x = Math.Max(World.m_minPhys, Math.Min(World.m_maxPhys, scale.x));
scale.y = Math.Max(World.m_minPhys, Math.Min(World.m_maxPhys, scale.y));
scale.z = Math.Max(World.m_minPhys, Math.Min(World.m_maxPhys, scale.z));
}
else
{
// If not physical, then we clamp the scale to the non-physical min/max
scale.x = Math.Max(World.m_minNonphys, Math.Min(World.m_maxNonphys, scale.x));
scale.y = Math.Max(World.m_minNonphys, Math.Min(World.m_maxNonphys, scale.y));
scale.z = Math.Max(World.m_minNonphys, Math.Min(World.m_maxNonphys, scale.z));
}
Vector3 tmp = part.Scale;
tmp.X = (float)scale.x;
tmp.Y = (float)scale.y;
tmp.Z = (float)scale.z;
part.Scale = tmp;
part.SendFullUpdateToAllClients();
}
public LSL_Vector llGetScale()
{
return new LSL_Vector(m_host.Scale.X, m_host.Scale.Y, m_host.Scale.Z);
}
public void llSetClickAction(int action)
{
m_host.ClickAction = (byte)action;
m_host.ParentGroup.HasGroupChanged = true;
m_host.ScheduleFullUpdate();
return;
}
public void llSetColor(LSL_Vector color, int face)
{
SetColor(m_host, color, face);
}
protected void SetColor(SceneObjectPart part, LSL_Vector color, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
Color4 texcolor;
if (face >= 0 && face < nsides)
{
texcolor = tex.CreateFace((uint)face).RGBA;
texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f);
texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f);
texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f);
tex.FaceTextures[face].RGBA = texcolor;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
texcolor = tex.FaceTextures[i].RGBA;
texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f);
texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f);
texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f);
tex.FaceTextures[i].RGBA = texcolor;
}
texcolor = tex.DefaultTexture.RGBA;
texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f);
texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f);
texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f);
tex.DefaultTexture.RGBA = texcolor;
}
part.UpdateTextureEntry(tex.GetBytes());
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
face = SceneObjectPart.ALL_SIDES;
m_host.SetFaceColorAlpha(face, color, null);
}
public void llSetContentType(LSL_Key id, LSL_Integer type)
{
if (m_UrlModule == null)
return;
// Make sure the content type is text/plain to start with
m_UrlModule.HttpContentType(new UUID(id), "text/plain");
// Is the object owner online and in the region
ScenePresence agent = World.GetScenePresence(m_host.ParentGroup.OwnerID);
if (agent == null || agent.IsChildAgent)
return; // Fail if the owner is not in the same region
// Is it the embeded browser?
string userAgent = m_UrlModule.GetHttpHeader(new UUID(id), "user-agent");
if (userAgent.IndexOf("SecondLife") < 0)
return; // Not the embedded browser. Is this check good enough?
// Use the IP address of the client and check against the request
// seperate logins from the same IP will allow all of them to get non-text/plain as long
// as the owner is in the region. Same as SL!
string logonFromIPAddress = agent.ControllingClient.RemoteEndPoint.Address.ToString();
string requestFromIPAddress = m_UrlModule.GetHttpHeader(new UUID(id), "remote_addr");
//m_log.Debug("IP from header='" + requestFromIPAddress + "' IP from endpoint='" + logonFromIPAddress + "'");
if (requestFromIPAddress == null || requestFromIPAddress.Trim() == "")
return;
if (logonFromIPAddress == null || logonFromIPAddress.Trim() == "")
return;
// If the request isnt from the same IP address then the request cannot be from the owner
if (!requestFromIPAddress.Trim().Equals(logonFromIPAddress.Trim()))
return;
switch (type)
{
case ScriptBaseClass.CONTENT_TYPE_HTML:
m_UrlModule.HttpContentType(new UUID(id), "text/html");
break;
case ScriptBaseClass.CONTENT_TYPE_XML:
m_UrlModule.HttpContentType(new UUID(id), "application/xml");
break;
case ScriptBaseClass.CONTENT_TYPE_XHTML:
m_UrlModule.HttpContentType(new UUID(id), "application/xhtml+xml");
break;
case ScriptBaseClass.CONTENT_TYPE_ATOM:
m_UrlModule.HttpContentType(new UUID(id), "application/atom+xml");
break;
case ScriptBaseClass.CONTENT_TYPE_JSON:
m_UrlModule.HttpContentType(new UUID(id), "application/json");
break;
case ScriptBaseClass.CONTENT_TYPE_LLSD:
m_UrlModule.HttpContentType(new UUID(id), "application/llsd+xml");
break;
case ScriptBaseClass.CONTENT_TYPE_FORM:
m_UrlModule.HttpContentType(new UUID(id), "application/x-www-form-urlencoded");
break;
case ScriptBaseClass.CONTENT_TYPE_RSS:
m_UrlModule.HttpContentType(new UUID(id), "application/rss+xml");
break;
default:
m_UrlModule.HttpContentType(new UUID(id), "text/plain");
break;
}
}
/*
public void llSetContentType(LSL_Key id, LSL_Integer content_type)
{
if (m_UrlModule != null)
{
string type = "text.plain";
if (content_type == (int)ScriptBaseClass.CONTENT_TYPE_HTML)
type = "text/html";
m_UrlModule.HttpContentType(new UUID(id),type);
}
}
*/
public void SetTexGen(SceneObjectPart part, int face,int style)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
MappingType textype;
textype = MappingType.Default;
if (style == (int)ScriptBaseClass.PRIM_TEXGEN_PLANAR)
textype = MappingType.Planar;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].TexMapType = textype;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].TexMapType = textype;
}
tex.DefaultTexture.TexMapType = textype;
}
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void SetGlow(SceneObjectPart part, int face, float glow)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Glow = glow;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Glow = glow;
}
tex.DefaultTexture.Glow = glow;
}
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void SetShiny(SceneObjectPart part, int face, int shiny, Bumpiness bump)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Shininess sval = new Shininess();
switch (shiny)
{
case 0:
sval = Shininess.None;
break;
case 1:
sval = Shininess.Low;
break;
case 2:
sval = Shininess.Medium;
break;
case 3:
sval = Shininess.High;
break;
default:
sval = Shininess.None;
break;
}
int nsides = GetNumberOfSides(part);
Primitive.TextureEntry tex = part.Shape.Textures;
if (face >= 0 && face < nsides)
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Shiny = sval;
tex.FaceTextures[face].Bump = bump;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Shiny = sval;
tex.FaceTextures[i].Bump = bump;
}
tex.DefaultTexture.Shiny = sval;
tex.DefaultTexture.Bump = bump;
}
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void SetFullBright(SceneObjectPart part, int face, bool bright)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
int nsides = GetNumberOfSides(part);
Primitive.TextureEntry tex = part.Shape.Textures;
if (face >= 0 && face < nsides)
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Fullbright = bright;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Fullbright = bright;
}
}
tex.DefaultTexture.Fullbright = bright;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public LSL_Float llGetAlpha(int face)
{
return GetAlpha(m_host, face);
}
protected LSL_Float GetAlpha(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face == ScriptBaseClass.ALL_SIDES)
{
int i;
double sum = 0.0;
for (i = 0 ; i < nsides; i++)
sum += (double)tex.GetFace((uint)i).RGBA.A;
return sum;
}
if (face >= 0 && face < nsides)
{
return (double)tex.GetFace((uint)face).RGBA.A;
}
return 0.0;
}
public void llSetAlpha(double alpha, int face)
{
SetAlpha(m_host, alpha, face);
}
public void llSetLinkAlpha(int linknumber, double alpha, int face)
{
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
foreach (SceneObjectPart part in parts)
SetAlpha(part, alpha, face);
}
finally { }
}
}
protected void SetAlpha(SceneObjectPart part, double alpha, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
Color4 texcolor;
if (face >= 0 && face < nsides)
{
texcolor = tex.CreateFace((uint)face).RGBA;
texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f);
tex.FaceTextures[face].RGBA = texcolor;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
texcolor = tex.FaceTextures[i].RGBA;
texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f);
tex.FaceTextures[i].RGBA = texcolor;
}
}
// In some cases, the default texture can be null, eg when every face
// has a unique texture
if (tex.DefaultTexture != null)
{
texcolor = tex.DefaultTexture.RGBA;
texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f);
tex.DefaultTexture.RGBA = texcolor;
}
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
///
/// Set flexi parameters of a part.
///
/// FIXME: Much of this code should probably be within the part itself.
///
///
///
///
///
///
///
///
///
protected void SetFlexi(SceneObjectPart part, bool flexi, int softness, float gravity, float friction,
float wind, float tension, LSL_Vector Force)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
if (flexi)
{
part.Shape.FlexiEntry = true; // this setting flexi true isn't working, but the below parameters do
// work once the prim is already flexi
part.Shape.FlexiSoftness = softness;
part.Shape.FlexiGravity = gravity;
part.Shape.FlexiDrag = friction;
part.Shape.FlexiWind = wind;
part.Shape.FlexiTension = tension;
part.Shape.FlexiForceX = (float)Force.x;
part.Shape.FlexiForceY = (float)Force.y;
part.Shape.FlexiForceZ = (float)Force.z;
part.Shape.PathCurve = (byte)Extrusion.Flexible;
}
else
{
// Other values not set, they do not seem to be sent to the viewer
// Setting PathCurve appears to be what actually toggles the check box and turns Flexi on and off
part.Shape.PathCurve = (byte)Extrusion.Straight;
part.Shape.FlexiEntry = false;
}
part.ParentGroup.HasGroupChanged = true;
part.ScheduleFullUpdate();
}
///
/// Set a light point on a part
///
/// FIXME: Much of this code should probably be in SceneObjectGroup
///
///
///
///
///
///
///
protected void SetPointLight(SceneObjectPart part, bool light, LSL_Vector color, float intensity, float radius, float falloff)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
if (light)
{
part.Shape.LightEntry = true;
part.Shape.LightColorR = Util.Clip((float)color.x, 0.0f, 1.0f);
part.Shape.LightColorG = Util.Clip((float)color.y, 0.0f, 1.0f);
part.Shape.LightColorB = Util.Clip((float)color.z, 0.0f, 1.0f);
part.Shape.LightIntensity = Util.Clip((float)intensity, 0.0f, 1.0f);
part.Shape.LightRadius = Util.Clip((float)radius, 0.1f, 20.0f);
part.Shape.LightFalloff = Util.Clip((float)falloff, 0.01f, 2.0f);
}
else
{
part.Shape.LightEntry = false;
}
part.ParentGroup.HasGroupChanged = true;
part.ScheduleFullUpdate();
}
public LSL_Vector llGetColor(int face)
{
return GetColor(m_host, face);
}
protected LSL_Vector GetColor(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
Color4 texcolor;
LSL_Vector rgb = new LSL_Vector();
int nsides = GetNumberOfSides(part);
if (face == ScriptBaseClass.ALL_SIDES)
{
int i;
for (i = 0; i < nsides; i++)
{
texcolor = tex.GetFace((uint)i).RGBA;
rgb.x += texcolor.R;
rgb.y += texcolor.G;
rgb.z += texcolor.B;
}
float invnsides = 1.0f / (float)nsides;
rgb.x *= invnsides;
rgb.y *= invnsides;
rgb.z *= invnsides;
return rgb;
}
if (face >= 0 && face < nsides)
{
texcolor = tex.GetFace((uint)face).RGBA;
rgb.x = texcolor.R;
rgb.y = texcolor.G;
rgb.z = texcolor.B;
return rgb;
}
else
{
return new LSL_Vector();
}
}
public void llSetTexture(string texture, int face)
{
SetTexture(m_host, texture, face);
}
public void llSetLinkTexture(int linknumber, string texture, int face)
{
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
foreach (SceneObjectPart part in parts)
SetTexture(part, texture, face);
}
finally { }
}
}
protected void SetTexture(SceneObjectPart part, string texture, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
UUID textureID = new UUID();
textureID = ScriptUtils.GetAssetIdFromItemName(m_host, texture, (int)AssetType.Texture);
if (textureID == UUID.Zero)
{
if (!UUID.TryParse(texture, out textureID))
return;
}
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.TextureID = textureID;
tex.FaceTextures[face] = texface;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].TextureID = textureID;
}
}
tex.DefaultTexture.TextureID = textureID;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void llScaleTexture(double u, double v, int face)
{
ScaleTexture(m_host, u, v, face);
}
protected void ScaleTexture(SceneObjectPart part, double u, double v, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.RepeatU = (float)u;
texface.RepeatV = (float)v;
tex.FaceTextures[face] = texface;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].RepeatU = (float)u;
tex.FaceTextures[i].RepeatV = (float)v;
}
}
tex.DefaultTexture.RepeatU = (float)u;
tex.DefaultTexture.RepeatV = (float)v;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void llOffsetTexture(double u, double v, int face)
{
OffsetTexture(m_host, u, v, face);
}
protected void OffsetTexture(SceneObjectPart part, double u, double v, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.OffsetU = (float)u;
texface.OffsetV = (float)v;
tex.FaceTextures[face] = texface;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].OffsetU = (float)u;
tex.FaceTextures[i].OffsetV = (float)v;
}
}
tex.DefaultTexture.OffsetU = (float)u;
tex.DefaultTexture.OffsetV = (float)v;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public void llRotateTexture(double rotation, int face)
{
RotateTexture(m_host, rotation, face);
}
protected void RotateTexture(SceneObjectPart part, double rotation, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face >= 0 && face < nsides)
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.Rotation = (float)rotation;
tex.FaceTextures[face] = texface;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < nsides; i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Rotation = (float)rotation;
}
}
tex.DefaultTexture.Rotation = (float)rotation;
part.UpdateTextureEntry(tex.GetBytes());
return;
}
}
public LSL_String llGetTexture(int face)
{
return GetTexture(m_host, face);
}
protected LSL_String GetTexture(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
int nsides = GetNumberOfSides(part);
if (face == ScriptBaseClass.ALL_SIDES)
{
face = 0;
}
if (face >= 0 && face < nsides)
{
Primitive.TextureEntryFace texface;
texface = tex.GetFace((uint)face);
string texture = texface.TextureID.ToString();
lock (part.TaskInventory)
{
foreach (KeyValuePair inv in part.TaskInventory)
{
if (inv.Value.AssetID == texface.TextureID)
{
texture = inv.Value.Name.ToString();
break;
}
}
}
return texture;
}
else
{
return UUID.Zero.ToString();
}
}
public void llSetPos(LSL_Vector pos)
{
SetPos(m_host, pos, true);
}
///
/// Tries to move the entire object so that the root prim is within 0.1m of position. http://wiki.secondlife.com/wiki/LlSetRegionPos
/// Documentation indicates that the use of x/y coordinates up to 10 meters outside the bounds of a region will work but do not specify what happens if there is no adjacent region for the object to move into.
/// Uses the RegionSize constant here rather than hard-coding 266.0 to alert any developer modifying OpenSim to support variable-sized regions that this method will need tweaking.
///
///
/// 1 if successful, 0 otherwise.
public LSL_Integer llSetRegionPos(LSL_Vector pos)
{
// BEGIN WORKAROUND
// IF YOU GET REGION CROSSINGS WORKING WITH THIS FUNCTION, REPLACE THE WORKAROUND.
//
// This workaround is to prevent silent failure of this function.
// According to the specification on the SL Wiki, providing a position outside of the
if (pos.x < 0 || pos.x > World.RegionInfo.RegionSizeX || pos.y < 0 || pos.y > World.RegionInfo.RegionSizeY)
{
return 0;
}
// END WORK AROUND
else if ( // this is not part of the workaround if-block because it's not related to the workaround.
IsPhysical() ||
m_host.ParentGroup.IsAttachment || // return FALSE if attachment
(
pos.x < -10.0 || // return FALSE if more than 10 meters into a west-adjacent region.
pos.x > (World.RegionInfo.RegionSizeX + 10) || // return FALSE if more than 10 meters into a east-adjacent region.
pos.y < -10.0 || // return FALSE if more than 10 meters into a south-adjacent region.
pos.y > (World.RegionInfo.RegionSizeY + 10) // return FALSE if more than 10 meters into a north-adjacent region.
)
)
{
return 0;
}
// if we reach this point, then the object is not physical, it's not an attachment, and the destination is within the valid range.
// this could possibly be done in the above else-if block, but we're doing the check here to keep the code easier to read.
Vector3 objectPos = m_host.ParentGroup.RootPart.AbsolutePosition;
LandData here = World.GetLandData(objectPos);
LandData there = World.GetLandData(pos);
// we're only checking prim limits if it's moving to a different parcel under the assumption that if the object got onto the parcel without exceeding the prim limits.
bool sameParcel = here.GlobalID == there.GlobalID;
if (!sameParcel && !World.Permissions.CanRezObject(
m_host.ParentGroup.PrimCount, m_host.ParentGroup.OwnerID, pos))
{
return 0;
}
SetPos(m_host.ParentGroup.RootPart, pos, false);
return VecDist(pos, llGetRootPosition()) <= 0.1 ? 1 : 0;
}
// Capped movemment if distance > 10m (http://wiki.secondlife.com/wiki/LlSetPos)
// note linked setpos is capped "differently"
private LSL_Vector SetPosAdjust(LSL_Vector start, LSL_Vector end)
{
if (llVecDist(start, end) > 10.0f * m_ScriptDistanceFactor)
return start + m_ScriptDistanceFactor * 10.0f * llVecNorm(end - start);
else
return end;
}
protected LSL_Vector GetSetPosTarget(SceneObjectPart part, LSL_Vector targetPos, LSL_Vector fromPos, bool adjust)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return fromPos;
// Capped movemment if distance > 10m (http://wiki.secondlife.com/wiki/LlSetPos)
float ground = World.GetGroundHeight((float)targetPos.x, (float)targetPos.y);
bool disable_underground_movement = m_ScriptEngine.Config.GetBoolean("DisableUndergroundMovement", true);
if (part.ParentGroup.RootPart == part)
{
if ((targetPos.z < ground) && disable_underground_movement && m_host.ParentGroup.AttachmentPoint == 0)
targetPos.z = ground;
}
if (adjust)
return SetPosAdjust(fromPos, targetPos);
return targetPos;
}
///
/// set object position, optionally capping the distance.
///
///
///
/// if TRUE, will cap the distance to 10m.
protected void SetPos(SceneObjectPart part, LSL_Vector targetPos, bool adjust)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted || part.ParentGroup.inTransit)
return;
LSL_Vector currentPos = GetPartLocalPos(part);
LSL_Vector toPos = GetSetPosTarget(part, targetPos, currentPos, adjust);
if (part.ParentGroup.RootPart == part)
{
SceneObjectGroup parent = part.ParentGroup;
if (!parent.IsAttachment && !World.Permissions.CanObjectEntry(parent, false, (Vector3)toPos))
return;
parent.UpdateGroupPosition((Vector3)toPos);
}
else
{
part.OffsetPosition = (Vector3)toPos;
// SceneObjectGroup parent = part.ParentGroup;
// parent.HasGroupChanged = true;
// parent.ScheduleGroupForTerseUpdate();
part.ScheduleTerseUpdate();
}
}
public LSL_Vector llGetPos()
{
return m_host.GetWorldPosition();
}
public LSL_Vector llGetLocalPos()
{
return GetPartLocalPos(m_host);
}
protected LSL_Vector GetPartLocalPos(SceneObjectPart part)
{
Vector3 pos;
if (!part.IsRoot)
{
pos = part.OffsetPosition;
}
else
{
if (part.ParentGroup.IsAttachment)
pos = part.AttachedPos;
else
pos = part.AbsolutePosition;
}
// m_log.DebugFormat("[LSL API]: Returning {0} in GetPartLocalPos()", pos);
return new LSL_Vector(pos);
}
public void llSetRot(LSL_Rotation rot)
{
// try to let this work as in SL...
if (m_host.ParentID == 0 || (m_host.ParentGroup != null && m_host == m_host.ParentGroup.RootPart))
{
// special case: If we are root, rotate complete SOG to new rotation
SetRot(m_host, rot);
}
else
{
// we are a child. The rotation values will be set to the one of root modified by rot, as in SL. Don't ask.
SceneObjectPart rootPart = m_host.ParentGroup.RootPart;
if (rootPart != null) // better safe than sorry
{
SetRot(m_host, rootPart.RotationOffset * (Quaternion)rot);
}
}
}
public void llSetLocalRot(LSL_Rotation rot)
{
SetRot(m_host, rot);
}
protected void SetRot(SceneObjectPart part, Quaternion rot)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
bool isroot = (part == part.ParentGroup.RootPart);
bool isphys;
PhysicsActor pa = part.PhysActor;
// keep using physactor ideia of isphysical
// it should be SOP ideia of that
// not much of a issue with ubOde
if (pa != null && pa.IsPhysical)
isphys = true;
else
isphys = false;
// SL doesn't let scripts rotate root of physical linksets
if (isroot && isphys)
return;
part.UpdateRotation(rot);
// Update rotation does not move the object in the physics engine if it's a non physical linkset
// so do a nasty update of parts positions if is a root part rotation
if (isroot && pa != null) // with if above implies non physical root part
{
part.ParentGroup.ResetChildPrimPhysicsPositions();
}
else // fix sitting avatars. This is only needed bc of how we link avas to child parts, not root part
{
// List sittingavas = part.ParentGroup.GetLinkedAvatars();
List sittingavas = part.ParentGroup.GetSittingAvatars();
if (sittingavas.Count > 0)
{
foreach (ScenePresence av in sittingavas)
{
if (isroot || part.LocalId == av.ParentID)
av.SendTerseUpdateToAllClients();
}
}
}
}
///
/// See http://lslwiki.net/lslwiki/wakka.php?wakka=ChildRotation
///
public LSL_Rotation llGetRot()
{
// unlinked or root prim then use llRootRotation
// see llRootRotaion for references.
if (m_host.LinkNum == 0 || m_host.LinkNum == 1)
{
return llGetRootRotation();
}
Quaternion q = m_host.GetWorldRotation();
if (m_host.ParentGroup != null && m_host.ParentGroup.AttachmentPoint != 0)
{
ScenePresence avatar = World.GetScenePresence(m_host.ParentGroup.AttachedAvatar);
if (avatar != null)
{
if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0)
q = avatar.CameraRotation * q; // Mouselook
else
q = avatar.Rotation * q; // Currently infrequently updated so may be inaccurate
}
}
return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
}
private LSL_Rotation GetPartRot(SceneObjectPart part)
{
Quaternion q;
if (part.LinkNum == 0 || part.LinkNum == 1) // unlinked or root prim
{
if (part.ParentGroup.AttachmentPoint != 0)
{
ScenePresence avatar = World.GetScenePresence(part.ParentGroup.AttachedAvatar);
if (avatar != null)
{
if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0)
q = avatar.CameraRotation; // Mouselook
else
q = avatar.GetWorldRotation(); // Currently infrequently updated so may be inaccurate
}
else
q = part.ParentGroup.GroupRotation; // Likely never get here but just in case
}
else
q = part.ParentGroup.GroupRotation; // just the group rotation
return new LSL_Rotation(q);
}
q = part.GetWorldRotation();
if (part.ParentGroup.AttachmentPoint != 0)
{
ScenePresence avatar = World.GetScenePresence(part.ParentGroup.AttachedAvatar);
if (avatar != null)
{
if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0)
q = avatar.CameraRotation * q; // Mouselook
else
q = avatar.Rotation * q; // Currently infrequently updated so may be inaccurate
}
}
return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
}
public LSL_Rotation llGetLocalRot()
{
return GetPartLocalRot(m_host);
}
private LSL_Rotation GetPartLocalRot(SceneObjectPart part)
{
Quaternion rot = part.RotationOffset;
return new LSL_Rotation(rot.X, rot.Y, rot.Z, rot.W);
}
public void llSetForce(LSL_Vector force, int local)
{
if (!m_host.ParentGroup.IsDeleted)
{
if (local != 0)
force *= llGetRot();
m_host.ParentGroup.RootPart.SetForce(force);
}
}
public LSL_Vector llGetForce()
{
LSL_Vector force = new LSL_Vector(0.0, 0.0, 0.0);
if (!m_host.ParentGroup.IsDeleted)
{
force = m_host.ParentGroup.RootPart.GetForce();
}
return force;
}
public void llSetVelocity(LSL_Vector vel, int local)
{
m_host.SetVelocity(new Vector3((float)vel.x, (float)vel.y, (float)vel.z), local != 0);
}
public void llSetAngularVelocity(LSL_Vector avel, int local)
{
m_host.SetAngularVelocity(new Vector3((float)avel.x, (float)avel.y, (float)avel.z), local != 0);
}
public LSL_Integer llTarget(LSL_Vector position, double range)
{
return m_host.ParentGroup.registerTargetWaypoint(position,
(float)range);
}
public void llTargetRemove(int number)
{
m_host.ParentGroup.unregisterTargetWaypoint(number);
}
public LSL_Integer llRotTarget(LSL_Rotation rot, double error)
{
return m_host.ParentGroup.registerRotTargetWaypoint(rot, (float)error);
}
public void llRotTargetRemove(int number)
{
m_host.ParentGroup.unregisterRotTargetWaypoint(number);
}
public void llMoveToTarget(LSL_Vector target, double tau)
{
m_host.MoveToTarget(target, (float)tau);
}
public void llStopMoveToTarget()
{
m_host.StopMoveToTarget();
}
public void llApplyImpulse(LSL_Vector force, int local)
{
//No energy force yet
Vector3 v = force;
if (v.Length() > 20000.0f)
{
v.Normalize();
v = v * 20000.0f;
}
m_host.ApplyImpulse(v, local != 0);
}
public void llApplyRotationalImpulse(LSL_Vector force, int local)
{
m_host.ParentGroup.RootPart.ApplyAngularImpulse(force, local != 0);
}
public void llSetTorque(LSL_Vector torque, int local)
{
m_host.ParentGroup.RootPart.SetAngularImpulse(torque, local != 0);
}
public LSL_Vector llGetTorque()
{
return new LSL_Vector(m_host.ParentGroup.GetTorque());
}
public void llSetForceAndTorque(LSL_Vector force, LSL_Vector torque, int local)
{
llSetForce(force, local);
llSetTorque(torque, local);
}
public LSL_Vector llGetVel()
{
Vector3 vel = Vector3.Zero;
if (m_host.ParentGroup.IsAttachment)
{
ScenePresence avatar = m_host.ParentGroup.Scene.GetScenePresence(m_host.ParentGroup.AttachedAvatar);
if (avatar != null)
vel = avatar.GetWorldVelocity();
}
else
{
vel = m_host.ParentGroup.RootPart.Velocity;
}
return new LSL_Vector(vel);
}
public LSL_Vector llGetAccel()
{
return new LSL_Vector(m_host.Acceleration);
}
public LSL_Vector llGetOmega()
{
Vector3 avel = m_host.AngularVelocity;
return new LSL_Vector(avel.X, avel.Y, avel.Z);
}
public LSL_Float llGetTimeOfDay()
{
return (double)((DateTime.Now.TimeOfDay.TotalMilliseconds / 1000) % (3600 * 4));
}
public LSL_Float llGetWallclock()
{
return DateTime.Now.TimeOfDay.TotalSeconds;
}
public LSL_Float llGetTime()
{
double ScriptTime = Util.GetTimeStampMS() - m_timer;
return (float)Math.Round((ScriptTime / 1000.0), 3);
}
public void llResetTime()
{
m_timer = Util.GetTimeStampMS();
}
public LSL_Float llGetAndResetTime()
{
double now = Util.GetTimeStampMS();
double ScriptTime = now - m_timer;
m_timer = now;
return (float)Math.Round((ScriptTime / 1000.0), 3);
}
public void llSound(string sound, double volume, int queue, int loop)
{
Deprecated("llSound", "Use llPlaySound instead");
}
// Xantor 20080528 PlaySound updated so it accepts an objectinventory name -or- a key to a sound
// 20080530 Updated to remove code duplication
public void llPlaySound(string sound, double volume)
{
// send the sound, once, to all clients in range
if (m_SoundModule != null)
{
m_SoundModule.SendSound(
m_host.UUID,
ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound),
volume, false, 0,
0, false, false);
}
}
public void llLoopSound(string sound, double volume)
{
if (m_SoundModule != null)
{
m_SoundModule.LoopSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound),
volume, 20, false,false);
}
}
public void llLoopSoundMaster(string sound, double volume)
{
if (m_SoundModule != null)
{
m_SoundModule.LoopSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound),
volume, 20, true, false);
}
}
public void llLoopSoundSlave(string sound, double volume)
{
if (m_SoundModule != null)
{
m_SoundModule.LoopSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound),
volume, 20, false, true);
}
}
public void llPlaySoundSlave(string sound, double volume)
{
// send the sound, once, to all clients in range
if (m_SoundModule != null)
{
m_SoundModule.SendSound(m_host.UUID,
ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), volume, false, 0,
0, true, false);
}
}
public void llTriggerSound(string sound, double volume)
{
// send the sound, once, to all clients in rangeTrigger or play an attached sound in this part's inventory.
if (m_SoundModule != null)
{
m_SoundModule.SendSound(m_host.UUID,
ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), volume, true, 0, 0,
false, false);
}
}
public void llStopSound()
{
if (m_SoundModule != null)
m_SoundModule.StopSound(m_host.UUID);
}
public void llPreloadSound(string sound)
{
if (m_SoundModule != null)
m_SoundModule.PreloadSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound), 0);
}
///
/// Return a portion of the designated string bounded by
/// inclusive indices (start and end). As usual, the negative
/// indices, and the tolerance for out-of-bound values, makes
/// this more complicated than it might otherwise seem.
///
public LSL_String llGetSubString(string src, int start, int end)
{
// Normalize indices (if negative).
// After normlaization they may still be
// negative, but that is now relative to
// the start, rather than the end, of the
// sequence.
if (start < 0)
{
start = src.Length+start;
}
if (end < 0)
{
end = src.Length+end;
}
// Conventional substring
if (start <= end)
{
// Implies both bounds are out-of-range.
if (end < 0 || start >= src.Length)
{
return String.Empty;
}
// If end is positive, then it directly
// corresponds to the lengt of the substring
// needed (plus one of course). BUT, it
// must be within bounds.
if (end >= src.Length)
{
end = src.Length-1;
}
if (start < 0)
{
return src.Substring(0,end+1);
}
// Both indices are positive
return src.Substring(start, (end+1) - start);
}
// Inverted substring (end < start)
else
{
// Implies both indices are below the
// lower bound. In the inverted case, that
// means the entire string will be returned
// unchanged.
if (start < 0)
{
return src;
}
// If both indices are greater than the upper
// bound the result may seem initially counter
// intuitive.
if (end >= src.Length)
{
return src;
}
if (end < 0)
{
if (start < src.Length)
{
return src.Substring(start);
}
else
{
return String.Empty;
}
}
else
{
if (start < src.Length)
{
return src.Substring(0,end+1) + src.Substring(start);
}
else
{
return src.Substring(0,end+1);
}
}
}
}
///
/// Delete substring removes the specified substring bounded
/// by the inclusive indices start and end. Indices may be
/// negative (indicating end-relative) and may be inverted,
/// i.e. end < start.
///
public LSL_String llDeleteSubString(string src, int start, int end)
{
// Normalize indices (if negative).
// After normlaization they may still be
// negative, but that is now relative to
// the start, rather than the end, of the
// sequence.
if (start < 0)
{
start = src.Length+start;
}
if (end < 0)
{
end = src.Length+end;
}
// Conventionally delimited substring
if (start <= end)
{
// If both bounds are outside of the existing
// string, then return unchanges.
if (end < 0 || start >= src.Length)
{
return src;
}
// At least one bound is in-range, so we
// need to clip the out-of-bound argument.
if (start < 0)
{
start = 0;
}
if (end >= src.Length)
{
end = src.Length-1;
}
return src.Remove(start,end-start+1);
}
// Inverted substring
else
{
// In this case, out of bounds means that
// the existing string is part of the cut.
if (start < 0 || end >= src.Length)
{
return String.Empty;
}
if (end > 0)
{
if (start < src.Length)
{
return src.Remove(start).Remove(0,end+1);
}
else
{
return src.Remove(0,end+1);
}
}
else
{
if (start < src.Length)
{
return src.Remove(start);
}
else
{
return src;
}
}
}
}
///
/// Insert string inserts the specified string identified by src
/// at the index indicated by index. Index may be negative, in
/// which case it is end-relative. The index may exceed either
/// string bound, with the result being a concatenation.
///
public LSL_String llInsertString(string dest, int index, string src)
{
// Normalize indices (if negative).
// After normlaization they may still be
// negative, but that is now relative to
// the start, rather than the end, of the
// sequence.
if (index < 0)
{
index = dest.Length+index;
// Negative now means it is less than the lower
// bound of the string.
if (index < 0)
{
return src+dest;
}
}
if (index >= dest.Length)
{
return dest+src;
}
// The index is in bounds.
// In this case the index refers to the index that will
// be assigned to the first character of the inserted string.
// So unlike the other string operations, we do not add one
// to get the correct string length.
return dest.Substring(0,index)+src+dest.Substring(index);
}
public LSL_String llToUpper(string src)
{
return src.ToUpper();
}
public LSL_String llToLower(string src)
{
return src.ToLower();
}
public LSL_Integer llGiveMoney(string destination, int amount)
{
Util.FireAndForget(x =>
{
if (m_item.PermsGranter == UUID.Zero)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0)
{
Error("llGiveMoney", "No permissions to give money");
return;
}
UUID toID = new UUID();
if (!UUID.TryParse(destination, out toID))
{
Error("llGiveMoney", "Bad key in llGiveMoney");
return;
}
IMoneyModule money = World.RequestModuleInterface();
if (money == null)
{
NotImplemented("llGiveMoney");
return;
}
string reason;
money.ObjectGiveMoney(
m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount,UUID.Zero, out reason);
}, null, "LSL_Api.llGiveMoney");
return 0;
}
public void llMakeExplosion(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
Deprecated("llMakeExplosion", "Use llParticleSystem instead");
}
public void llMakeFountain(int particles, double scale, double vel, double lifetime, double arc, int bounce, string texture, LSL_Vector offset, double bounce_offset)
{
Deprecated("llMakeFountain", "Use llParticleSystem instead");
}
public void llMakeSmoke(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
Deprecated("llMakeSmoke", "Use llParticleSystem instead");
}
public void llMakeFire(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
Deprecated("llMakeFire", "Use llParticleSystem instead");
}
public void llRezAtRoot(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param)
{
doObjectRez(inventory, pos, vel, rot, param, true);
}
public void doObjectRez(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param, bool atRoot)
{
if (Double.IsNaN(rot.x) || Double.IsNaN(rot.y) || Double.IsNaN(rot.z) || Double.IsNaN(rot.s))
return;
float dist = (float)llVecDist(llGetPos(), pos);
TaskInventoryItem item = m_host.Inventory.GetInventoryItem(inventory);
if (item == null)
{
Error("llRez(AtRoot/Object)", "Can't find object '" + inventory + "'");
return;
}
if (item.InvType != (int)InventoryType.Object)
{
Error("llRez(AtRoot/Object)", "Can't create requested object; object is missing from database");
return;
}
Util.FireAndForget(x =>
{
Quaternion wrot = rot;
wrot.Normalize();
List new_groups = World.RezObject(m_host, item, pos, wrot, vel, param, atRoot);
// If either of these are null, then there was an unknown error.
if (new_groups == null)
return;
bool notAttachment = !m_host.ParentGroup.IsAttachment;
foreach (SceneObjectGroup group in new_groups)
{
// objects rezzed with this method are die_at_edge by default.
group.RootPart.SetDieAtEdge(true);
group.ResumeScripts();
m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams(
"object_rez", new Object[] {
new LSL_String(
group.RootPart.UUID.ToString()) },
new DetectParams[0]));
if (notAttachment)
{
float groupmass = group.GetMass();
PhysicsActor pa = group.RootPart.PhysActor;
//Recoil.
if (pa != null && pa.IsPhysical && (Vector3)vel != Vector3.Zero)
{
Vector3 recoil = -vel * groupmass * m_recoilScaleFactor;
if (recoil != Vector3.Zero)
{
llApplyImpulse(recoil, 0);
}
}
}
}
}, null, "LSL_Api.doObjectRez");
}
public void llRezObject(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param)
{
doObjectRez(inventory, pos, vel, rot, param, false);
}
public void llLookAt(LSL_Vector target, double strength, double damping)
{
// Get the normalized vector to the target
LSL_Vector from = llGetPos();
// normalized direction to target
LSL_Vector dir = llVecNorm(target - from);
// use vertical to help compute left axis
// LSL_Vector up = new LSL_Vector(0.0, 0.0, 1.0);
// find normalized left axis parallel to horizon
// LSL_Vector left = llVecNorm(LSL_Vector.Cross(up, dir));
LSL_Vector left = new LSL_Vector(-dir.y, dir.x, 0.0f);
left = llVecNorm(left);
// make up orthogonal to left and dir
LSL_Vector up = LSL_Vector.Cross(dir, left);
// compute rotation based on orthogonal axes
// and rotate so Z points to target with X below horizont
LSL_Rotation rot = new LSL_Rotation(0.0, 0.707107, 0.0, 0.707107) * llAxes2Rot(dir, left, up);
SceneObjectGroup sog = m_host.ParentGroup;
if(sog == null || sog.IsDeleted)
return;
if (!sog.UsesPhysics || sog.IsAttachment)
{
// Do nothing if either value is 0 (this has been checked in SL)
if (strength <= 0.0 || damping <= 0.0)
return;
llSetLocalRot(rot);
}
else
{
if (strength == 0)
{
llSetLocalRot(rot);
return;
}
sog.StartLookAt(rot, (float)strength, (float)damping);
}
}
public void llStopLookAt()
{
m_host.StopLookAt();
}
public void llSetTimerEvent(double sec)
{
if (sec != 0.0 && sec < m_MinTimerInterval)
sec = m_MinTimerInterval;
// Setting timer repeat
AsyncCommands.TimerPlugin.SetTimerEvent(m_host.LocalId, m_item.ItemID, sec);
}
public virtual void llSleep(double sec)
{
// m_log.Info("llSleep snoozing " + sec + "s.");
Sleep((int)(sec * 1000));
}
public LSL_Float llGetMass()
{
if (m_host.ParentGroup.IsAttachment)
{
ScenePresence attachedAvatar = World.GetScenePresence(m_host.ParentGroup.AttachedAvatar);
if (attachedAvatar != null)
{
return attachedAvatar.GetMass();
}
else
{
return 0;
}
}
else
{
// new SL always returns object mass
// if (m_host.IsRoot)
// {
return m_host.ParentGroup.GetMass();
// }
// else
// {
// return m_host.GetMass();
// }
}
}
public LSL_Float llGetMassMKS()
{
return 100f * llGetMass();
}
public void llCollisionFilter(string name, string id, int accept)
{
m_host.CollisionFilter.Clear();
UUID objectID;
if (!UUID.TryParse(id, out objectID))
objectID = UUID.Zero;
if (objectID == UUID.Zero && name == "")
return;
m_host.CollisionFilter.Add(accept,objectID.ToString() + name);
}
public void llTakeControls(int controls, int accept, int pass_on)
{
if (m_item.PermsGranter != UUID.Zero)
{
ScenePresence presence = World.GetScenePresence(m_item.PermsGranter);
if (presence != null)
{
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
{
presence.RegisterControlEventsToScript(controls, accept, pass_on, m_host.LocalId, m_item.ItemID);
}
}
}
}
public void llReleaseControls()
{
if (m_item.PermsGranter != UUID.Zero)
{
ScenePresence presence = World.GetScenePresence(m_item.PermsGranter);
if (presence != null)
{
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
{
// Unregister controls from Presence
presence.UnRegisterControlEventsToScript(m_host.LocalId, m_item.ItemID);
// Remove Take Control permission.
m_item.PermsMask &= ~ScriptBaseClass.PERMISSION_TAKE_CONTROLS;
}
}
}
}
public void llReleaseURL(string url)
{
if (m_UrlModule != null)
m_UrlModule.ReleaseURL(url);
}
///
/// Attach the object containing this script to the avatar that owns it.
///
///
/// The attachment point (e.g. ATTACH_CHEST)
///
/// true if the attach suceeded, false if it did not
public bool AttachToAvatar(int attachmentPoint)
{
SceneObjectGroup grp = m_host.ParentGroup;
ScenePresence presence = World.GetScenePresence(m_host.OwnerID);
IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule;
if (attachmentsModule != null)
return attachmentsModule.AttachObject(presence, grp, (uint)attachmentPoint, false, true, true);
else
return false;
}
///
/// Detach the object containing this script from the avatar it is attached to.
///
///
/// Nothing happens if the object is not attached.
///
public void DetachFromAvatar()
{
Util.FireAndForget(DetachWrapper, m_host, "LSL_Api.DetachFromAvatar");
}
private void DetachWrapper(object o)
{
if (World.AttachmentsModule != null)
{
SceneObjectPart host = (SceneObjectPart)o;
ScenePresence presence = World.GetScenePresence(host.OwnerID);
World.AttachmentsModule.DetachSingleAttachmentToInv(presence, host.ParentGroup);
}
}
public void llAttachToAvatar(int attachmentPoint)
{
if (m_item.PermsGranter != m_host.OwnerID)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_ATTACH) != 0)
AttachToAvatar(attachmentPoint);
}
public void llDetachFromAvatar()
{
if (m_host.ParentGroup.AttachmentPoint == 0)
return;
if (m_item.PermsGranter != m_host.OwnerID)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_ATTACH) != 0)
DetachFromAvatar();
}
public void llTakeCamera(string avatar)
{
Deprecated("llTakeCamera", "Use llSetCameraParams instead");
}
public void llReleaseCamera(string avatar)
{
Deprecated("llReleaseCamera", "Use llClearCameraParams instead");
}
public LSL_String llGetOwner()
{
return m_host.OwnerID.ToString();
}
public void llInstantMessage(string user, string message)
{
UUID result;
if (!UUID.TryParse(user, out result) || result == UUID.Zero)
{
Error("llInstantMessage","An invalid key was passed to llInstantMessage");
//// ScriptSleep(2000);
return;
}
// We may be able to use ClientView.SendInstantMessage here, but we need a client instance.
// InstantMessageModule.OnInstantMessage searches through a list of scenes for a client matching the toAgent,
// but I don't think we have a list of scenes available from here.
// (We also don't want to duplicate the code in OnInstantMessage if we can avoid it.)
// user is a UUID
// TODO: figure out values for client, fromSession, and imSessionID
// client.SendInstantMessage(m_host.UUID, fromSession, message, user, imSessionID, m_host.Name, AgentManager.InstantMessageDialog.MessageFromAgent, (uint)Util.UnixTimeSinceEpoch());
UUID friendTransactionID = UUID.Random();
//m_pendingFriendRequests.Add(friendTransactionID, fromAgentID);
GridInstantMessage msg = new GridInstantMessage();
msg.fromAgentID = new Guid(m_host.OwnerID.ToString()); // fromAgentID.Guid;
msg.toAgentID = new Guid(user); // toAgentID.Guid;
msg.imSessionID = new Guid(m_host.UUID.ToString()); // This is the item we're mucking with here
msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
msg.fromAgentName = m_host.Name;//client.FirstName + " " + client.LastName;// fromAgentName;
if (message != null && message.Length > 1024)
msg.message = message.Substring(0, 1024);
else
msg.message = message;
msg.dialog = (byte)19; // MessageFromObject
msg.fromGroup = false;// fromGroup;
msg.offline = (byte)0; //offline;
msg.ParentEstateID = World.RegionInfo.EstateSettings.EstateID;
msg.Position = new Vector3(m_host.AbsolutePosition);
msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid;
Vector3 pos = m_host.AbsolutePosition;
msg.binaryBucket
= Util.StringToBytes256(
"{0}/{1}/{2}/{3}",
World.RegionInfo.RegionName,
(int)Math.Floor(pos.X),
(int)Math.Floor(pos.Y),
(int)Math.Floor(pos.Z));
if (m_TransferModule != null)
{
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
}
}
public void llEmail(string address, string subject, string message)
{
IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface();
if (emailModule == null)
{
Error("llEmail", "Email module not configured");
return;
}
//Restrict email destination to the avatars registered email address?
//The restriction only applies if the destination address is not local.
if (m_restrictEmail == true && address.Contains(m_internalObjectHost) == false)
{
UserAccount account =
World.UserAccountService.GetUserAccount(
World.RegionInfo.ScopeID,
m_host.OwnerID);
if (account == null)
{
Error("llEmail", "Can't find user account for '" + m_host.OwnerID.ToString() + "'");
return;
}
if (String.IsNullOrEmpty(account.Email))
{
Error("llEmail", "User account has not registered an email address.");
return;
}
address = account.Email;
}
emailModule.SendEmail(m_host.UUID, address, subject, message);
//// ScriptSleep(m_sleepMsOnEmail);
}
public void llGetNextEmail(string address, string subject)
{
IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface();
if (emailModule == null)
{
Error("llGetNextEmail", "Email module not configured");
return;
}
Email email;
email = emailModule.GetNextEmail(m_host.UUID, address, subject);
if (email == null)
return;
m_ScriptEngine.PostObjectEvent(m_host.LocalId,
new EventParams("email",
new Object[] {
new LSL_String(email.time),
new LSL_String(email.sender),
new LSL_String(email.subject),
new LSL_String(email.message),
new LSL_Integer(email.numLeft)},
new DetectParams[0]));
}
public LSL_String llGetKey()
{
return m_host.UUID.ToString();
}
public LSL_Key llGenerateKey()
{
return UUID.Random().ToString();
}
public void llSetBuoyancy(double buoyancy)
{
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetBuoyancy((float)buoyancy);
}
}
///
/// Attempt to clamp the object on the Z axis at the given height over tau seconds.
///
/// Height to hover. Height of zero disables hover.
/// False if height is calculated just from ground, otherwise uses ground or water depending on whichever is higher
/// Number of seconds over which to reach target
public void llSetHoverHeight(double height, int water, double tau)
{
PIDHoverType hoverType = PIDHoverType.Ground;
if (water != 0)
{
hoverType = PIDHoverType.GroundAndWater;
}
m_host.SetHoverHeight((float)height, hoverType, (float)tau);
}
public void llStopHover()
{
m_host.SetHoverHeight(0f, PIDHoverType.Ground, 0f);
}
public void llMinEventDelay(double delay)
{
try
{
m_ScriptEngine.SetMinEventDelay(m_item.ItemID, delay);
}
catch (NotImplementedException)
{
// Currently not implemented in DotNetEngine only XEngine
NotImplemented("llMinEventDelay", "In DotNetEngine");
}
}
public void llSoundPreload(string sound)
{
Deprecated("llSoundPreload", "Use llPreloadSound instead");
}
public void llRotLookAt(LSL_Rotation target, double strength, double damping)
{
// Per discussion with Melanie, for non-physical objects llLookAt appears to simply
// set the rotation of the object, copy that behavior
SceneObjectGroup sog = m_host.ParentGroup;
if(sog == null || sog.IsDeleted)
return;
if (strength == 0 || !sog.UsesPhysics || sog.IsAttachment)
{
llSetLocalRot(target);
}
else
{
sog.RotLookAt(target, (float)strength, (float)damping);
}
}
public LSL_Integer llStringLength(string str)
{
if (str.Length > 0)
{
return str.Length;
}
else
{
return 0;
}
}
public void llStartAnimation(string anim)
{
if (m_item.PermsGranter == UUID.Zero)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) != 0)
{
ScenePresence presence = World.GetScenePresence(m_item.PermsGranter);
if (presence != null)
{
// Do NOT try to parse UUID, animations cannot be triggered by ID
UUID animID = ScriptUtils.GetAssetIdFromItemName(m_host, anim, (int)AssetType.Animation);
if (animID == UUID.Zero)
presence.Animator.AddAnimation(anim, m_host.UUID);
else
presence.Animator.AddAnimation(animID, m_host.UUID);
}
}
}
public void llStopAnimation(string anim)
{
if (m_item.PermsGranter == UUID.Zero)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) != 0)
{
ScenePresence presence = World.GetScenePresence(m_item.PermsGranter);
if (presence != null)
{
UUID animID = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, anim);
if (animID == UUID.Zero)
presence.Animator.RemoveAnimation(anim);
else
presence.Animator.RemoveAnimation(animID, true);
}
}
}
public void llPointAt(LSL_Vector pos)
{
}
public void llStopPointAt()
{
}
public void llTargetOmega(LSL_Vector axis, double spinrate, double gain)
{
TargetOmega(m_host, axis, spinrate, gain);
}
protected void TargetOmega(SceneObjectPart part, LSL_Vector axis, double spinrate, double gain)
{
PhysicsActor pa = part.PhysActor;
if ( ( pa == null || !pa.IsPhysical ) && gain == 0.0d )
spinrate = 0.0d;
part.UpdateAngularVelocity(axis * spinrate);
}
public LSL_Integer llGetStartParameter()
{
return m_ScriptEngine.GetStartParameter(m_item.ItemID);
}
public void llRequestPermissions(string agent, int perm)
{
UUID agentID;
if (!UUID.TryParse(agent, out agentID))
return;
if (agentID == UUID.Zero || perm == 0) // Releasing permissions
{
llReleaseControls();
m_item.PermsGranter = UUID.Zero;
m_item.PermsMask = 0;
m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(0) },
new DetectParams[0]));
return;
}
if (m_item.PermsGranter != agentID || (perm & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) == 0)
llReleaseControls();
int implicitPerms = 0;
if (m_host.ParentGroup.IsAttachment && (UUID)agent == m_host.ParentGroup.AttachedAvatar)
{
// When attached, certain permissions are implicit if requested from owner
implicitPerms = ScriptBaseClass.PERMISSION_TAKE_CONTROLS |
ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION |
ScriptBaseClass.PERMISSION_CONTROL_CAMERA |
ScriptBaseClass.PERMISSION_TRACK_CAMERA |
ScriptBaseClass.PERMISSION_ATTACH |
ScriptBaseClass.PERMISSION_OVERRIDE_ANIMATIONS;
}
else
{
if (m_host.ParentGroup.GetSittingAvatars().SingleOrDefault(sp => sp.UUID == agentID) != null)
{
// When agent is sitting, certain permissions are implicit if requested from sitting agent
implicitPerms = ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION |
ScriptBaseClass.PERMISSION_CONTROL_CAMERA |
ScriptBaseClass.PERMISSION_TRACK_CAMERA |
ScriptBaseClass.PERMISSION_TAKE_CONTROLS;
}
else
{
if (World.GetExtraSetting("auto_grant_attach_perms") == "true")
implicitPerms = ScriptBaseClass.PERMISSION_ATTACH;
}
if (World.GetExtraSetting("auto_grant_all_perms") == "true")
{
implicitPerms = perm;
}
}
if ((perm & (~implicitPerms)) == 0) // Requested only implicit perms
{
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[m_item.ItemID].PermsGranter = agentID;
m_host.TaskInventory[m_item.ItemID].PermsMask = perm;
m_host.TaskInventory.LockItemsForWrite(false);
m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(perm) },
new DetectParams[0]));
return;
}
ScenePresence presence = World.GetScenePresence(agentID);
if (presence != null)
{
// If permissions are being requested from an NPC and were not implicitly granted above then
// auto grant all requested permissions if the script is owned by the NPC or the NPCs owner
INPCModule npcModule = World.RequestModuleInterface();
if (npcModule != null && npcModule.IsNPC(agentID, World))
{
if (npcModule.CheckPermissions(agentID, m_host.OwnerID))
{
lock (m_host.TaskInventory)
{
m_host.TaskInventory[m_item.ItemID].PermsGranter = agentID;
m_host.TaskInventory[m_item.ItemID].PermsMask = perm;
}
m_ScriptEngine.PostScriptEvent(
m_item.ItemID,
new EventParams(
"run_time_permissions", new Object[] { new LSL_Integer(perm) }, new DetectParams[0]));
}
// it is an NPC, exit even if the permissions werent granted above, they are not going to answer
// the question!
return;
}
string ownerName = resolveName(m_host.ParentGroup.RootPart.OwnerID);
if (ownerName == String.Empty)
ownerName = "(hippos)";
if (!m_waitingForScriptAnswer)
{
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[m_item.ItemID].PermsGranter = agentID;
m_host.TaskInventory[m_item.ItemID].PermsMask = 0;
m_host.TaskInventory.LockItemsForWrite(false);
presence.ControllingClient.OnScriptAnswer += handleScriptAnswer;
m_waitingForScriptAnswer=true;
}
presence.ControllingClient.SendScriptQuestion(
m_host.UUID, m_host.ParentGroup.RootPart.Name, ownerName, m_item.ItemID, perm);
return;
}
// Requested agent is not in range, refuse perms
m_ScriptEngine.PostScriptEvent(
m_item.ItemID,
new EventParams("run_time_permissions", new Object[] { new LSL_Integer(0) }, new DetectParams[0]));
}
void handleScriptAnswer(IClientAPI client, UUID taskID, UUID itemID, int answer)
{
if (taskID != m_host.UUID)
return;
client.OnScriptAnswer -= handleScriptAnswer;
m_waitingForScriptAnswer = false;
if ((answer & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) == 0)
llReleaseControls();
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[m_item.ItemID].PermsMask = answer;
m_host.TaskInventory.LockItemsForWrite(false);
m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(answer) },
new DetectParams[0]));
}
public LSL_String llGetPermissionsKey()
{
return m_item.PermsGranter.ToString();
}
public LSL_Integer llGetPermissions()
{
int perms = m_item.PermsMask;
if (m_automaticLinkPermission)
perms |= ScriptBaseClass.PERMISSION_CHANGE_LINKS;
return perms;
}
public LSL_Integer llGetLinkNumber()
{
if (m_host.ParentGroup.PrimCount > 1)
{
return m_host.LinkNum;
}
else
{
return 0;
}
}
public void llSetLinkColor(int linknumber, LSL_Vector color, int face)
{
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
foreach (SceneObjectPart part in parts)
part.SetFaceColorAlpha(face, color, null);
}
finally { }
}
}
public void llCreateLink(string target, int parent)
{
UUID targetID;
if (!UUID.TryParse(target, out targetID))
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
Error("llCreateLink", "PERMISSION_CHANGE_LINKS permission not set");
return;
}
CreateLink(target, parent);
}
public void CreateLink(string target, int parent)
{
UUID targetID;
if (!UUID.TryParse(target, out targetID))
return;
SceneObjectPart targetPart = World.GetSceneObjectPart((UUID)targetID);
if (targetPart == null)
return;
if (targetPart.ParentGroup.AttachmentPoint != 0)
return; // Fail silently if attached
if (targetPart.ParentGroup.RootPart.OwnerID != m_host.ParentGroup.RootPart.OwnerID)
return;
SceneObjectGroup parentPrim = null, childPrim = null;
if (parent != 0)
{
parentPrim = m_host.ParentGroup;
childPrim = targetPart.ParentGroup;
}
else
{
parentPrim = targetPart.ParentGroup;
childPrim = m_host.ParentGroup;
}
// Required for linking
childPrim.RootPart.ClearUpdateSchedule();
parentPrim.LinkToGroup(childPrim, true);
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
parentPrim.RootPart.CreateSelected = true;
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
IClientAPI client = null;
ScenePresence sp = World.GetScenePresence(m_host.OwnerID);
if (sp != null)
client = sp.ControllingClient;
if (client != null)
parentPrim.SendPropertiesToClient(client);
}
public void llBreakLink(int linknum)
{
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
Error("llBreakLink", "PERMISSION_CHANGE_LINKS permission not set");
return;
}
BreakLink(linknum);
}
public void BreakLink(int linknum)
{
if (linknum < ScriptBaseClass.LINK_THIS)
return;
SceneObjectGroup parentPrim = m_host.ParentGroup;
if (parentPrim.AttachmentPoint != 0)
return; // Fail silently if attached
SceneObjectPart childPrim = null;
switch (linknum)
{
case ScriptBaseClass.LINK_ROOT:
break;
case ScriptBaseClass.LINK_SET:
case ScriptBaseClass.LINK_ALL_OTHERS:
case ScriptBaseClass.LINK_ALL_CHILDREN:
case ScriptBaseClass.LINK_THIS:
foreach (SceneObjectPart part in parentPrim.Parts)
{
if (part.UUID != m_host.UUID)
{
childPrim = part;
break;
}
}
break;
default:
childPrim = parentPrim.GetLinkNumPart(linknum);
if (childPrim.UUID == m_host.UUID)
childPrim = null;
break;
}
if (linknum == ScriptBaseClass.LINK_ROOT)
{
// Restructuring Multiple Prims.
List parts = new List(parentPrim.Parts);
parts.Remove(parentPrim.RootPart);
if (parts.Count > 0)
{
try
{
foreach (SceneObjectPart part in parts)
{
parentPrim.DelinkFromGroup(part.LocalId, true);
}
}
finally { }
}
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
if (parts.Count > 0)
{
SceneObjectPart newRoot = parts[0];
parts.Remove(newRoot);
try
{
foreach (SceneObjectPart part in parts)
{
part.ClearUpdateSchedule();
newRoot.ParentGroup.LinkToGroup(part.ParentGroup);
}
}
finally { }
newRoot.ParentGroup.HasGroupChanged = true;
newRoot.ParentGroup.ScheduleGroupForFullUpdate();
}
}
else
{
if (childPrim == null)
return;
parentPrim.DelinkFromGroup(childPrim.LocalId, true);
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
}
}
public void llBreakAllLinks()
{
TaskInventoryItem item = m_item;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
Error("llBreakAllLinks","Script trying to link but PERMISSION_CHANGE_LINKS permission not set!");
return;
}
BreakAllLinks();
}
public void BreakAllLinks()
{
SceneObjectGroup parentPrim = m_host.ParentGroup;
if (parentPrim.AttachmentPoint != 0)
return; // Fail silently if attached
List parts = new List(parentPrim.Parts);
parts.Remove(parentPrim.RootPart);
foreach (SceneObjectPart part in parts)
{
parentPrim.DelinkFromGroup(part.LocalId, true);
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
}
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
}
public LSL_String llGetLinkKey(int linknum)
{
SceneObjectPart part = m_host.ParentGroup.GetLinkNumPart(linknum);
if (part != null)
{
return part.UUID.ToString();
}
else
{
if (linknum > m_host.ParentGroup.PrimCount || (linknum == 1 && m_host.ParentGroup.PrimCount == 1))
{
linknum -= (m_host.ParentGroup.PrimCount) + 1;
if (linknum < 0)
return UUID.Zero.ToString();
List avatars = GetLinkAvatars(ScriptBaseClass.LINK_SET);
if (avatars.Count > linknum)
{
return avatars[linknum].UUID.ToString();
}
}
return UUID.Zero.ToString();
}
}
///
/// Returns the name of the child prim or seated avatar matching the
/// specified link number.
///
///
/// The number of a link in the linkset or a link-related constant.
///
///
/// The name determined to match the specified link number.
///
///
/// The rules governing the returned name are not simple. The only
/// time a blank name is returned is if the target prim has a blank
/// name. If no prim with the given link number can be found then
/// usually NULL_KEY is returned but there are exceptions.
///
/// In a single unlinked prim, A call with 0 returns the name, all
/// other values for link number return NULL_KEY
///
/// In link sets it is more complicated.
///
/// If the script is in the root prim:-
/// A zero link number returns NULL_KEY.
/// Positive link numbers return the name of the prim, or NULL_KEY
/// if a prim does not exist at that position.
/// Negative link numbers return the name of the first child prim.
///
/// If the script is in a child prim:-
/// Link numbers 0 or 1 return the name of the root prim.
/// Positive link numbers return the name of the prim or NULL_KEY
/// if a prim does not exist at that position.
/// Negative numbers return the name of the root prim.
///
/// References
/// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetLinkName
/// Mentions NULL_KEY being returned
/// http://wiki.secondlife.com/wiki/LlGetLinkName
/// Mentions using the LINK_* constants, some of which are negative
///
public LSL_String llGetLinkName(int linknum)
{
ISceneEntity entity = GetLinkEntity(m_host, linknum);
if (entity != null)
return entity.Name;
else
return ScriptBaseClass.NULL_KEY;
}
public LSL_Integer llGetInventoryNumber(int type)
{
int count = 0;
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Type == type || type == -1)
{
count = count + 1;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return count;
}
public LSL_String llGetInventoryName(int type, int number)
{
ArrayList keys = new ArrayList();
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Type == type || type == -1)
{
keys.Add(inv.Value.Name);
}
}
m_host.TaskInventory.LockItemsForRead(false);
if (keys.Count == 0)
{
return String.Empty;
}
keys.Sort();
if (keys.Count > number)
{
return (string)keys[number];
}
return String.Empty;
}
public LSL_Float llGetEnergy()
{
// TODO: figure out real energy value
return 1.0f;
}
public void llGiveInventory(string destination, string inventory)
{
UUID destId = UUID.Zero;
if (!UUID.TryParse(destination, out destId))
{
Error("llGiveInventory", "Can't parse destination key '" + destination + "'");
return;
}
TaskInventoryItem item = m_host.Inventory.GetInventoryItem(inventory);
if (item == null)
{
Error("llGiveInventory", "Can't find inventory object '" + inventory + "'");
return;
}
UUID objId = item.ItemID;
// check if destination is an object
if (World.GetSceneObjectPart(destId) != null)
{
// destination is an object
World.MoveTaskInventoryItem(destId, m_host, objId);
}
else
{
ScenePresence presence = World.GetScenePresence(destId);
if (presence == null)
{
UserAccount account =
World.UserAccountService.GetUserAccount(
World.RegionInfo.ScopeID,
destId);
if (account == null)
{
GridUserInfo info = World.GridUserService.GetGridUserInfo(destId.ToString());
if(info == null || info.Online == false)
{
Error("llGiveInventory", "Can't find destination '" + destId.ToString() + "'");
return;
}
}
}
// destination is an avatar
string message;
InventoryItemBase agentItem = World.MoveTaskInventoryItem(destId, UUID.Zero, m_host, objId, out message);
if (agentItem == null)
{
llSay(0, message);
return;
}
byte[] bucket = new byte[1];
bucket[0] = (byte)item.Type;
//byte[] objBytes = agentItem.ID.GetBytes();
//Array.Copy(objBytes, 0, bucket, 1, 16);
GridInstantMessage msg = new GridInstantMessage(World,
m_host.OwnerID, m_host.Name, destId,
(byte)InstantMessageDialog.TaskInventoryOffered,
false, item.Name+". "+m_host.Name+" is located at "+
World.RegionInfo.RegionName+" "+
m_host.AbsolutePosition.ToString(),
agentItem.ID, true, m_host.AbsolutePosition,
bucket, true);
ScenePresence sp;
if (World.TryGetScenePresence(destId, out sp))
{
sp.ControllingClient.SendInstantMessage(msg);
}
else
{
if (m_TransferModule != null)
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
}
}
}
[DebuggerNonUserCode]
public void llRemoveInventory(string name)
{
TaskInventoryItem item = m_host.Inventory.GetInventoryItem(name);
if (item == null)
return;
if (item.ItemID == m_item.ItemID)
throw new ScriptDeleteException();
else
m_host.Inventory.RemoveInventoryItem(item.ItemID);
}
public void llSetText(string text, LSL_Vector color, double alpha)
{
Vector3 av3 = Util.Clip(color, 0.0f, 1.0f);
byte[] data;
data = Util.StringToBytes256(text);
text = Util.UTF8.GetString(data);
m_host.SetText(text, av3, Util.Clip((float)alpha, 0.0f, 1.0f));
}
public LSL_Float llWater(LSL_Vector offset)
{
return World.RegionInfo.RegionSettings.WaterHeight;
}
public void llPassTouches(int pass)
{
if (pass != 0)
m_host.PassTouches = true;
else
m_host.PassTouches = false;
}
public LSL_String llRequestAgentData(string id, int data)
{
UUID uuid;
if (UUID.TryParse(id, out uuid))
{
PresenceInfo pinfo = null;
UserAccount account;
UserInfoCacheEntry ce;
if (!m_userInfoCache.TryGetValue(uuid, out ce))
{
account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, uuid);
if (account == null)
{
m_userInfoCache[uuid] = null; // Cache negative
return UUID.Zero.ToString();
}
PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() });
if (pinfos != null && pinfos.Length > 0)
{
foreach (PresenceInfo p in pinfos)
{
if (p.RegionID != UUID.Zero)
{
pinfo = p;
}
}
}
ce = new UserInfoCacheEntry();
ce.time = Util.EnvironmentTickCount();
ce.account = account;
ce.pinfo = pinfo;
m_userInfoCache[uuid] = ce;
}
else
{
if (ce == null)
return UUID.Zero.ToString();
account = ce.account;
pinfo = ce.pinfo;
}
if (Util.EnvironmentTickCount() < ce.time ||
(Util.EnvironmentTickCount() - ce.time) >= LlRequestAgentDataCacheTimeoutMs)
{
PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() });
if (pinfos != null && pinfos.Length > 0)
{
foreach (PresenceInfo p in pinfos)
{
if (p.RegionID != UUID.Zero)
{
pinfo = p;
}
}
}
else
pinfo = null;
ce.time = Util.EnvironmentTickCount();
ce.pinfo = pinfo;
}
string reply = String.Empty;
switch (data)
{
case ScriptBaseClass.DATA_ONLINE: // DATA_ONLINE (0|1)
if (pinfo != null && pinfo.RegionID != UUID.Zero)
reply = "1";
else
reply = "0";
break;
case ScriptBaseClass.DATA_NAME: // DATA_NAME (First Last)
reply = account.FirstName + " " + account.LastName;
break;
case ScriptBaseClass.DATA_BORN: // DATA_BORN (YYYY-MM-DD)
DateTime born = new DateTime(1970, 1, 1, 0, 0, 0, 0);
born = born.AddSeconds(account.Created);
reply = born.ToString("yyyy-MM-dd");
break;
case ScriptBaseClass.DATA_RATING: // DATA_RATING (0,0,0,0,0,0)
reply = "0,0,0,0,0,0";
break;
case 7: // DATA_USERLEVEL (integer). This is not available in LL and so has no constant.
reply = account.UserLevel.ToString();
break;
case ScriptBaseClass.DATA_PAYINFO: // DATA_PAYINFO (0|1|2|3)
reply = "0";
break;
default:
return UUID.Zero.ToString(); // Raise no event
}
UUID rq = UUID.Random();
UUID tid = AsyncCommands.
DataserverPlugin.RegisterRequest(m_host.LocalId,
m_item.ItemID, rq.ToString());
AsyncCommands.
DataserverPlugin.DataserverReply(rq.ToString(), reply);
return tid.ToString();
}
else
{
Error("llRequestAgentData","Invalid UUID passed to llRequestAgentData.");
}
return "";
}
public LSL_String llRequestInventoryData(string name)
{
foreach (TaskInventoryItem item in m_host.Inventory.GetInventoryItems())
{
if (item.Type == 3 && item.Name == name)
{
UUID tid = AsyncCommands.
DataserverPlugin.RegisterRequest(m_host.LocalId,
m_item.ItemID, item.AssetID.ToString());
Vector3 region = new Vector3(World.RegionInfo.WorldLocX, World.RegionInfo.WorldLocY, 0);
World.AssetService.Get(item.AssetID.ToString(), this,
delegate(string i, object sender, AssetBase a)
{
AssetLandmark lm = new AssetLandmark(a);
float rx = (uint)(lm.RegionHandle >> 32);
float ry = (uint)lm.RegionHandle;
region = lm.Position + new Vector3(rx, ry, 0) - region;
string reply = region.ToString();
AsyncCommands.
DataserverPlugin.DataserverReply(i.ToString(),
reply);
});
return tid.ToString();
}
}
return String.Empty;
}
public void llSetDamage(double damage)
{
m_host.ParentGroup.Damage = (float)damage;
}
public void llTeleportAgentHome(string agent)
{
UUID agentId = new UUID();
if (UUID.TryParse(agent, out agentId))
{
ScenePresence presence = World.GetScenePresence(agentId);
if (presence != null && presence.PresenceType != PresenceType.Npc)
{
// agent must not be a god
if (presence.GodController.UserLevel >= 200) return;
// agent must be over the owners land
if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID)
{
if (!World.TeleportClientHome(agentId, presence.ControllingClient))
{
// They can't be teleported home for some reason
GridRegion regionInfo = World.GridService.GetRegionByUUID(UUID.Zero, new UUID("2b02daac-e298-42fa-9a75-f488d37896e6"));
if (regionInfo != null)
{
World.RequestTeleportLocation(
presence.ControllingClient, regionInfo.RegionHandle, new Vector3(128, 128, 23), Vector3.Zero,
(uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome | Constants.TeleportFlags.ViaScript));
}
}
}
}
}
}
public void llTeleportAgent(string agent, string destination, LSL_Vector targetPos, LSL_Vector targetLookAt)
{
UUID agentId = new UUID();
if (UUID.TryParse(agent, out agentId))
{
ScenePresence presence = World.GetScenePresence(agentId);
if (presence != null && presence.PresenceType != PresenceType.Npc)
{
if (destination == String.Empty)
destination = World.RegionInfo.RegionName;
if (m_item.PermsGranter == agentId)
{
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TELEPORT) != 0)
{
DoLLTeleport(presence, destination, targetPos, targetLookAt);
}
}
// agent must be wearing the object
if (m_host.ParentGroup.AttachmentPoint != 0 && m_host.OwnerID == presence.UUID)
{
DoLLTeleport(presence, destination, targetPos, targetLookAt);
}
else
{
// agent must not be a god
if (presence.IsViewerUIGod) return;
// agent must be over the owners land
ILandObject agentLand = World.LandChannel.GetLandObject(presence.AbsolutePosition);
ILandObject objectLand = World.LandChannel.GetLandObject(m_host.AbsolutePosition);
if (m_host.OwnerID == objectLand.LandData.OwnerID && m_host.OwnerID == agentLand.LandData.OwnerID)
{
DoLLTeleport(presence, destination, targetPos, targetLookAt);
}
}
}
}
}
public void llTeleportAgentGlobalCoords(string agent, LSL_Vector global_coords, LSL_Vector targetPos, LSL_Vector targetLookAt)
{
UUID agentId = new UUID();
ulong regionHandle = Util.RegionWorldLocToHandle((uint)global_coords.x, (uint)global_coords.y);
if (UUID.TryParse(agent, out agentId))
{
// This function is owner only!
if (m_host.OwnerID != agentId)
return;
ScenePresence presence = World.GetScenePresence(agentId);
if (presence == null || presence.PresenceType == PresenceType.Npc)
return;
// Can't TP sitting avatars
if (presence.ParentID != 0) // Sitting
return;
if (m_item.PermsGranter == agentId)
{
// If attached using llAttachToAvatarTemp, cowardly refuse
if (m_host.ParentGroup.AttachmentPoint != 0 && m_host.ParentGroup.FromItemID == UUID.Zero)
return;
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TELEPORT) != 0)
{
World.RequestTeleportLocation(presence.ControllingClient, regionHandle, targetPos, targetLookAt, (uint)(Constants.TeleportFlags.ViaLocation | Constants.TeleportFlags.ViaScript));
}
}
}
}
private void DoLLTeleport(ScenePresence sp, string destination, Vector3 targetPos, Vector3 targetLookAt)
{
UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, destination);
// The destinaion is not an asset ID and also doesn't name a landmark.
// Use it as a sim name
if (assetID == UUID.Zero)
{
World.RequestTeleportLocation(sp.ControllingClient, destination, targetPos, targetLookAt, (uint)(Constants.TeleportFlags.ViaLocation | Constants.TeleportFlags.ViaScript));
return;
}
AssetBase lma = World.AssetService.Get(assetID.ToString());
if (lma == null)
return;
if (lma.Type != (sbyte)AssetType.Landmark)
return;
AssetLandmark lm = new AssetLandmark(lma);
World.RequestTeleportLocation(sp.ControllingClient, lm.RegionHandle, targetPos, targetLookAt, (uint)(Constants.TeleportFlags.ViaLocation | Constants.TeleportFlags.ViaScript));
}
public void llTextBox(string agent, string message, int chatChannel)
{
IDialogModule dm = World.RequestModuleInterface();
if (dm == null)
return;
UUID av = new UUID();
if (!UUID.TryParse(agent,out av))
{
Error("llTextBox", "First parameter must be a key");
return;
}
if (message == string.Empty)
{
Error("llTextBox", "Empty message");
}
else if (Encoding.UTF8.GetByteCount(message) > 512)
{
Error("llTextBox", "Message longer than 512 bytes");
}
else
{
dm.SendTextBoxToUser(av, message, chatChannel, m_host.Name, m_host.UUID, m_host.OwnerID);
}
}
public void llModifyLand(int action, int brush)
{
ITerrainModule tm = m_ScriptEngine.World.RequestModuleInterface();
if (tm != null)
{
tm.ModifyTerrain(m_host.OwnerID, m_host.AbsolutePosition, (byte) brush, (byte) action, m_host.OwnerID);
}
}
public void llCollisionSound(string impact_sound, double impact_volume)
{
if(impact_sound == "")
{
m_host.CollisionSoundVolume = (float)impact_volume;
m_host.CollisionSound = m_host.invalidCollisionSoundUUID;
m_host.CollisionSoundType = -1; // disable all sounds
m_host.aggregateScriptEvents();
return;
}
// TODO: Parameter check logic required.
UUID soundId = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, impact_sound, AssetType.Sound);
if(soundId != UUID.Zero)
{
m_host.CollisionSound = soundId;
m_host.CollisionSoundVolume = (float)impact_volume;
m_host.CollisionSoundType = 1;
}
else
m_host.CollisionSoundType = -1;
m_host.aggregateScriptEvents();
}
public LSL_String llGetAnimation(string id)
{
// This should only return a value if the avatar is in the same region
UUID avatar = (UUID)id;
ScenePresence presence = World.GetScenePresence(avatar);
if (presence == null)
return "";
if (m_host.RegionHandle == presence.RegionHandle)
{
if (presence != null)
{
// if (presence.SitGround)
// return "Sitting on Ground";
// if (presence.ParentID != 0 || presence.ParentUUID != UUID.Zero)
// return "Sitting";
string movementAnimation = presence.Animator.CurrentMovementAnimation;
string lslMovementAnimation;
if (MovementAnimationsForLSL.TryGetValue(movementAnimation, out lslMovementAnimation))
return lslMovementAnimation;
}
}
return String.Empty;
}
public void llMessageLinked(int linknumber, int num, string msg, string id)
{
List parts = GetLinkParts(linknumber);
UUID partItemID;
foreach (SceneObjectPart part in parts)
{
foreach (TaskInventoryItem item in part.Inventory.GetInventoryItems())
{
if (item.Type == ScriptBaseClass.INVENTORY_SCRIPT)
{
partItemID = item.ItemID;
int linkNumber = m_host.LinkNum;
if (m_host.ParentGroup.PrimCount == 1)
linkNumber = 0;
object[] resobj = new object[]
{
new LSL_Integer(linkNumber), new LSL_Integer(num), new LSL_String(msg), new LSL_String(id)
};
m_ScriptEngine.PostScriptEvent(partItemID,
new EventParams("link_message",
resobj, new DetectParams[0]));
}
}
}
}
public void llPushObject(string target, LSL_Vector impulse, LSL_Vector ang_impulse, int local)
{
bool pushrestricted = World.RegionInfo.RegionSettings.RestrictPushing;
bool pushAllowed = false;
bool pusheeIsAvatar = false;
UUID targetID = UUID.Zero;
if (!UUID.TryParse(target,out targetID))
return;
ScenePresence pusheeav = null;
Vector3 PusheePos = Vector3.Zero;
SceneObjectPart pusheeob = null;
ScenePresence avatar = World.GetScenePresence(targetID);
if (avatar != null)
{
pusheeIsAvatar = true;
// Pushee doesn't have a physics actor
if (avatar.PhysicsActor == null)
return;
// Pushee is in GodMode this pushing object isn't owned by them
if (avatar.IsViewerUIGod && m_host.OwnerID != targetID)
return;
pusheeav = avatar;
// Find pushee position
// Pushee Linked?
SceneObjectPart sitPart = pusheeav.ParentPart;
if (sitPart != null)
PusheePos = sitPart.AbsolutePosition;
else
PusheePos = pusheeav.AbsolutePosition;
}
if (!pusheeIsAvatar)
{
// not an avatar so push is not affected by parcel flags
pusheeob = World.GetSceneObjectPart((UUID)target);
// We can't find object
if (pusheeob == null)
return;
// Object not pushable. Not an attachment and has no physics component
if (!pusheeob.ParentGroup.IsAttachment && pusheeob.PhysActor == null)
return;
PusheePos = pusheeob.AbsolutePosition;
pushAllowed = true;
}
else
{
if (pushrestricted)
{
ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos);
// We didn't find the parcel but region is push restricted so assume it is NOT ok
if (targetlandObj == null)
return;
// Need provisions for Group Owned here
if (m_host.OwnerID == targetlandObj.LandData.OwnerID ||
targetlandObj.LandData.IsGroupOwned || m_host.OwnerID == targetID)
{
pushAllowed = true;
}
}
else
{
ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos);
if (targetlandObj == null)
{
// We didn't find the parcel but region isn't push restricted so assume it's ok
pushAllowed = true;
}
else
{
// Parcel push restriction
if ((targetlandObj.LandData.Flags & (uint)ParcelFlags.RestrictPushObject) == (uint)ParcelFlags.RestrictPushObject)
{
// Need provisions for Group Owned here
if (m_host.OwnerID == targetlandObj.LandData.OwnerID ||
targetlandObj.LandData.IsGroupOwned ||
m_host.OwnerID == targetID)
{
pushAllowed = true;
}
//ParcelFlags.RestrictPushObject
//pushAllowed = true;
}
else
{
// Parcel isn't push restricted
pushAllowed = true;
}
}
}
}
if (pushAllowed)
{
float distance = (PusheePos - m_host.AbsolutePosition).Length();
float distance_term = distance * distance * distance; // Script Energy
// use total object mass and not part
float pusher_mass = m_host.ParentGroup.GetMass();
float PUSH_ATTENUATION_DISTANCE = 17f;
float PUSH_ATTENUATION_SCALE = 5f;
float distance_attenuation = 1f;
if (distance > PUSH_ATTENUATION_DISTANCE)
{
float normalized_units = 1f + (distance - PUSH_ATTENUATION_DISTANCE) / PUSH_ATTENUATION_SCALE;
distance_attenuation = 1f / normalized_units;
}
Vector3 applied_linear_impulse = impulse;
{
float impulse_length = applied_linear_impulse.Length();
float desired_energy = impulse_length * pusher_mass;
if (desired_energy > 0f)
desired_energy += distance_term;
float scaling_factor = 1f;
scaling_factor *= distance_attenuation;
applied_linear_impulse *= scaling_factor;
}
if (pusheeIsAvatar)
{
if (pusheeav != null)
{
PhysicsActor pa = pusheeav.PhysicsActor;
if (pa != null)
{
if (local != 0)
{
// applied_linear_impulse *= m_host.GetWorldRotation();
applied_linear_impulse *= pusheeav.GetWorldRotation();
}
pa.AddForce(applied_linear_impulse, true);
}
}
}
else
{
if (pusheeob != null)
{
if (pusheeob.PhysActor != null)
{
pusheeob.ApplyImpulse(applied_linear_impulse, local != 0);
}
}
}
}
}
public void llPassCollisions(int pass)
{
if (pass == 0)
{
m_host.PassCollisions = false;
}
else
{
m_host.PassCollisions = true;
}
}
public LSL_String llGetScriptName()
{
return m_item.Name != null ? m_item.Name : String.Empty;
}
public LSL_Integer llGetLinkNumberOfSides(int link)
{
SceneObjectPart linkedPart;
if (link == ScriptBaseClass.LINK_ROOT)
linkedPart = m_host.ParentGroup.RootPart;
else if (link == ScriptBaseClass.LINK_THIS)
linkedPart = m_host;
else
linkedPart = m_host.ParentGroup.GetLinkNumPart(link);
return GetNumberOfSides(linkedPart);
}
public LSL_Integer llGetNumberOfSides()
{
return m_host.GetNumberOfSides();
}
protected int GetNumberOfSides(SceneObjectPart part)
{
return part.GetNumberOfSides();
}
/* The new / changed functions were tested with the following LSL script:
default
{
state_entry()
{
rotation rot = llEuler2Rot(<0,70,0> * DEG_TO_RAD);
llOwnerSay("to get here, we rotate over: "+ (string) llRot2Axis(rot));
llOwnerSay("and we rotate for: "+ (llRot2Angle(rot) * RAD_TO_DEG));
// convert back and forth between quaternion <-> vector and angle
rotation newrot = llAxisAngle2Rot(llRot2Axis(rot),llRot2Angle(rot));
llOwnerSay("Old rotation was: "+(string) rot);
llOwnerSay("re-converted rotation is: "+(string) newrot);
llSetRot(rot); // to check the parameters in the prim
}
}
*/
// Xantor 29/apr/2008
// Returns rotation described by rotating angle radians about axis.
// q = cos(a/2) + i (x * sin(a/2)) + j (y * sin(a/2)) + k (z * sin(a/2))
public LSL_Rotation llAxisAngle2Rot(LSL_Vector axis, double angle)
{
double x, y, z, s, t;
s = Math.Cos(angle * 0.5);
t = Math.Sin(angle * 0.5); // temp value to avoid 2 more sin() calcs
axis = LSL_Vector.Norm(axis);
x = axis.x * t;
y = axis.y * t;
z = axis.z * t;
return new LSL_Rotation(x,y,z,s);
}
///
/// Returns the axis of rotation for a quaternion
///
///
///
public LSL_Vector llRot2Axis(LSL_Rotation rot)
{
rot.Normalize();
double s = Math.Sqrt(1 - rot.s * rot.s);
if (s < 1e-8)
return new LSL_Vector(0, 0, 0);
double invS = 1.0 / s;
if (rot.s < 0)
invS = -invS;
return new LSL_Vector(rot.x * invS, rot.y * invS, rot.z * invS);
}
// Returns the angle of a quaternion (see llRot2Axis for the axis)
public LSL_Float llRot2Angle(LSL_Rotation rot)
{
rot.Normalize();
double angle = 2 * Math.Acos(rot.s);
if (angle > Math.PI)
angle = 2 * Math.PI - angle;
return angle;
}
public LSL_Float llAcos(double val)
{
return (double)Math.Acos(val);
}
public LSL_Float llAsin(double val)
{
return (double)Math.Asin(val);
}
// jcochran 5/jan/2012
public LSL_Float llAngleBetween(LSL_Rotation a, LSL_Rotation b)
{
double aa = (a.x * a.x + a.y * a.y + a.z * a.z + a.s * a.s);
double bb = (b.x * b.x + b.y * b.y + b.z * b.z + b.s * b.s);
double aa_bb = aa * bb;
if (aa_bb == 0) return 0.0;
double ab = (a.x * b.x + a.y * b.y + a.z * b.z + a.s * b.s);
double quotient = (ab * ab) / aa_bb;
if (quotient >= 1.0) return 0.0;
return Math.Acos(2 * quotient - 1);
}
public LSL_String llGetInventoryKey(string name)
{
TaskInventoryItem item = m_host.Inventory.GetInventoryItem(name);
if (item == null)
return UUID.Zero.ToString();
if ((item.CurrentPermissions
& (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify))
== (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify))
{
return item.AssetID.ToString();
}
return UUID.Zero.ToString();
}
public void llAllowInventoryDrop(int add)
{
if (add != 0)
m_host.ParentGroup.RootPart.AllowedDrop = true;
else
m_host.ParentGroup.RootPart.AllowedDrop = false;
// Update the object flags
m_host.ParentGroup.RootPart.aggregateScriptEvents();
}
public LSL_Vector llGetSunDirection()
{
LSL_Vector SunDoubleVector3;
Vector3 SunFloatVector3;
// sunPosition estate setting is set in OpenSim.Region.CoreModules.SunModule
// have to convert from Vector3 (float) to LSL_Vector (double)
SunFloatVector3 = World.RegionInfo.RegionSettings.SunVector;
SunDoubleVector3.x = (double)SunFloatVector3.X;
SunDoubleVector3.y = (double)SunFloatVector3.Y;
SunDoubleVector3.z = (double)SunFloatVector3.Z;
return SunDoubleVector3;
}
public LSL_Vector llGetTextureOffset(int face)
{
return GetTextureOffset(m_host, face);
}
protected LSL_Vector GetTextureOffset(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
LSL_Vector offset = new LSL_Vector();
if (face == ScriptBaseClass.ALL_SIDES)
{
face = 0;
}
if (face >= 0 && face < GetNumberOfSides(part))
{
offset.x = tex.GetFace((uint)face).OffsetU;
offset.y = tex.GetFace((uint)face).OffsetV;
offset.z = 0.0;
return offset;
}
else
{
return offset;
}
}
public LSL_Vector llGetTextureScale(int side)
{
Primitive.TextureEntry tex = m_host.Shape.Textures;
LSL_Vector scale;
if (side == -1)
{
side = 0;
}
scale.x = tex.GetFace((uint)side).RepeatU;
scale.y = tex.GetFace((uint)side).RepeatV;
scale.z = 0.0;
return scale;
}
public LSL_Float llGetTextureRot(int face)
{
return GetTextureRot(m_host, face);
}
protected LSL_Float GetTextureRot(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
if (face == -1)
{
face = 0;
}
if (face >= 0 && face < GetNumberOfSides(part))
{
return tex.GetFace((uint)face).Rotation;
}
else
{
return 0.0;
}
}
public LSL_Integer llSubStringIndex(string source, string pattern)
{
return source.IndexOf(pattern);
}
public LSL_String llGetOwnerKey(string id)
{
UUID key = new UUID();
if (UUID.TryParse(id, out key))
{
try
{
SceneObjectPart obj = World.GetSceneObjectPart(key);
if (obj == null)
return id; // the key is for an agent so just return the key
else
return obj.OwnerID.ToString();
}
catch (KeyNotFoundException)
{
return id; // The Object/Agent not in the region so just return the key
}
}
else
{
return UUID.Zero.ToString();
}
}
public LSL_Vector llGetCenterOfMass()
{
return new LSL_Vector(m_host.GetCenterOfMass());
}
public LSL_List llListSort(LSL_List src, int stride, int ascending)
{
if (stride <= 0)
{
stride = 1;
}
return src.Sort(stride, ascending);
}
public LSL_Integer llGetListLength(LSL_List src)
{
return src.Length;
}
public LSL_Integer llList2Integer(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return 0;
object item = src.Data[index];
// Vectors & Rotations always return zero in SL, but
// keys don't always return zero, it seems to be a bit complex.
if (item is LSL_Vector || item is LSL_Rotation)
return 0;
try
{
if (item is LSL_Integer)
return (LSL_Integer)item;
else if (item is LSL_Float)
return Convert.ToInt32(((LSL_Float)item).value);;
return new LSL_Integer(item.ToString());
}
catch (FormatException)
{
return 0;
}
}
public LSL_Float llList2Float(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return 0;
object item = src.Data[index];
// Vectors & Rotations always return zero in SL
if(item is LSL_Vector || item is LSL_Rotation)
return 0;
// valid keys seem to get parsed as integers then converted to floats
if (item is LSL_Key)
{
UUID uuidt;
string s = item.ToString();
if(UUID.TryParse(s, out uuidt))
return Convert.ToDouble(new LSL_Integer(s).value);
// we can't do this because a string is also a LSL_Key for now :(
// else
// return 0;
}
try
{
if (item is LSL_Integer)
return Convert.ToDouble(((LSL_Integer)item).value);
else if (item is LSL_Float)
return Convert.ToDouble(((LSL_Float)item).value);
else if (item is LSL_String)
{
string str = ((LSL_String)item).m_string;
Match m = Regex.Match(str, "^\\s*(-?\\+?[,0-9]+\\.?[0-9]*)");
if (m != Match.Empty)
{
str = m.Value;
double d = 0.0;
if (!Double.TryParse(str, out d))
return 0.0;
return d;
}
return 0.0;
}
return Convert.ToDouble(item);
}
catch (FormatException)
{
return 0.0;
}
}
public LSL_String llList2String(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return String.Empty;
return src.Data[index].ToString();
}
public LSL_Key llList2Key(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return String.Empty;
object item = src.Data[index];
// SL spits out an empty string for types other than key & string
// At the time of patching, LSL_Key is currently LSL_String,
// so the OR check may be a little redundant, but it's being done
// for completion and should LSL_Key ever be implemented
// as it's own struct
// NOTE: 3rd case is needed because a NULL_KEY comes through as
// type 'obj' and wrongly returns ""
if (!(item is LSL_String ||
item is LSL_Key ||
item.ToString() == "00000000-0000-0000-0000-000000000000"))
{
return String.Empty;
}
return item.ToString();
}
public LSL_Vector llList2Vector(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return new LSL_Vector(0, 0, 0);
object item = src.Data[index];
if (item.GetType() == typeof(LSL_Vector))
return (LSL_Vector)item;
// SL spits always out ZERO_VECTOR for anything other than
// strings or vectors. Although keys always return ZERO_VECTOR,
// it is currently difficult to make the distinction between
// a string, a key as string and a string that by coincidence
// is a string, so we're going to leave that up to the
// LSL_Vector constructor.
if(item is LSL_Vector)
return (LSL_Vector) item;
if (item is LSL_String)
return new LSL_Vector(item.ToString());
return new LSL_Vector(0, 0, 0);
}
public LSL_Rotation llList2Rot(LSL_List src, int index)
{
if (index < 0)
index = src.Length + index;
if (index >= src.Length || index < 0)
return new LSL_Rotation(0, 0, 0, 1);
object item = src.Data[index];
// SL spits always out ZERO_ROTATION for anything other than
// strings or vectors. Although keys always return ZERO_ROTATION,
// it is currently difficult to make the distinction between
// a string, a key as string and a string that by coincidence
// is a string, so we're going to leave that up to the
// LSL_Rotation constructor.
if (item.GetType() == typeof(LSL_Rotation))
return (LSL_Rotation)item;
if (item is LSL_String)
return new LSL_Rotation(src.Data[index].ToString());
return new LSL_Rotation(0, 0, 0, 1);
}
public LSL_List llList2List(LSL_List src, int start, int end)
{
return src.GetSublist(start, end);
}
public LSL_List llDeleteSubList(LSL_List src, int start, int end)
{
return src.DeleteSublist(start, end);
}
public LSL_Integer llGetListEntryType(LSL_List src, int index)
{
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length)
{
return 0;
}
if (src.Data[index] is LSL_Integer || src.Data[index] is Int32)
return 1;
if (src.Data[index] is LSL_Float || src.Data[index] is Single || src.Data[index] is Double)
return 2;
if (src.Data[index] is LSL_String || src.Data[index] is String)
{
UUID tuuid;
if (UUID.TryParse(src.Data[index].ToString(), out tuuid))
{
return 4;
}
else
{
return 3;
}
}
if (src.Data[index] is LSL_Vector)
return 5;
if (src.Data[index] is LSL_Rotation)
return 6;
if (src.Data[index] is LSL_List)
return 7;
return 0;
}
///
/// Process the supplied list and return the
/// content of the list formatted as a comma
/// separated list. There is a space after
/// each comma.
///
public LSL_String llList2CSV(LSL_List src)
{
return string.Join(", ",
(new List