/*
* 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.Diagnostics; //for [DebuggerNonUserCode]
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.Packets;
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.Physics.Manager;
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 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;
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);
protected IScriptEngine m_ScriptEngine;
protected SceneObjectPart m_host;
protected uint m_localID;
protected UUID m_itemID;
protected bool throwErrorOnNotImplemented = true;
protected AsyncCommandManager AsyncCommands = null;
protected float m_ScriptDelayFactor = 1.0f;
protected float m_ScriptDistanceFactor = 1.0f;
protected float m_MinTimerInterval = 0.5f;
protected DateTime m_timer = DateTime.Now;
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 Timer m_ShoutSayTimer;
protected int m_SayShoutCount = 0;
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, uint localID, UUID itemID)
{
m_ShoutSayTimer = new Timer(1000);
m_ShoutSayTimer.Elapsed += SayShoutTimerElapsed;
m_ShoutSayTimer.AutoReset = true;
m_ShoutSayTimer.Start();
m_ScriptEngine = ScriptEngine;
m_host = host;
m_localID = localID;
m_itemID = itemID;
m_debuggerSafe = m_ScriptEngine.Config.GetBoolean("DebuggerSafe", false);
m_ScriptDelayFactor =
m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f);
m_ScriptDistanceFactor =
m_ScriptEngine.Config.GetFloat("ScriptDistanceLimitFactor", 1.0f);
m_MinTimerInterval =
m_ScriptEngine.Config.GetFloat("MinTimerInterval", 0.5f);
m_automaticLinkPermission =
m_ScriptEngine.Config.GetBoolean("AutomaticLinkPermission", false);
m_notecardLineReadCharsMax =
m_ScriptEngine.Config.GetInt("NotecardLineReadCharsMax", 255);
if (m_notecardLineReadCharsMax > 65535)
m_notecardLineReadCharsMax = 65535;
m_TransferModule =
m_ScriptEngine.World.RequestModuleInterface();
m_UrlModule = m_ScriptEngine.World.RequestModuleInterface();
if (m_UrlModule != null)
{
m_ScriptEngine.OnScriptRemoved += m_UrlModule.ScriptRemoved;
m_ScriptEngine.OnObjectRemoved += m_UrlModule.ObjectRemoved;
}
AsyncCommands = new AsyncCommandManager(ScriptEngine);
}
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 virtual void ScriptSleep(int delay)
{
delay = (int)((float)delay * m_ScriptDelayFactor);
if (delay == 0)
return;
System.Threading.Thread.Sleep(delay);
}
public Scene World
{
get { return m_ScriptEngine.World; }
}
[DebuggerNonUserCode]
public void state(string newState)
{
m_ScriptEngine.SetState(m_itemID, newState);
}
///
/// Reset the named script. The script must be present
/// in the same prim.
///
[DebuggerNonUserCode]
public void llResetScript()
{
m_host.AddScriptLPS(1);
m_ScriptEngine.ApiResetScript(m_itemID);
}
public void llResetOtherScript(string name)
{
UUID item;
m_host.AddScriptLPS(1);
if ((item = ScriptByName(name)) != UUID.Zero)
m_ScriptEngine.ResetScript(item);
else
ShoutError("llResetOtherScript: script "+name+" not found");
}
public LSL_Integer llGetScriptState(string name)
{
UUID item;
m_host.AddScriptLPS(1);
if ((item = ScriptByName(name)) != UUID.Zero)
{
return m_ScriptEngine.GetScriptState(item) ?1:0;
}
ShoutError("llGetScriptState: script "+name+" not found");
// 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;
m_host.AddScriptLPS(1);
// These functions are supposed to be robust,
// so get the state one step at a time.
if ((item = ScriptByName(name)) != UUID.Zero)
{
m_ScriptEngine.SetScriptState(item, run == 0 ? false : true);
}
else
{
ShoutError("llSetScriptState: script "+name+" not found");
}
}
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();
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;
}
}
}
}
public List GetLinkParts(int linkType)
{
List ret = new List();
if (m_host == null || m_host.ParentGroup == null || m_host.ParentGroup.IsDeleted)
return ret;
ret.Add(m_host);
switch (linkType)
{
case ScriptBaseClass.LINK_SET:
return new List(m_host.ParentGroup.Parts);
case ScriptBaseClass.LINK_ROOT:
ret = new List();
ret.Add(m_host.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_ALL_OTHERS:
ret = new List(m_host.ParentGroup.Parts);
if (ret.Contains(m_host))
ret.Remove(m_host);
return ret;
case ScriptBaseClass.LINK_ALL_CHILDREN:
ret = new List(m_host.ParentGroup.Parts);
if (ret.Contains(m_host.ParentGroup.RootPart))
ret.Remove(m_host.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_THIS:
return ret;
default:
if (linkType < 0)
return new List();
SceneObjectPart target = m_host.ParentGroup.GetLinkNumPart(linkType);
if (target == null)
return new List();
ret = new List();
ret.Add(target);
return ret;
}
}
protected UUID InventorySelf()
{
UUID invItemID = new UUID();
bool unlock = false;
if (!m_host.TaskInventory.IsReadLockedByMe())
{
m_host.TaskInventory.LockItemsForRead(true);
unlock = true;
}
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Type == 10 && inv.Value.ItemID == m_itemID)
{
invItemID = inv.Key;
break;
}
}
if (unlock)
{
m_host.TaskInventory.LockItemsForRead(false);
}
return invItemID;
}
protected UUID InventoryKey(string name, int type)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
m_host.TaskInventory.LockItemsForRead(false);
if (inv.Value.Type != type)
{
return UUID.Zero;
}
return inv.Value.AssetID;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero;
}
protected UUID InventoryKey(string name)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
m_host.TaskInventory.LockItemsForRead(false);
return inv.Value.AssetID;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero;
}
///
/// accepts a valid UUID, -or- a name of an inventory item.
/// Returns a valid UUID or UUID.Zero if key invalid and item not found
/// in prim inventory.
///
///
///
protected UUID KeyOrName(string k)
{
UUID key = UUID.Zero;
// if we can parse the string as a key, use it.
if (UUID.TryParse(k, out key))
{
return key;
}
// else try to locate the name in inventory of object. found returns key,
// not found returns UUID.Zero which will translate to the default particle texture
else
{
return InventoryKey(k);
}
}
// convert a LSL_Rotation to a Quaternion
public static Quaternion Rot2Quaternion(LSL_Rotation r)
{
Quaternion q = new Quaternion((float)r.x, (float)r.y, (float)r.z, (float)r.s);
q.Normalize();
return q;
}
//These are the implementations of the various ll-functions used by the LSL scripts.
public LSL_Float llSin(double f)
{
m_host.AddScriptLPS(1);
return (double)Math.Sin(f);
}
public LSL_Float llCos(double f)
{
m_host.AddScriptLPS(1);
return (double)Math.Cos(f);
}
public LSL_Float llTan(double f)
{
m_host.AddScriptLPS(1);
return (double)Math.Tan(f);
}
public LSL_Float llAtan2(double x, double y)
{
m_host.AddScriptLPS(1);
return (double)Math.Atan2(x, y);
}
public LSL_Float llSqrt(double f)
{
m_host.AddScriptLPS(1);
return (double)Math.Sqrt(f);
}
public LSL_Float llPow(double fbase, double fexponent)
{
m_host.AddScriptLPS(1);
return (double)Math.Pow(fbase, fexponent);
}
public LSL_Integer llAbs(int i)
{
// changed to replicate LSL behaviour whereby minimum int value is returned untouched.
m_host.AddScriptLPS(1);
if (i == Int32.MinValue)
return i;
else
return (int)Math.Abs(i);
}
public LSL_Float llFabs(double f)
{
m_host.AddScriptLPS(1);
return (double)Math.Abs(f);
}
public LSL_Float llFrand(double mag)
{
m_host.AddScriptLPS(1);
lock (Util.RandomClass)
{
return Util.RandomClass.NextDouble() * mag;
}
}
public LSL_Integer llFloor(double f)
{
m_host.AddScriptLPS(1);
return (int)Math.Floor(f);
}
public LSL_Integer llCeil(double f)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
return LSL_Vector.Mag(v);
}
public LSL_Vector llVecNorm(LSL_Vector v)
{
m_host.AddScriptLPS(1);
return LSL_Vector.Norm(v);
}
public LSL_Float llVecDist(LSL_Vector a, LSL_Vector b)
{
m_host.AddScriptLPS(1);
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);
}
//Now we start getting into quaternions which means sin/cos, matrices and vectors. ckrinke
// Utility function for llRot2Euler
// normalize an angle between -PI and PI (-180 to +180 degrees)
protected double NormalizeAngle(double angle)
{
if (angle > -Math.PI && angle < Math.PI)
return angle;
int numPis = (int)(Math.PI / angle);
double remainder = angle - Math.PI * numPis;
if (numPis % 2 == 1)
return Math.PI - angle;
return remainder;
}
public LSL_Vector llRot2Euler(LSL_Rotation q1)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
double x,y,z,s;
double c1 = Math.Cos(v.x * 0.5);
double c2 = Math.Cos(v.y * 0.5);
double c3 = Math.Cos(v.z * 0.5);
double s1 = Math.Sin(v.x * 0.5);
double s2 = Math.Sin(v.y * 0.5);
double s3 = Math.Sin(v.z * 0.5);
x = s1 * c2 * c3 + c1 * s2 * s3;
y = c1 * s2 * c3 - s1 * c2 * s3;
z = s1 * s2 * c3 + c1 * c2 * s3;
s = c1 * c2 * c3 - s1 * s2 * s3;
return new LSL_Rotation(x, y, z, s);
}
public LSL_Rotation llAxes2Rot(LSL_Vector fwd, LSL_Vector left, LSL_Vector up)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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
m_host.AddScriptLPS(1);
/* 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)
{
m_host.AddScriptLPS(1);
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);
}
public void llSay(int channelID, string text)
{
m_host.AddScriptLPS(1);
if (channelID == 0)
m_SayShoutCount++;
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)
{
m_host.AddScriptLPS(1);
if (channelID == 0)
m_SayShoutCount++;
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)
{
LSLError("Cannot use llRegionSay() on channel 0");
return;
}
if (text.Length > 1023)
text = text.Substring(0, 1023);
m_host.AddScriptLPS(1);
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)
{
string error = String.Empty;
if (msg.Length > 1023)
msg = msg.Substring(0, 1023);
m_host.AddScriptLPS(1);
UUID TargetID;
UUID.TryParse(target, out TargetID);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
if (!wComm.DeliverMessageTo(TargetID, channel, m_host.AbsolutePosition, m_host.Name, m_host.UUID, msg, out error))
LSLError(error);
}
public LSL_Integer llListen(int channelID, string name, string ID, string msg)
{
m_host.AddScriptLPS(1);
UUID keyID;
UUID.TryParse(ID, out keyID);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
return wComm.Listen(m_localID, m_itemID, m_host.UUID, channelID, name, keyID, msg);
else
return -1;
}
public void llListenControl(int number, int active)
{
m_host.AddScriptLPS(1);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.ListenControl(m_itemID, number, active);
}
public void llListenRemove(int number)
{
m_host.AddScriptLPS(1);
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
if (wComm != null)
wComm.ListenRemove(m_itemID, number);
}
public void llSensor(string name, string id, int type, double range, double arc)
{
m_host.AddScriptLPS(1);
UUID keyID = UUID.Zero;
UUID.TryParse(id, out keyID);
AsyncCommands.SensorRepeatPlugin.SenseOnce(m_localID, m_itemID, name, keyID, type, range, arc, m_host);
}
public void llSensorRepeat(string name, string id, int type, double range, double arc, double rate)
{
m_host.AddScriptLPS(1);
UUID keyID = UUID.Zero;
UUID.TryParse(id, out keyID);
AsyncCommands.SensorRepeatPlugin.SetSenseRepeatEvent(m_localID, m_itemID, name, keyID, type, range, arc, rate, m_host);
}
public void llSensorRemove()
{
m_host.AddScriptLPS(1);
AsyncCommands.SensorRepeatPlugin.UnSetSenseRepeaterEvents(m_localID, m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Name;
}
public LSL_String llDetectedKey(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Key.ToString();
}
public LSL_String llDetectedOwner(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return String.Empty;
return detectedParams.Owner.ToString();
}
public LSL_Integer llDetectedType(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return 0;
return new LSL_Integer(detectedParams.Type);
}
public LSL_Vector llDetectedPos(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.Position;
}
public LSL_Vector llDetectedVel(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return new LSL_Vector();
return detectedParams.Velocity;
}
public LSL_Vector llDetectedGrab(int number)
{
m_host.AddScriptLPS(1);
DetectParams parms = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (parms == null)
return new LSL_Vector(0, 0, 0);
return parms.OffsetPos;
}
public LSL_Rotation llDetectedRot(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, number);
if (detectedParams == null)
return new LSL_Rotation();
return detectedParams.Rotation;
}
public LSL_Integer llDetectedGroup(int number)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams parms = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_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)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, index);
if (detectedParams == null)
return new LSL_Vector(-1.0, -1.0, 0.0);
return detectedParams.TouchUV;
}
[DebuggerNonUserCode]
public virtual void llDie()
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsAttachment) throw new SelfDeleteException();
}
public LSL_Float llGround(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
Vector3 pos = m_host.GetWorldPosition() + new Vector3((float)offset.x,
(float)offset.y,
(float)offset.z);
//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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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;
m_host.AddScriptLPS(1);
int statusrotationaxis = 0;
if ((status & ScriptBaseClass.STATUS_PHYSICS) == ScriptBaseClass.STATUS_PHYSICS)
{
if (value != 0)
{
SceneObjectGroup group = m_host.ParentGroup;
bool allow = true;
foreach (SceneObjectPart part in group.Parts)
{
if (part.Scale.X > World.m_maxPhys || part.Scale.Y > World.m_maxPhys || part.Scale.Z > World.m_maxPhys)
{
allow = false;
break;
}
}
if (!allow)
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)
{
if (value != 0)
m_host.SetBlockGrab(true);
else
m_host.SetBlockGrab(false);
}
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);
}
}
public LSL_Integer llGetStatus(int status)
{
m_host.AddScriptLPS(1);
// m_log.Debug(m_host.ToString() + " status is " + m_host.GetEffectiveObjectFlags().ToString());
switch (status)
{
case ScriptBaseClass.STATUS_PHYSICS:
if ((m_host.GetEffectiveObjectFlags() & (uint)PrimFlags.Physics) == (uint)PrimFlags.Physics)
{
return 1;
}
return 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:
if (m_host.GetBlockGrab())
return 1;
else
return 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) == 2)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_ROTATE_Y:
if (m_host.GetAxisRotation(4) == 4)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_ROTATE_Z:
if (m_host.GetAxisRotation(8) == 8)
return 1;
else
return 0;
case ScriptBaseClass.STATUS_SANDBOX:
if (m_host.GetStatusSandbox())
return 1;
else
return 0;
}
return 0;
}
public void llSetScale(LSL_Vector scale)
{
m_host.AddScriptLPS(1);
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;
if (scale.x < 0.01)
scale.x = 0.01;
if (scale.y < 0.01)
scale.y = 0.01;
if (scale.z < 0.01)
scale.z = 0.01;
if (part.ParentGroup.RootPart.PhysActor != null && part.ParentGroup.RootPart.PhysActor.IsPhysical)
{
if (scale.x > World.m_maxPhys)
scale.x = World.m_maxPhys;
if (scale.y > World.m_maxPhys)
scale.y = World.m_maxPhys;
if (scale.z > World.m_maxPhys)
scale.z = World.m_maxPhys;
}
if (scale.x > World.m_maxNonphys)
scale.x = World.m_maxNonphys;
if (scale.y > World.m_maxNonphys)
scale.y = World.m_maxNonphys;
if (scale.z > World.m_maxNonphys)
scale.z = World.m_maxNonphys;
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()
{
m_host.AddScriptLPS(1);
return new LSL_Vector(m_host.Scale.X, m_host.Scale.Y, m_host.Scale.Z);
}
public void llSetClickAction(int action)
{
m_host.AddScriptLPS(1);
m_host.ClickAction = (byte)action;
m_host.ParentGroup.HasGroupChanged = true;
m_host.ScheduleFullUpdate();
return;
}
public void llSetColor(LSL_Vector color, int face)
{
m_host.AddScriptLPS(1);
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;
Color4 texcolor;
if (face >= 0 && face < GetNumberOfSides(part))
{
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.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); 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.UpdateTexture(tex);
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
face = SceneObjectPart.ALL_SIDES;
m_host.SetFaceColor(new Vector3((float)color.x, (float)color.y, (float)color.z), face);
}
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;
if (face >= 0 && face < GetNumberOfSides(part))
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].TexMapType = textype;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].TexMapType = textype;
}
tex.DefaultTexture.TexMapType = textype;
}
part.UpdateTexture(tex);
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;
if (face >= 0 && face < GetNumberOfSides(part))
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Glow = glow;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Glow = glow;
}
tex.DefaultTexture.Glow = glow;
}
part.UpdateTexture(tex);
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;
}
Primitive.TextureEntry tex = part.Shape.Textures;
if (face >= 0 && face < GetNumberOfSides(part))
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Shiny = sval;
tex.FaceTextures[face].Bump = bump;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); 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.UpdateTexture(tex);
return;
}
}
public void SetFullBright(SceneObjectPart part, int face, bool bright)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
Primitive.TextureEntry tex = part.Shape.Textures;
if (face >= 0 && face < GetNumberOfSides(part))
{
tex.CreateFace((uint) face);
tex.FaceTextures[face].Fullbright = bright;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Fullbright = bright;
}
}
tex.DefaultTexture.Fullbright = bright;
part.UpdateTexture(tex);
return;
}
}
public LSL_Float llGetAlpha(int face)
{
m_host.AddScriptLPS(1);
return GetAlpha(m_host, face);
}
protected LSL_Float GetAlpha(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
if (face == ScriptBaseClass.ALL_SIDES)
{
int i;
double sum = 0.0;
for (i = 0 ; i < GetNumberOfSides(part); i++)
sum += (double)tex.GetFace((uint)i).RGBA.A;
return sum;
}
if (face >= 0 && face < GetNumberOfSides(part))
{
return (double)tex.GetFace((uint)face).RGBA.A;
}
return 0.0;
}
public void llSetAlpha(double alpha, int face)
{
m_host.AddScriptLPS(1);
SetAlpha(m_host, alpha, face);
}
public void llSetLinkAlpha(int linknumber, double alpha, int face)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
SetAlpha(part, alpha, face);
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
}
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;
Color4 texcolor;
if (face >= 0 && face < GetNumberOfSides(part))
{
texcolor = tex.CreateFace((uint)face).RGBA;
texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f);
tex.FaceTextures[face].RGBA = texcolor;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < GetNumberOfSides(part); 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.UpdateTexture(tex);
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 = 0x80;
}
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 = intensity;
part.Shape.LightRadius = radius;
part.Shape.LightFalloff = falloff;
}
else
{
part.Shape.LightEntry = false;
}
part.ParentGroup.HasGroupChanged = true;
part.ScheduleFullUpdate();
}
public LSL_Vector llGetColor(int face)
{
m_host.AddScriptLPS(1);
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();
if (face == ScriptBaseClass.ALL_SIDES)
{
int i;
for (i = 0 ; i < GetNumberOfSides(part); i++)
{
texcolor = tex.GetFace((uint)i).RGBA;
rgb.x += texcolor.R;
rgb.y += texcolor.G;
rgb.z += texcolor.B;
}
rgb.x /= (float)GetNumberOfSides(part);
rgb.y /= (float)GetNumberOfSides(part);
rgb.z /= (float)GetNumberOfSides(part);
return rgb;
}
if (face >= 0 && face < GetNumberOfSides(part))
{
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)
{
m_host.AddScriptLPS(1);
SetTexture(m_host, texture, face);
ScriptSleep(200);
}
public void llSetLinkTexture(int linknumber, string texture, int face)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
SetTexture(part, texture, face);
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
ScriptSleep(200);
}
protected void SetTexture(SceneObjectPart part, string texture, int face)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
UUID textureID = new UUID();
textureID = InventoryKey(texture, (int)AssetType.Texture);
if (textureID == UUID.Zero)
{
if (!UUID.TryParse(texture, out textureID))
return;
}
Primitive.TextureEntry tex = part.Shape.Textures;
if (face >= 0 && face < GetNumberOfSides(part))
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.TextureID = textureID;
tex.FaceTextures[face] = texface;
part.UpdateTexture(tex);
return;
}
else if (face == ScriptBaseClass.ALL_SIDES)
{
for (uint i = 0; i < GetNumberOfSides(part); i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].TextureID = textureID;
}
}
tex.DefaultTexture.TextureID = textureID;
part.UpdateTexture(tex);
return;
}
}
public void llScaleTexture(double u, double v, int face)
{
m_host.AddScriptLPS(1);
ScaleTexture(m_host, u, v, face);
ScriptSleep(200);
}
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;
if (face >= 0 && face < GetNumberOfSides(part))
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.RepeatU = (float)u;
texface.RepeatV = (float)v;
tex.FaceTextures[face] = texface;
part.UpdateTexture(tex);
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < GetNumberOfSides(part); 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.UpdateTexture(tex);
return;
}
}
public void llOffsetTexture(double u, double v, int face)
{
m_host.AddScriptLPS(1);
OffsetTexture(m_host, u, v, face);
ScriptSleep(200);
}
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;
if (face >= 0 && face < GetNumberOfSides(part))
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.OffsetU = (float)u;
texface.OffsetV = (float)v;
tex.FaceTextures[face] = texface;
part.UpdateTexture(tex);
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < GetNumberOfSides(part); 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.UpdateTexture(tex);
return;
}
}
public void llRotateTexture(double rotation, int face)
{
m_host.AddScriptLPS(1);
RotateTexture(m_host, rotation, face);
ScriptSleep(200);
}
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;
if (face >= 0 && face < GetNumberOfSides(part))
{
Primitive.TextureEntryFace texface = tex.CreateFace((uint)face);
texface.Rotation = (float)rotation;
tex.FaceTextures[face] = texface;
part.UpdateTexture(tex);
return;
}
if (face == ScriptBaseClass.ALL_SIDES)
{
for (int i = 0; i < GetNumberOfSides(part); i++)
{
if (tex.FaceTextures[i] != null)
{
tex.FaceTextures[i].Rotation = (float)rotation;
}
}
tex.DefaultTexture.Rotation = (float)rotation;
part.UpdateTexture(tex);
return;
}
}
public LSL_String llGetTexture(int face)
{
m_host.AddScriptLPS(1);
return GetTexture(m_host, face);
}
protected LSL_String GetTexture(SceneObjectPart part, int face)
{
Primitive.TextureEntry tex = part.Shape.Textures;
if (face == ScriptBaseClass.ALL_SIDES)
{
face = 0;
}
if (face >= 0 && face < GetNumberOfSides(part))
{
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)
{
m_host.AddScriptLPS(1);
SetPos(m_host, pos);
ScriptSleep(200);
}
// 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)
{
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;
}
LSL_Vector real_vec = SetPosAdjust(fromPos, targetPos);
return real_vec;
}
protected void SetPos(SceneObjectPart part, LSL_Vector targetPos)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
LSL_Vector currentPos = GetPartLocalPos(part);
LSL_Vector toPos = GetSetPosTarget(part, targetPos, currentPos);
if (part.ParentGroup.RootPart == part)
{
SceneObjectGroup parent = part.ParentGroup;
parent.UpdateGroupPosition(new Vector3((float)toPos.x, (float)toPos.y, (float)toPos.z));
}
else
{
part.OffsetPosition = new Vector3((float)toPos.x, (float)toPos.y, (float)toPos.z);
SceneObjectGroup parent = part.ParentGroup;
parent.HasGroupChanged = true;
parent.ScheduleGroupForTerseUpdate();
}
}
public LSL_Vector llGetPos()
{
m_host.AddScriptLPS(1);
Vector3 pos = m_host.GetWorldPosition();
return new LSL_Vector(pos.X, pos.Y, pos.Z);
}
public LSL_Vector llGetLocalPos()
{
m_host.AddScriptLPS(1);
return GetPartLocalPos(m_host);
}
protected LSL_Vector GetPartLocalPos(SceneObjectPart part)
{
m_host.AddScriptLPS(1);
if (part.ParentID == 0)
{
return new LSL_Vector(part.AbsolutePosition.X,
part.AbsolutePosition.Y,
part.AbsolutePosition.Z);
}
else
{
if (m_host.IsRoot)
{
return new LSL_Vector(m_host.AttachedPos.X,
m_host.AttachedPos.Y,
m_host.AttachedPos.Z);
}
else
{
return new LSL_Vector(part.OffsetPosition.X,
part.OffsetPosition.Y,
part.OffsetPosition.Z);
}
}
}
public void llSetRot(LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
// try to let this work as in SL...
if (m_host.LinkNum < 2)
{
// Special case: If we are root, rotate complete SOG to new
// rotation.
// We are root if the link number is 0 (single prim) or 1
// (root prim). ParentID may be nonzero in attachments and
// using it would cause attachments and HUDs to rotate
// to the wrong positions.
SetRot(m_host, Rot2Quaternion(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 * Rot2Quaternion(rot));
}
}
ScriptSleep(200);
}
public void llSetLocalRot(LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
SetRot(m_host, Rot2Quaternion(rot));
ScriptSleep(200);
}
protected void SetRot(SceneObjectPart part, Quaternion rot)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
part.UpdateRotation(rot);
// Update rotation does not move the object in the physics scene if it's a linkset.
//KF: Do NOT use this next line if using ODE physics engine. This need a switch based on .ini Phys Engine type
// part.ParentGroup.AbsolutePosition = part.ParentGroup.AbsolutePosition;
// So, after thinking about this for a bit, the issue with the part.ParentGroup.AbsolutePosition = part.ParentGroup.AbsolutePosition line
// is it isn't compatible with vehicles because it causes the vehicle body to have to be broken down and rebuilt
// It's perfectly okay when the object is not an active physical body though.
// So, part.ParentGroup.ResetChildPrimPhysicsPositions(); does the thing that Kitto is warning against
// but only if the object is not physial and active. This is important for rotating doors.
// without the absoluteposition = absoluteposition happening, the doors do not move in the physics
// scene
if (part.PhysActor != null && !part.PhysActor.IsPhysical)
{
part.ParentGroup.ResetChildPrimPhysicsPositions();
}
}
///
/// 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();
}
m_host.AddScriptLPS(1);
Quaternion q = m_host.GetWorldRotation();
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.Rotation; // 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.X, q.Y, q.Z, q.W);
}
q = part.GetWorldRotation();
return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
}
public LSL_Rotation llGetLocalRot()
{
m_host.AddScriptLPS(1);
return new LSL_Rotation(m_host.RotationOffset.X, m_host.RotationOffset.Y, m_host.RotationOffset.Z, m_host.RotationOffset.W);
}
public void llSetForce(LSL_Vector force, int local)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
if (local != 0)
force *= llGetRot();
m_host.ParentGroup.RootPart.SetForce(new Vector3((float)force.x, (float)force.y, (float)force.z));
}
}
public LSL_Vector llGetForce()
{
LSL_Vector force = new LSL_Vector(0.0, 0.0, 0.0);
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
Vector3 tmpForce = m_host.ParentGroup.RootPart.GetForce();
force.x = tmpForce.X;
force.y = tmpForce.Y;
force.z = tmpForce.Z;
}
return force;
}
public LSL_Integer llTarget(LSL_Vector position, double range)
{
m_host.AddScriptLPS(1);
return m_host.ParentGroup.registerTargetWaypoint(
new Vector3((float)position.x, (float)position.y, (float)position.z), (float)range);
}
public void llTargetRemove(int number)
{
m_host.AddScriptLPS(1);
m_host.ParentGroup.unregisterTargetWaypoint(number);
}
public LSL_Integer llRotTarget(LSL_Rotation rot, double error)
{
m_host.AddScriptLPS(1);
return m_host.ParentGroup.registerRotTargetWaypoint(
new Quaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s), (float)error);
}
public void llRotTargetRemove(int number)
{
m_host.AddScriptLPS(1);
m_host.ParentGroup.unregisterRotTargetWaypoint(number);
}
public void llMoveToTarget(LSL_Vector target, double tau)
{
m_host.AddScriptLPS(1);
m_host.MoveToTarget(new Vector3((float)target.x, (float)target.y, (float)target.z), (float)tau);
}
public void llStopMoveToTarget()
{
m_host.AddScriptLPS(1);
m_host.StopMoveToTarget();
}
public void llApplyImpulse(LSL_Vector force, int local)
{
m_host.AddScriptLPS(1);
//No energy force yet
Vector3 v = new Vector3((float)force.x, (float)force.y, (float)force.z);
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.AddScriptLPS(1);
m_host.ApplyAngularImpulse(new Vector3((float)force.x, (float)force.y, (float)force.z), local != 0);
}
public void llSetTorque(LSL_Vector torque, int local)
{
m_host.AddScriptLPS(1);
m_host.SetAngularImpulse(new Vector3((float)torque.x, (float)torque.y, (float)torque.z), local != 0);
}
public LSL_Vector llGetTorque()
{
m_host.AddScriptLPS(1);
Vector3 torque = m_host.ParentGroup.GetTorque();
return new LSL_Vector(torque.X,torque.Y,torque.Z);
}
public void llSetForceAndTorque(LSL_Vector force, LSL_Vector torque, int local)
{
m_host.AddScriptLPS(1);
llSetForce(force, local);
llSetTorque(torque, local);
}
public LSL_Vector llGetVel()
{
m_host.AddScriptLPS(1);
Vector3 vel;
if (m_host.ParentGroup.IsAttachment)
{
ScenePresence avatar = m_host.ParentGroup.Scene.GetScenePresence(m_host.ParentGroup.AttachedAvatar);
vel = avatar.Velocity;
}
else
{
vel = m_host.Velocity;
}
return new LSL_Vector(vel.X, vel.Y, vel.Z);
}
public LSL_Vector llGetAccel()
{
m_host.AddScriptLPS(1);
return new LSL_Vector(m_host.Acceleration.X, m_host.Acceleration.Y, m_host.Acceleration.Z);
}
public LSL_Vector llGetOmega()
{
m_host.AddScriptLPS(1);
return new LSL_Vector(m_host.AngularVelocity.X, m_host.AngularVelocity.Y, m_host.AngularVelocity.Z);
}
public LSL_Float llGetTimeOfDay()
{
m_host.AddScriptLPS(1);
return (double)((DateTime.Now.TimeOfDay.TotalMilliseconds / 1000) % (3600 * 4));
}
public LSL_Float llGetWallclock()
{
m_host.AddScriptLPS(1);
return DateTime.Now.TimeOfDay.TotalSeconds;
}
public LSL_Float llGetTime()
{
m_host.AddScriptLPS(1);
TimeSpan ScriptTime = DateTime.Now - m_timer;
return (double)(ScriptTime.TotalMilliseconds / 1000);
}
public void llResetTime()
{
m_host.AddScriptLPS(1);
m_timer = DateTime.Now;
}
public LSL_Float llGetAndResetTime()
{
m_host.AddScriptLPS(1);
TimeSpan ScriptTime = DateTime.Now - m_timer;
m_timer = DateTime.Now;
return (double)(ScriptTime.TotalMilliseconds / 1000);
}
public void llSound(string sound, double volume, int queue, int loop)
{
m_host.AddScriptLPS(1);
// This function has been deprecated
// see http://www.lslwiki.net/lslwiki/wakka.php?wakka=llSound
Deprecated("llSound");
}
// 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)
{
m_host.AddScriptLPS(1);
// send the sound, once, to all clients in range
m_host.SendSound(KeyOrName(sound).ToString(), volume, false, 0, 0, false, false);
}
// Xantor 20080528 we should do this differently.
// 1) apply the sound to the object
// 2) schedule full update
// just sending the sound out once doesn't work so well when other avatars come in view later on
// or when the prim gets moved, changed, sat on, whatever
// see large number of mantises (mantes?)
// 20080530 Updated to remove code duplication
// 20080530 Stop sound if there is one, otherwise volume only changes don't work
public void llLoopSound(string sound, double volume)
{
m_host.AddScriptLPS(1);
if (m_host.Sound != UUID.Zero)
llStopSound();
m_host.Sound = KeyOrName(sound);
m_host.SoundGain = volume;
m_host.SoundFlags = 1; // looping
m_host.SoundRadius = 20; // Magic number, 20 seems reasonable. Make configurable?
m_host.ScheduleFullUpdate();
m_host.SendFullUpdateToAllClients();
}
public void llLoopSoundMaster(string sound, double volume)
{
m_host.AddScriptLPS(1);
m_host.ParentGroup.LoopSoundMasterPrim = m_host;
lock (m_host.ParentGroup.LoopSoundSlavePrims)
{
foreach (SceneObjectPart prim in m_host.ParentGroup.LoopSoundSlavePrims)
{
if (prim.Sound != UUID.Zero)
llStopSound();
prim.Sound = KeyOrName(sound);
prim.SoundGain = volume;
prim.SoundFlags = 1; // looping
prim.SoundRadius = 20; // Magic number, 20 seems reasonable. Make configurable?
prim.ScheduleFullUpdate();
prim.SendFullUpdateToAllClients();
}
}
if (m_host.Sound != UUID.Zero)
llStopSound();
m_host.Sound = KeyOrName(sound);
m_host.SoundGain = volume;
m_host.SoundFlags = 1; // looping
m_host.SoundRadius = 20; // Magic number, 20 seems reasonable. Make configurable?
m_host.ScheduleFullUpdate();
m_host.SendFullUpdateToAllClients();
}
public void llLoopSoundSlave(string sound, double volume)
{
m_host.AddScriptLPS(1);
lock (m_host.ParentGroup.LoopSoundSlavePrims)
{
m_host.ParentGroup.LoopSoundSlavePrims.Add(m_host);
}
}
public void llPlaySoundSlave(string sound, double volume)
{
m_host.AddScriptLPS(1);
// send the sound, once, to all clients in range
m_host.SendSound(KeyOrName(sound).ToString(), volume, false, 0, 0, true, false);
}
public void llTriggerSound(string sound, double volume)
{
m_host.AddScriptLPS(1);
// send the sound, once, to all clients in range
m_host.SendSound(KeyOrName(sound).ToString(), volume, true, 0, 0, false, false);
}
// Xantor 20080528: Clear prim data of sound instead
public void llStopSound()
{
m_host.AddScriptLPS(1);
if (m_host.ParentGroup.LoopSoundSlavePrims.Contains(m_host))
{
if (m_host.ParentGroup.LoopSoundMasterPrim == m_host)
{
foreach (SceneObjectPart part in m_host.ParentGroup.LoopSoundSlavePrims)
{
part.Sound = UUID.Zero;
part.SoundGain = 0;
part.SoundFlags = 0;
part.SoundRadius = 0;
part.ScheduleFullUpdate();
part.SendFullUpdateToAllClients();
}
m_host.ParentGroup.LoopSoundMasterPrim = null;
m_host.ParentGroup.LoopSoundSlavePrims.Clear();
}
else
{
m_host.Sound = UUID.Zero;
m_host.SoundGain = 0;
m_host.SoundFlags = 0;
m_host.SoundRadius = 0;
m_host.ScheduleFullUpdate();
m_host.SendFullUpdateToAllClients();
}
}
else
{
m_host.Sound = UUID.Zero;
m_host.SoundGain = 0;
m_host.SoundFlags = 0;
m_host.SoundRadius = 0;
m_host.ScheduleFullUpdate();
m_host.SendFullUpdateToAllClients();
}
}
public void llPreloadSound(string sound)
{
m_host.AddScriptLPS(1);
m_host.PreloadSound(sound);
ScriptSleep(1000);
}
///
/// 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)
{
m_host.AddScriptLPS(1);
// 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)
{
m_host.AddScriptLPS(1);
// 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)
{
m_host.AddScriptLPS(1);
// 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)
{
m_host.AddScriptLPS(1);
return src.ToUpper();
}
public LSL_String llToLower(string src)
{
m_host.AddScriptLPS(1);
return src.ToLower();
}
public LSL_Integer llGiveMoney(string destination, int amount)
{
UUID invItemID=InventorySelf();
if (invItemID == UUID.Zero)
return 0;
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
TaskInventoryItem item = m_host.TaskInventory[invItemID];
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter == UUID.Zero)
return 0;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0)
{
LSLError("No permissions to give money");
return 0;
}
UUID toID = new UUID();
if (!UUID.TryParse(destination, out toID))
{
LSLError("Bad key in llGiveMoney");
return 0;
}
IMoneyModule money = World.RequestModuleInterface();
if (money == null)
{
NotImplemented("llGiveMoney");
return 0;
}
bool result = money.ObjectGiveMoney(
m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount);
if (result)
return 1;
return 0;
}
public void llMakeExplosion(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
m_host.AddScriptLPS(1);
Deprecated("llMakeExplosion");
ScriptSleep(100);
}
public void llMakeFountain(int particles, double scale, double vel, double lifetime, double arc, int bounce, string texture, LSL_Vector offset, double bounce_offset)
{
m_host.AddScriptLPS(1);
Deprecated("llMakeFountain");
ScriptSleep(100);
}
public void llMakeSmoke(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
m_host.AddScriptLPS(1);
Deprecated("llMakeSmoke");
ScriptSleep(100);
}
public void llMakeFire(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
{
m_host.AddScriptLPS(1);
Deprecated("llMakeFire");
ScriptSleep(100);
}
public void llRezAtRoot(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param)
{
m_host.AddScriptLPS(1);
if (Double.IsNaN(rot.x) || Double.IsNaN(rot.y) || Double.IsNaN(rot.z) || Double.IsNaN(rot.s))
return;
float dist = (float)llVecDist(llGetPos(), pos);
if (dist > m_ScriptDistanceFactor * 10.0f)
return;
//Clone is thread-safe
TaskInventoryDictionary partInventory = (TaskInventoryDictionary)m_host.TaskInventory.Clone();
foreach (KeyValuePair inv in partInventory)
{
if (inv.Value.Name == inventory)
{
// make sure we're an object.
if (inv.Value.InvType != (int)InventoryType.Object)
{
llSay(0, "Unable to create requested object. Object is missing from database.");
return;
}
Vector3 llpos = new Vector3((float)pos.x, (float)pos.y, (float)pos.z);
Vector3 llvel = new Vector3((float)vel.x, (float)vel.y, (float)vel.z);
// need the magnitude later
float velmag = (float)Util.GetMagnitude(llvel);
SceneObjectGroup new_group = World.RezObject(m_host, inv.Value, llpos, Rot2Quaternion(rot), llvel, param);
// If either of these are null, then there was an unknown error.
if (new_group == null)
continue;
// objects rezzed with this method are die_at_edge by default.
new_group.RootPart.SetDieAtEdge(true);
new_group.ResumeScripts();
m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams(
"object_rez", new Object[] {
new LSL_String(
new_group.RootPart.UUID.ToString()) },
new DetectParams[0]));
float groupmass = new_group.GetMass();
if (new_group.RootPart.PhysActor != null && new_group.RootPart.PhysActor.IsPhysical && llvel != Vector3.Zero)
{
//Recoil.
llApplyImpulse(new LSL_Vector(llvel.X * groupmass, llvel.Y * groupmass, llvel.Z * groupmass), 0);
}
// Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay)
ScriptSleep((int)((groupmass * velmag) / 10));
ScriptSleep(100);
return;
}
}
llSay(0, "Could not find object " + inventory);
}
public void llRezObject(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param)
{
llRezAtRoot(inventory, pos, vel, rot, param);
}
public void llLookAt(LSL_Vector target, double strength, double damping)
{
/*
m_host.AddScriptLPS(1);
// Determine where we are looking from
LSL_Vector from = llGetPos();
// Work out the normalised vector from the source to the target
LSL_Vector delta = llVecNorm(target - from);
LSL_Vector angle = new LSL_Vector(0,0,0);
// Calculate the yaw
// subtracting PI_BY_TWO is required to compensate for the odd SL co-ordinate system
angle.x = llAtan2(delta.z, delta.y) - ScriptBaseClass.PI_BY_TWO;
// Calculate pitch
angle.y = llAtan2(delta.x, llSqrt((delta.y * delta.y) + (delta.z * delta.z)));
// we need to convert from a vector describing
// the angles of rotation in radians into rotation value
LSL_Types.Quaternion rot = llEuler2Rot(angle);
// This would only work if your physics system contains an APID controller:
// Quaternion rotation = new Quaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s);
// m_host.startLookAt(rotation, (float)damping, (float)strength);
// Orient the object to the angle calculated
llSetRot(rot);
*/
//The above code, while nice, doesn't replicate the behaviour of SL and tends to "roll" the object.
//There's probably a smarter way of doing this, my rotation math-fu is weak.
// http://bugs.meta7.com/view.php?id=28
// - Tom
/* And the following does not do the job either. It has to be performed inside the ODE glue-code. -.- .._.
LSL_Rotation newrot = llGetRot() * llRotBetween(new LSL_Vector(1.0d, 0.0d, 0.0d) * llGetRot(), new LSL_Vector(0.0d, 0.0d, -1.0d));
llSetRot(newrot * llRotBetween(new LSL_Vector(0.0d,0.0d,1.0d) * newrot, target - llGetPos()));
*/
if (m_host.PhysActor != null && !m_host.PhysActor.IsPhysical)
{
// Part is non-phys, convert this to a llSetRot()
Vector3 tgt = new Vector3((float)target.x, (float)target.y, (float)target.z);
Vector3 dir = tgt - m_host.GroupPosition;
dir.Normalize();
float tzrot = (float)Math.Atan2(dir.Y, dir.X);
float txy = (float)Math.Sqrt((dir.X * dir.X) + (dir.Y * dir.Y));
float terot = (float)Math.Atan2(-dir.Z, txy);
LSL_Vector az = new LSL_Vector(0.0f, 0.0f, tzrot);
LSL_Vector ae = new LSL_Vector(0.0f, terot, 0.0f);
LSL_Types.Quaternion spin = llEuler2Rot(az);
LSL_Types.Quaternion rot = llEuler2Rot(ae) * spin;
llSetRot(rot);
}
else
{
// Physical, send the target vector to RotLookAt method inside a 'rotation', the .w -99.9 value indicates it is really a LookAt.
Quaternion q = new Quaternion((float)target.x, (float)target.y, (float)target.z, -99.9f);
m_host.RotLookAt(q, (float)strength, (float)damping);
}
}
public void llRotLookAt(LSL_Rotation target, double strength, double damping)
{
m_host.AddScriptLPS(1);
// NotImplemented("llRotLookAt");
m_host.RotLookAt(Rot2Quaternion(target), (float)strength, (float)damping);
}
public void llStopLookAt()
{
m_host.AddScriptLPS(1);
// NotImplemented("llStopLookAt");
m_host.StopLookAt();
}
public void llSetTimerEvent(double sec)
{
if (sec != 0.0 && sec < m_MinTimerInterval)
sec = m_MinTimerInterval;
m_host.AddScriptLPS(1);
// Setting timer repeat
AsyncCommands.TimerPlugin.SetTimerEvent(m_localID, m_itemID, sec);
}
public virtual void llSleep(double sec)
{
m_host.AddScriptLPS(1);
Thread.Sleep((int)(sec * 1000));
}
public LSL_Float llGetMass()
{
m_host.AddScriptLPS(1);
return m_host.GetMass();
}
public void llCollisionFilter(string name, string id, int accept)
{
m_host.AddScriptLPS(1);
m_host.CollisionFilter.Clear();
if (id != null)
{
m_host.CollisionFilter.Add(accept,id);
}
else
{
m_host.CollisionFilter.Add(accept,name);
}
}
public void llTakeControls(int controls, int accept, int pass_on)
{
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter != UUID.Zero)
{
ScenePresence presence = World.GetScenePresence(item.PermsGranter);
if (presence != null)
{
if ((item.PermsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
{
presence.RegisterControlEventsToScript(controls, accept, pass_on, m_localID, m_itemID);
}
}
}
m_host.AddScriptLPS(1);
}
public void llReleaseControls()
{
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
lock (m_host.TaskInventory)
{
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
}
m_host.TaskInventory.LockItemsForRead(false);
m_host.AddScriptLPS(1);
if (item.PermsGranter != UUID.Zero)
{
ScenePresence presence = World.GetScenePresence(item.PermsGranter);
if (presence != null)
{
if ((item.PermsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
{
// Unregister controls from Presence
presence.UnRegisterControlEventsToScript(m_localID, m_itemID);
// Remove Take Control permission.
item.PermsMask &= ~ScriptBaseClass.PERMISSION_TAKE_CONTROLS;
}
}
}
}
public void llReleaseURL(string url)
{
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
m_UrlModule.ReleaseURL(url);
}
public void llAttachToAvatar(int attachment)
{
m_host.AddScriptLPS(1);
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter != m_host.OwnerID)
return;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_ATTACH) != 0)
{
SceneObjectGroup grp = m_host.ParentGroup;
ScenePresence presence = World.GetScenePresence(m_host.OwnerID);
IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule;
if (attachmentsModule != null)
attachmentsModule.AttachObject(presence, grp, (uint)attachment, false);
}
}
public void llDetachFromAvatar()
{
m_host.AddScriptLPS(1);
if (m_host.ParentGroup.AttachmentPoint == 0)
return;
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter != m_host.OwnerID)
return;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_ATTACH) != 0)
{
IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule;
if (attachmentsModule != null)
Util.FireAndForget(DetachWrapper, m_host);
}
}
private void DetachWrapper(object o)
{
SceneObjectPart host = (SceneObjectPart)o;
SceneObjectGroup grp = host.ParentGroup;
UUID itemID = grp.GetFromItemID();
ScenePresence presence = World.GetScenePresence(host.OwnerID);
IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule;
if (attachmentsModule != null)
attachmentsModule.DetachSingleAttachmentToInv(presence, itemID);
}
public void llTakeCamera(string avatar)
{
m_host.AddScriptLPS(1);
Deprecated("llTakeCamera");
}
public void llReleaseCamera(string avatar)
{
m_host.AddScriptLPS(1);
Deprecated("llReleaseCamera");
}
public LSL_String llGetOwner()
{
m_host.AddScriptLPS(1);
return m_host.OwnerID.ToString();
}
public void llInstantMessage(string user, string message)
{
UUID result;
if (!UUID.TryParse(user, out result))
{
ShoutError("An invalid key was passed to llInstantMessage");
ScriptSleep(2000);
return;
}
m_host.AddScriptLPS(1);
// 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.UUID.ToString()); // fromAgentID.Guid;
msg.toAgentID = new Guid(user); // toAgentID.Guid;
msg.imSessionID = new Guid(friendTransactionID.ToString()); // This is the item we're mucking with here
// m_log.Debug("[Scripting IM]: From:" + msg.fromAgentID.ToString() + " To: " + msg.toAgentID.ToString() + " Session:" + msg.imSessionID.ToString() + " Message:" + message);
// m_log.Debug("[Scripting IM]: Filling Session: " + msg.imSessionID.ToString());
// DateTime dt = DateTime.UtcNow;
//
// // Ticks from UtcNow, but make it look like local. Evil, huh?
// dt = DateTime.SpecifyKind(dt, DateTimeKind.Local);
//
// try
// {
// // Convert that to the PST timezone
// TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
// dt = TimeZoneInfo.ConvertTime(dt, timeZoneInfo);
// }
// catch
// {
// // No logging here, as it could be VERY spammy
// }
//
// // And make it look local again to fool the unix time util
// dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
//if (client != null)
//{
msg.fromAgentName = m_host.Name;//client.FirstName + " " + client.LastName;// fromAgentName;
//}
//else
//{
// msg.fromAgentName = "(hippos)";// Added for posterity. This means that we can't figure out who sent it
//}
// Cap the message length at 1024.
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;
msg.binaryBucket
= Util.StringToBytes256(
"{0}/{1}/{2}/{3}",
World.RegionInfo.RegionName,
(int)Math.Floor(m_host.AbsolutePosition.X),
(int)Math.Floor(m_host.AbsolutePosition.Y),
(int)Math.Floor(m_host.AbsolutePosition.Z));
if (m_TransferModule != null)
{
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
}
ScriptSleep(2000);
}
public void llEmail(string address, string subject, string message)
{
m_host.AddScriptLPS(1);
IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface();
if (emailModule == null)
{
ShoutError("llEmail: email module not configured");
return;
}
emailModule.SendEmail(m_host.UUID, address, subject, message);
ScriptSleep(15000);
}
public void llGetNextEmail(string address, string subject)
{
m_host.AddScriptLPS(1);
IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface();
if (emailModule == null)
{
ShoutError("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()
{
m_host.AddScriptLPS(1);
return m_host.UUID.ToString();
}
public void llSetBuoyancy(double buoyancy)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
if (m_host.PhysActor != null)
{
PIDHoverType hoverType = PIDHoverType.Ground;
if (water != 0)
{
hoverType = PIDHoverType.GroundAndWater;
}
m_host.SetHoverHeight((float)height, hoverType, (float)tau);
}
}
public void llStopHover()
{
m_host.AddScriptLPS(1);
if (m_host.PhysActor != null)
{
m_host.SetHoverHeight(0f, PIDHoverType.Ground, 0f);
}
}
public void llMinEventDelay(double delay)
{
m_host.AddScriptLPS(1);
try
{
m_ScriptEngine.SetMinEventDelay(m_itemID, delay);
}
catch (NotImplementedException)
{
// Currently not implemented in DotNetEngine only XEngine
NotImplemented("llMinEventDelay in DotNetEngine");
}
}
///
/// llSoundPreload is deprecated. In SL this appears to do absolutely nothing
/// and is documented to have no delay.
///
public void llSoundPreload(string sound)
{
m_host.AddScriptLPS(1);
}
public LSL_Integer llStringLength(string str)
{
m_host.AddScriptLPS(1);
if (str.Length > 0)
{
return str.Length;
}
else
{
return 0;
}
}
public void llStartAnimation(string anim)
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero)
return;
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter == UUID.Zero)
return;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) != 0)
{
ScenePresence presence = World.GetScenePresence(item.PermsGranter);
if (presence != null)
{
// Do NOT try to parse UUID, animations cannot be triggered by ID
UUID animID = InventoryKey(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)
{
m_host.AddScriptLPS(1);
UUID invItemID=InventorySelf();
if (invItemID == UUID.Zero)
return;
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(InventorySelf()))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[InventorySelf()];
}
m_host.TaskInventory.LockItemsForRead(false);
if (item.PermsGranter == UUID.Zero)
return;
if ((item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) != 0)
{
UUID animID = new UUID();
if (!UUID.TryParse(anim, out animID))
{
animID=InventoryKey(anim);
}
ScenePresence presence = World.GetScenePresence(item.PermsGranter);
if (presence != null)
{
if (animID == UUID.Zero)
presence.Animator.RemoveAnimation(anim);
else
presence.Animator.RemoveAnimation(animID);
}
}
}
public void llPointAt(LSL_Vector pos)
{
m_host.AddScriptLPS(1);
}
public void llStopPointAt()
{
m_host.AddScriptLPS(1);
}
public void llTargetOmega(LSL_Vector axis, double spinrate, double gain)
{
m_host.AddScriptLPS(1);
m_host.AngularVelocity = new Vector3((float)(axis.x * spinrate), (float)(axis.y * spinrate), (float)(axis.z * spinrate));
m_host.ScheduleTerseUpdate();
m_host.SendTerseUpdateToAllClients();
m_host.ParentGroup.HasGroupChanged = true;
}
public LSL_Integer llGetStartParameter()
{
m_host.AddScriptLPS(1);
return m_ScriptEngine.GetStartParameter(m_itemID);
}
public void llRequestPermissions(string agent, int perm)
{
UUID agentID = new UUID();
if (!UUID.TryParse(agent, out agentID))
return;
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero)
return; // Not in a prim? How??
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
if (!m_host.TaskInventory.ContainsKey(invItemID))
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
else
{
item = m_host.TaskInventory[invItemID];
}
m_host.TaskInventory.LockItemsForRead(false);
if (agentID == UUID.Zero || perm == 0) // Releasing permissions
{
llReleaseControls();
item.PermsGranter = UUID.Zero;
item.PermsMask = 0;
m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(0) },
new DetectParams[0]));
return;
}
if (item.PermsGranter != agentID || (perm & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) == 0)
llReleaseControls();
m_host.AddScriptLPS(1);
if (m_host.ParentGroup.IsAttachment && (UUID)agent == m_host.ParentGroup.AttachedAvatar)
{
// When attached, certain permissions are implicit if requested from owner
int implicitPerms = ScriptBaseClass.PERMISSION_TAKE_CONTROLS |
ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION |
ScriptBaseClass.PERMISSION_CONTROL_CAMERA |
ScriptBaseClass.PERMISSION_TRACK_CAMERA |
ScriptBaseClass.PERMISSION_ATTACH;
if ((perm & (~implicitPerms)) == 0) // Requested only implicit perms
{
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[invItemID].PermsGranter = agentID;
m_host.TaskInventory[invItemID].PermsMask = perm;
m_host.TaskInventory.LockItemsForWrite(false);
m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(perm) },
new DetectParams[0]));
return;
}
}
else
{
bool sitting = false;
if (m_host.SitTargetAvatar == agentID)
{
sitting = true;
}
else
{
foreach (SceneObjectPart p in m_host.ParentGroup.Parts)
{
if (p.SitTargetAvatar == agentID)
sitting = true;
}
}
if (sitting)
{
// When agent is sitting, certain permissions are implicit if requested from sitting agent
int implicitPerms = ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION |
ScriptBaseClass.PERMISSION_CONTROL_CAMERA |
ScriptBaseClass.PERMISSION_TRACK_CAMERA |
ScriptBaseClass.PERMISSION_TAKE_CONTROLS;
if ((perm & (~implicitPerms)) == 0) // Requested only implicit perms
{
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[invItemID].PermsGranter = agentID;
m_host.TaskInventory[invItemID].PermsMask = perm;
m_host.TaskInventory.LockItemsForWrite(false);
m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(perm) },
new DetectParams[0]));
return;
}
}
}
ScenePresence presence = World.GetScenePresence(agentID);
if (presence != null)
{
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[invItemID].PermsGranter = agentID;
m_host.TaskInventory[invItemID].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, invItemID, perm);
return;
}
// Requested agent is not in range, refuse perms
m_ScriptEngine.PostScriptEvent(m_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;
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero)
return;
client.OnScriptAnswer-=handleScriptAnswer;
m_waitingForScriptAnswer=false;
if ((answer & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) == 0)
llReleaseControls();
m_host.TaskInventory.LockItemsForWrite(true);
m_host.TaskInventory[invItemID].PermsMask = answer;
m_host.TaskInventory.LockItemsForWrite(false);
m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams(
"run_time_permissions", new Object[] {
new LSL_Integer(answer) },
new DetectParams[0]));
}
public LSL_String llGetPermissionsKey()
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (TaskInventoryItem item in m_host.TaskInventory.Values)
{
if (item.Type == 10 && item.ItemID == m_itemID)
{
m_host.TaskInventory.LockItemsForRead(false);
return item.PermsGranter.ToString();
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero.ToString();
}
public LSL_Integer llGetPermissions()
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (TaskInventoryItem item in m_host.TaskInventory.Values)
{
if (item.Type == 10 && item.ItemID == m_itemID)
{
int perms = item.PermsMask;
if (m_automaticLinkPermission)
perms |= ScriptBaseClass.PERMISSION_CHANGE_LINKS;
m_host.TaskInventory.LockItemsForRead(false);
return perms;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return 0;
}
public LSL_Integer llGetLinkNumber()
{
m_host.AddScriptLPS(1);
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
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
part.SetFaceColor(new Vector3((float)color.x, (float)color.y, (float)color.z), face);
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
}
public void llCreateLink(string target, int parent)
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
UUID targetID;
if (!UUID.TryParse(target, out targetID))
return;
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
item = m_host.TaskInventory[invItemID];
m_host.TaskInventory.LockItemsForRead(false);
if ((item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
ShoutError("Script trying to link but PERMISSION_CHANGE_LINKS permission not set!");
return;
}
IClientAPI client = null;
ScenePresence sp = World.GetScenePresence(item.PermsGranter);
if (sp != null)
client = sp.ControllingClient;
SceneObjectPart targetPart = World.GetSceneObjectPart((UUID)targetID);
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 (targetPart != null)
{
if (parent != 0)
{
parentPrim = m_host.ParentGroup;
childPrim = targetPart.ParentGroup;
}
else
{
parentPrim = targetPart.ParentGroup;
childPrim = m_host.ParentGroup;
}
// byte uf = childPrim.RootPart.UpdateFlag;
childPrim.RootPart.UpdateFlag = 0;
parentPrim.LinkToGroup(childPrim);
// if (uf != (Byte)0)
// parent.RootPart.UpdateFlag = uf;
}
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
parentPrim.RootPart.CreateSelected = true;
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
if (client != null)
parentPrim.SendPropertiesToClient(client);
ScriptSleep(1000);
}
public void llBreakLink(int linknum)
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
m_host.TaskInventory.LockItemsForRead(true);
if ((m_host.TaskInventory[invItemID].PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
ShoutError("Script trying to link but PERMISSION_CHANGE_LINKS permission not set!");
m_host.TaskInventory.LockItemsForRead(false);
return;
}
m_host.TaskInventory.LockItemsForRead(false);
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
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
{
parentPrim.DelinkFromGroup(part.LocalId, true);
}
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
parentPrim.HasGroupChanged = true;
parentPrim.ScheduleGroupForFullUpdate();
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
if (parts.Count > 0)
{
SceneObjectPart newRoot = parts[0];
parts.Remove(newRoot);
try
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
{
part.UpdateFlag = 0;
newRoot.ParentGroup.LinkToGroup(part.ParentGroup);
}
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
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()
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
TaskInventoryItem item;
m_host.TaskInventory.LockItemsForRead(true);
item = m_host.TaskInventory[invItemID];
m_host.TaskInventory.LockItemsForRead(false);
if ((item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0
&& !m_automaticLinkPermission)
{
ShoutError("Script trying to link but PERMISSION_CHANGE_LINKS permission not set!");
return;
}
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)
{
m_host.AddScriptLPS(1);
List keytable = new List();
// parse for sitting avatare-uuids
World.ForEachScenePresence(delegate(ScenePresence presence)
{
if (!presence.IsChildAgent && presence.ParentID != 0 && m_host.ParentGroup.HasChildPrim(presence.ParentID))
keytable.Add(presence.UUID);
});
int totalprims = m_host.ParentGroup.PrimCount + keytable.Count;
if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims)
{
return keytable[totalprims - linknum].ToString();
}
if (linknum == 1 && m_host.ParentGroup.PrimCount == 1 && keytable.Count == 1)
{
return m_host.UUID.ToString();
}
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();
}
}
///
/// 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)
{
m_host.AddScriptLPS(1);
// parse for sitting avatare-names
List nametable = new List();
World.ForEachScenePresence(delegate(ScenePresence presence)
{
if (!presence.IsChildAgent && presence.ParentID != 0 && m_host.ParentGroup.HasChildPrim(presence.ParentID))
nametable.Add(presence.ControllingClient.Name);
});
int totalprims = m_host.ParentGroup.PrimCount + nametable.Count;
if (totalprims > m_host.ParentGroup.PrimCount)
{
// sitting Avatar-Name with negativ linknum / SinglePrim
if (linknum < 0 && m_host.ParentGroup.PrimCount == 1 && nametable.Count == 1)
return nametable[0];
// Prim-Name / SinglePrim Sitting Avatar
if (linknum == 1 && m_host.ParentGroup.PrimCount == 1 && nametable.Count == 1)
return m_host.Name;
// LinkNumber > of Real PrimSet = AvatarName
if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims)
return nametable[totalprims - linknum];
}
// simplest case, this prims link number
if (m_host.LinkNum == linknum)
return m_host.Name;
// Single prim
if (m_host.LinkNum == 0)
{
if (linknum == 0)
return m_host.Name;
else
return UUID.Zero.ToString();
}
// Link set
SceneObjectPart part = null;
if (m_host.LinkNum == 1) // this is the Root prim
{
if (linknum < 0)
part = m_host.ParentGroup.GetLinkNumPart(2);
else
part = m_host.ParentGroup.GetLinkNumPart(linknum);
}
else // this is a child prim
{
if (linknum < 2)
part = m_host.ParentGroup.GetLinkNumPart(1);
else
part = m_host.ParentGroup.GetLinkNumPart(linknum);
}
if (part != null)
return part.Name;
else
return UUID.Zero.ToString();
}
public LSL_Integer llGetInventoryNumber(int type)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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()
{
m_host.AddScriptLPS(1);
// TODO: figure out real energy value
return 1.0f;
}
public void llGiveInventory(string destination, string inventory)
{
m_host.AddScriptLPS(1);
bool found = false;
UUID destId = UUID.Zero;
UUID objId = UUID.Zero;
int assetType = 0;
string objName = String.Empty;
if (!UUID.TryParse(destination, out destId))
{
llSay(0, "Could not parse key " + destination);
return;
}
// move the first object found with this inventory name
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == inventory)
{
found = true;
objId = inv.Key;
assetType = inv.Value.Type;
objName = inv.Value.Name;
break;
}
}
m_host.TaskInventory.LockItemsForRead(false);
if (!found)
{
llSay(0, String.Format("Could not find object '{0}'", inventory));
return;
// throw new Exception(String.Format("The inventory object '{0}' could not be found", inventory));
}
// 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)
{
llSay(0, "Can't find destination "+destId.ToString());
return;
}
}
// destination is an avatar
InventoryItemBase agentItem = World.MoveTaskInventoryItem(destId, UUID.Zero, m_host, objId);
if (agentItem == null)
return;
byte[] bucket = new byte[17];
bucket[0] = (byte)assetType;
byte[] objBytes = agentItem.ID.GetBytes();
Array.Copy(objBytes, 0, bucket, 1, 16);
GridInstantMessage msg = new GridInstantMessage(World,
m_host.UUID, m_host.Name+", an object owned by "+
resolveName(m_host.OwnerID)+",", destId,
(byte)InstantMessageDialog.TaskInventoryOffered,
false, objName+"\n"+m_host.Name+" is located at "+
World.RegionInfo.RegionName+" "+
m_host.AbsolutePosition.ToString(),
agentItem.ID, true, m_host.AbsolutePosition,
bucket);
if (m_TransferModule != null)
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
//This delay should only occur when giving inventory to avatars.
ScriptSleep(3000);
}
}
[DebuggerNonUserCode]
public void llRemoveInventory(string name)
{
m_host.AddScriptLPS(1);
List inv;
try
{
m_host.TaskInventory.LockItemsForRead(true);
inv = new List(m_host.TaskInventory.Values);
}
finally
{
m_host.TaskInventory.LockItemsForRead(false);
}
foreach (TaskInventoryItem item in inv)
{
if (item.Name == name)
{
if (item.ItemID == m_itemID)
throw new ScriptDeleteException();
else
m_host.Inventory.RemoveInventoryItem(item.ItemID);
return;
}
}
}
public void llSetText(string text, LSL_Vector color, double alpha)
{
m_host.AddScriptLPS(1);
Vector3 av3 = new Vector3(Util.Clip((float)color.x, 0.0f, 1.0f),
Util.Clip((float)color.y, 0.0f, 1.0f),
Util.Clip((float)color.z, 0.0f, 1.0f));
m_host.SetText(text, av3, Util.Clip((float)alpha, 0.0f, 1.0f));
m_host.ParentGroup.HasGroupChanged = true;
m_host.ParentGroup.ScheduleGroupForFullUpdate();
}
public LSL_Float llWater(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
return World.RegionInfo.RegionSettings.WaterHeight;
}
public void llPassTouches(int pass)
{
m_host.AddScriptLPS(1);
if (pass != 0)
m_host.PassTouches = true;
else
m_host.PassTouches = false;
}
public LSL_String llRequestAgentData(string id, int data)
{
m_host.AddScriptLPS(1);
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) >= 20000)
{
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 1: // DATA_ONLINE (0|1)
if (pinfo != null && pinfo.RegionID != UUID.Zero)
reply = "1";
else
reply = "0";
break;
case 2: // DATA_NAME (First Last)
reply = account.FirstName + " " + account.LastName;
break;
case 3: // 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 4: // DATA_RATING (0,0,0,0,0,0)
reply = "0,0,0,0,0,0";
break;
case 8: // 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_localID,
m_itemID, rq.ToString());
AsyncCommands.
DataserverPlugin.DataserverReply(rq.ToString(), reply);
ScriptSleep(100);
return tid.ToString();
}
else
{
ShoutError("Invalid UUID passed to llRequestAgentData.");
}
return "";
}
public LSL_String llRequestInventoryData(string name)
{
m_host.AddScriptLPS(1);
//Clone is thread safe
TaskInventoryDictionary itemDictionary = (TaskInventoryDictionary)m_host.TaskInventory.Clone();
foreach (TaskInventoryItem item in itemDictionary.Values)
{
if (item.Type == 3 && item.Name == name)
{
UUID tid = AsyncCommands.
DataserverPlugin.RegisterRequest(m_localID,
m_itemID, item.AssetID.ToString());
Vector3 region = new Vector3(
World.RegionInfo.RegionLocX * Constants.RegionSize,
World.RegionInfo.RegionLocY * Constants.RegionSize,
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);
});
ScriptSleep(1000);
return tid.ToString();
}
}
ScriptSleep(1000);
return String.Empty;
}
public void llSetDamage(double damage)
{
m_host.AddScriptLPS(1);
m_host.ParentGroup.Damage = (float)damage;
}
public void llTeleportAgentHome(string agent)
{
m_host.AddScriptLPS(1);
UUID agentId = new UUID();
if (UUID.TryParse(agent, out agentId))
{
ScenePresence presence = World.GetScenePresence(agentId);
if (presence != null)
{
// agent must not be a god
if (presence.UserLevel >= 200) return;
// agent must be over the owners land
if (m_host.OwnerID == World.LandChannel.GetLandObject(
presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID)
{
World.TeleportClientHome(agentId, presence.ControllingClient);
}
}
}
ScriptSleep(5000);
}
public void llTextBox(string agent, string message, int chatChannel)
{
IDialogModule dm = World.RequestModuleInterface();
if (dm == null)
return;
m_host.AddScriptLPS(1);
UUID av = new UUID();
if (!UUID.TryParse(agent,out av))
{
//LSLError("First parameter to llDialog needs to be a key");
return;
}
if (message == string.Empty)
{
ShoutError("Trying to use llTextBox with empty message.");
}
else if (message.Length > 512)
{
ShoutError("Trying to use llTextBox with message over 512 characters.");
}
else
{
dm.SendTextBoxToUser(av, message, chatChannel, m_host.Name, m_host.UUID, m_host.OwnerID);
ScriptSleep(1000);
}
}
public void llModifyLand(int action, int brush)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
// TODO: Parameter check logic required.
UUID soundId = UUID.Zero;
if (!UUID.TryParse(impact_sound, out soundId))
{
m_host.TaskInventory.LockItemsForRead(true);
foreach (TaskInventoryItem item in m_host.TaskInventory.Values)
{
if (item.Type == (int)AssetType.Sound && item.Name == impact_sound)
{
soundId = item.AssetID;
break;
}
}
m_host.TaskInventory.LockItemsForRead(false);
}
m_host.CollisionSound = soundId;
m_host.CollisionSoundVolume = (float)impact_volume;
}
public LSL_String llGetAnimation(string id)
{
// This should only return a value if the avatar is in the same region
m_host.AddScriptLPS(1);
UUID avatar = (UUID)id;
ScenePresence presence = World.GetScenePresence(avatar);
if (presence == null)
return "";
if (m_host.RegionHandle == presence.RegionHandle)
{
Dictionary animationstateNames = AnimationSet.Animations.AnimStateNames;
if (presence != null)
{
AnimationSet currentAnims = presence.Animator.Animations;
string currentAnimationState = String.Empty;
if (animationstateNames.TryGetValue(currentAnims.DefaultAnimation.AnimID, out currentAnimationState))
return currentAnimationState;
}
}
return String.Empty;
}
public void llMessageLinked(int linknumber, int num, string msg, string id)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
UUID partItemID;
foreach (SceneObjectPart part in parts)
{
//Clone is thread safe
TaskInventoryDictionary itemsDictionary = (TaskInventoryDictionary)part.TaskInventory.Clone();
foreach (TaskInventoryItem item in itemsDictionary.Values)
{
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)
{
m_host.AddScriptLPS(1);
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.GodLevel > 0 && m_host.OwnerID != targetID)
return;
pusheeav = avatar;
// Find pushee position
// Pushee Linked?
if (pusheeav.ParentID != 0)
{
SceneObjectPart parentobj = World.GetSceneObjectPart(pusheeav.ParentID);
if (parentobj != null)
{
PusheePos = parentobj.AbsolutePosition;
}
else
{
PusheePos = pusheeav.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.X, PusheePos.Y);
// 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.X, PusheePos.Y);
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
float pusher_mass = m_host.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 = new Vector3((float)impulse.x, (float)impulse.y, (float)impulse.z);
{
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)
{
if (pusheeav.PhysicsActor != null)
{
if (local != 0)
{
applied_linear_impulse *= m_host.GetWorldRotation();
}
pusheeav.PhysicsActor.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)
{
m_host.AddScriptLPS(1);
if (pass == 0)
{
m_host.ParentGroup.PassCollision = false;
}
else
{
m_host.ParentGroup.PassCollision = true;
}
}
public LSL_String llGetScriptName()
{
string result = String.Empty;
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (TaskInventoryItem item in m_host.TaskInventory.Values)
{
if (item.Type == 10 && item.ItemID == m_itemID)
{
result = item.Name!=null?item.Name:String.Empty;
break;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return result;
}
public LSL_Integer llGetLinkNumberOfSides(int link)
{
m_host.AddScriptLPS(1);
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()
{
m_host.AddScriptLPS(1);
return GetNumberOfSides(m_host);
}
protected int GetNumberOfSides(SceneObjectPart part)
{
int sides = part.GetNumberOfSides();
if (part.GetPrimType() == PrimType.SPHERE && part.Shape.ProfileHollow > 0)
{
// Make up for a bug where LSL shows 4 sides rather than 2
sides += 2;
}
return sides;
}
/* 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)
{
m_host.AddScriptLPS(1);
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
x = axis.x * t;
y = axis.y * t;
z = axis.z * t;
return new LSL_Rotation(x,y,z,s);
}
// Xantor 29/apr/2008
// converts a Quaternion to X,Y,Z axis rotations
public LSL_Vector llRot2Axis(LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
double x,y,z;
if (rot.s > 1) // normalization needed
{
double length = Math.Sqrt(rot.x * rot.x + rot.y * rot.y +
rot.z * rot.z + rot.s * rot.s);
rot.x /= length;
rot.y /= length;
rot.z /= length;
rot.s /= length;
}
// double angle = 2 * Math.Acos(rot.s);
double s = Math.Sqrt(1 - rot.s * rot.s);
if (s < 0.001)
{
x = 1;
y = z = 0;
}
else
{
x = rot.x / s; // normalise axis
y = rot.y / s;
z = rot.z / s;
}
return new LSL_Vector(x,y,z);
}
// Returns the angle of a quaternion (see llRot2Axis for the axis)
public LSL_Float llRot2Angle(LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
if (rot.s > 1) // normalization needed
{
double length = Math.Sqrt(rot.x * rot.x + rot.y * rot.y +
rot.z * rot.z + rot.s * rot.s);
rot.x /= length;
rot.y /= length;
rot.z /= length;
rot.s /= length;
}
double angle = 2 * Math.Acos(rot.s);
return angle;
}
public LSL_Float llAcos(double val)
{
m_host.AddScriptLPS(1);
return (double)Math.Acos(val);
}
public LSL_Float llAsin(double val)
{
m_host.AddScriptLPS(1);
return (double)Math.Asin(val);
}
// Xantor 30/apr/2008
public LSL_Float llAngleBetween(LSL_Rotation a, LSL_Rotation b)
{
m_host.AddScriptLPS(1);
double angle = Math.Acos(a.x * b.x + a.y * b.y + a.z * b.z + a.s * b.s) * 2;
if (angle < 0) angle = -angle;
if (angle > Math.PI) return (Math.PI * 2 - angle);
return angle;
}
public LSL_String llGetInventoryKey(string name)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
if ((inv.Value.CurrentPermissions & (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify)) == (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify))
{
m_host.TaskInventory.LockItemsForRead(false);
return inv.Value.AssetID.ToString();
}
else
{
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero.ToString();
}
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero.ToString();
}
public void llAllowInventoryDrop(int add)
{
m_host.AddScriptLPS(1);
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()
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
return source.IndexOf(pattern);
}
public LSL_String llGetOwnerKey(string id)
{
m_host.AddScriptLPS(1);
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()
{
m_host.AddScriptLPS(1);
Vector3 center = m_host.GetGeometricCenter();
return new LSL_Vector(center.X,center.Y,center.Z);
}
public LSL_List llListSort(LSL_List src, int stride, int ascending)
{
m_host.AddScriptLPS(1);
if (stride <= 0)
{
stride = 1;
}
return src.Sort(stride, ascending);
}
public LSL_Integer llGetListLength(LSL_List src)
{
m_host.AddScriptLPS(1);
return src.Length;
}
public LSL_Integer llList2Integer(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return 0;
}
try
{
if (src.Data[index] is LSL_Integer)
return (LSL_Integer) src.Data[index];
else if (src.Data[index] is LSL_Float)
return Convert.ToInt32(((LSL_Float) src.Data[index]).value);
return new LSL_Integer(src.Data[index].ToString());
}
catch (FormatException)
{
return 0;
}
}
public LSL_Float llList2Float(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return 0.0;
}
try
{
if (src.Data[index] is LSL_Integer)
return Convert.ToDouble(((LSL_Integer) src.Data[index]).value);
else if (src.Data[index] is LSL_Float)
return Convert.ToDouble(((LSL_Float) src.Data[index]).value);
else if (src.Data[index] is LSL_String)
{
string str = ((LSL_String) src.Data[index]).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(src.Data[index]);
}
catch (FormatException)
{
return 0.0;
}
}
public LSL_String llList2String(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return String.Empty;
}
return src.Data[index].ToString();
}
public LSL_String llList2Key(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return "";
}
return src.Data[index].ToString();
}
public LSL_Vector llList2Vector(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return new LSL_Vector(0, 0, 0);
}
if (src.Data[index].GetType() == typeof(LSL_Vector))
{
return (LSL_Vector)src.Data[index];
}
else
{
return new LSL_Vector(src.Data[index].ToString());
}
}
public LSL_Rotation llList2Rot(LSL_List src, int index)
{
m_host.AddScriptLPS(1);
if (index < 0)
{
index = src.Length + index;
}
if (index >= src.Length || index < 0)
{
return new LSL_Rotation(0, 0, 0, 1);
}
if (src.Data[index].GetType() == typeof(LSL_Rotation))
{
return (LSL_Rotation)src.Data[index];
}
else
{
return new LSL_Rotation(src.Data[index].ToString());
}
}
public LSL_List llList2List(LSL_List src, int start, int end)
{
m_host.AddScriptLPS(1);
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)
{
m_host.AddScriptLPS(1);
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)
{
string ret = String.Empty;
int x = 0;
m_host.AddScriptLPS(1);
if (src.Data.Length > 0)
{
ret = src.Data[x++].ToString();
for (; x < src.Data.Length; x++)
{
ret += ", "+src.Data[x].ToString();
}
}
return ret;
}
///
/// The supplied string is scanned for commas
/// and converted into a list. Commas are only
/// effective if they are encountered outside
/// of '<' '>' delimiters. Any whitespace
/// before or after an element is trimmed.
///
public LSL_List llCSV2List(string src)
{
LSL_List result = new LSL_List();
int parens = 0;
int start = 0;
int length = 0;
m_host.AddScriptLPS(1);
for (int i = 0; i < src.Length; i++)
{
switch (src[i])
{
case '<':
parens++;
length++;
break;
case '>':
if (parens > 0)
parens--;
length++;
break;
case ',':
if (parens == 0)
{
result.Add(new LSL_String(src.Substring(start,length).Trim()));
start += length+1;
length = 0;
}
else
{
length++;
}
break;
default:
length++;
break;
}
}
result.Add(new LSL_String(src.Substring(start,length).Trim()));
return result;
}
///
/// Randomizes the list, be arbitrarily reordering
/// sublists of stride elements. As the stride approaches
/// the size of the list, the options become very
/// limited.
///
///
/// This could take a while for very large list
/// sizes.
///
public LSL_List llListRandomize(LSL_List src, int stride)
{
LSL_List result;
Random rand = new Random();
int chunkk;
int[] chunks;
m_host.AddScriptLPS(1);
if (stride <= 0)
{
stride = 1;
}
// Stride MUST be a factor of the list length
// If not, then return the src list. This also
// traps those cases where stride > length.
if (src.Length != stride && src.Length%stride == 0)
{
chunkk = src.Length/stride;
chunks = new int[chunkk];
for (int i = 0; i < chunkk; i++)
chunks[i] = i;
// Knuth shuffle the chunkk index
for (int i = chunkk - 1; i >= 1; i--)
{
// Elect an unrandomized chunk to swap
int index = rand.Next(i + 1);
int tmp;
// and swap position with first unrandomized chunk
tmp = chunks[i];
chunks[i] = chunks[index];
chunks[index] = tmp;
}
// Construct the randomized list
result = new LSL_List();
for (int i = 0; i < chunkk; i++)
{
for (int j = 0; j < stride; j++)
{
result.Add(src.Data[chunks[i]*stride+j]);
}
}
}
else
{
object[] array = new object[src.Length];
Array.Copy(src.Data, 0, array, 0, src.Length);
result = new LSL_List(array);
}
return result;
}
///
/// Elements in the source list starting with 0 and then
/// every i+stride. If the stride is negative then the scan
/// is backwards producing an inverted result.
/// Only those elements that are also in the specified
/// range are included in the result.
///
public LSL_List llList2ListStrided(LSL_List src, int start, int end, int stride)
{
LSL_List result = new LSL_List();
int[] si = new int[2];
int[] ei = new int[2];
bool twopass = false;
m_host.AddScriptLPS(1);
// First step is always to deal with negative indices
if (start < 0)
start = src.Length+start;
if (end < 0)
end = src.Length+end;
// Out of bounds indices are OK, just trim them
// accordingly
if (start > src.Length)
start = src.Length;
if (end > src.Length)
end = src.Length;
if (stride == 0)
stride = 1;
// There may be one or two ranges to be considered
if (start != end)
{
if (start <= end)
{
si[0] = start;
ei[0] = end;
}
else
{
si[1] = start;
ei[1] = src.Length;
si[0] = 0;
ei[0] = end;
twopass = true;
}
// The scan always starts from the beginning of the
// source list, but members are only selected if they
// fall within the specified sub-range. The specified
// range values are inclusive.
// A negative stride reverses the direction of the
// scan producing an inverted list as a result.
if (stride > 0)
{
for (int i = 0; i < src.Length; i += stride)
{
if (i<=ei[0] && i>=si[0])
result.Add(src.Data[i]);
if (twopass && i>=si[1] && i<=ei[1])
result.Add(src.Data[i]);
}
}
else if (stride < 0)
{
for (int i = src.Length - 1; i >= 0; i += stride)
{
if (i <= ei[0] && i >= si[0])
result.Add(src.Data[i]);
if (twopass && i >= si[1] && i <= ei[1])
result.Add(src.Data[i]);
}
}
}
else
{
if (start%stride == 0)
{
result.Add(src.Data[start]);
}
}
return result;
}
public LSL_Integer llGetRegionAgentCount()
{
m_host.AddScriptLPS(1);
return new LSL_Integer(World.GetRootAgentCount());
}
public LSL_Vector llGetRegionCorner()
{
m_host.AddScriptLPS(1);
return new LSL_Vector(World.RegionInfo.RegionLocX * Constants.RegionSize, World.RegionInfo.RegionLocY * Constants.RegionSize, 0);
}
///
/// Insert the list identified by into the
/// list designated by such that the first
/// new element has the index specified by
///
public LSL_List llListInsertList(LSL_List dest, LSL_List src, int index)
{
LSL_List pref = null;
LSL_List suff = null;
m_host.AddScriptLPS(1);
if (index < 0)
{
index = index+dest.Length;
if (index < 0)
{
index = 0;
}
}
if (index != 0)
{
pref = dest.GetSublist(0,index-1);
if (index < dest.Length)
{
suff = dest.GetSublist(index,-1);
return pref + src + suff;
}
else
{
return pref + src;
}
}
else
{
if (index < dest.Length)
{
suff = dest.GetSublist(index,-1);
return src + suff;
}
else
{
return src;
}
}
}
///
/// Returns the index of the first occurrence of test
/// in src.
///
public LSL_Integer llListFindList(LSL_List src, LSL_List test)
{
int index = -1;
int length = src.Length - test.Length + 1;
m_host.AddScriptLPS(1);
// If either list is empty, do not match
if (src.Length != 0 && test.Length != 0)
{
for (int i = 0; i < length; i++)
{
if (src.Data[i].Equals(test.Data[0]))
{
int j;
for (j = 1; j < test.Length; j++)
if (!src.Data[i+j].Equals(test.Data[j]))
break;
if (j == test.Length)
{
index = i;
break;
}
}
}
}
return index;
}
public LSL_String llGetObjectName()
{
m_host.AddScriptLPS(1);
return m_host.Name!=null?m_host.Name:String.Empty;
}
public void llSetObjectName(string name)
{
m_host.AddScriptLPS(1);
m_host.Name = name!=null?name:String.Empty;
}
public LSL_String llGetDate()
{
m_host.AddScriptLPS(1);
DateTime date = DateTime.Now.ToUniversalTime();
string result = date.ToString("yyyy-MM-dd");
return result;
}
public LSL_Integer llEdgeOfWorld(LSL_Vector pos, LSL_Vector dir)
{
m_host.AddScriptLPS(1);
// edge will be used to pass the Region Coordinates offset
// we want to check for a neighboring sim
LSL_Vector edge = new LSL_Vector(0, 0, 0);
if (dir.x == 0)
{
if (dir.y == 0)
{
// Direction vector is 0,0 so return
// false since we're staying in the sim
return 0;
}
else
{
// Y is the only valid direction
edge.y = dir.y / Math.Abs(dir.y);
}
}
else
{
LSL_Float mag;
if (dir.x > 0)
{
mag = (Constants.RegionSize - pos.x) / dir.x;
}
else
{
mag = (pos.x/dir.x);
}
mag = Math.Abs(mag);
edge.y = pos.y + (dir.y * mag);
if (edge.y > Constants.RegionSize || edge.y < 0)
{
// Y goes out of bounds first
edge.y = dir.y / Math.Abs(dir.y);
}
else
{
// X goes out of bounds first or its a corner exit
edge.y = 0;
edge.x = dir.x / Math.Abs(dir.x);
}
}
List neighbors = World.GridService.GetNeighbours(World.RegionInfo.ScopeID, World.RegionInfo.RegionID);
uint neighborX = World.RegionInfo.RegionLocX + (uint)dir.x;
uint neighborY = World.RegionInfo.RegionLocY + (uint)dir.y;
foreach (GridRegion sri in neighbors)
{
if (sri.RegionLocX == neighborX && sri.RegionLocY == neighborY)
return 0;
}
return 1;
}
///
/// Not fully implemented yet. Still to do:-
/// AGENT_BUSY
/// Remove as they are done
///
public LSL_Integer llGetAgentInfo(string id)
{
m_host.AddScriptLPS(1);
UUID key = new UUID();
if (!UUID.TryParse(id, out key))
{
return 0;
}
int flags = 0;
ScenePresence agent = World.GetScenePresence(key);
if (agent == null)
{
return 0;
}
if (agent.IsChildAgent)
return 0; // Fail if they are not in the same region
// note: in OpenSim, sitting seems to cancel AGENT_ALWAYS_RUN, unlike SL
if (agent.SetAlwaysRun)
{
flags |= ScriptBaseClass.AGENT_ALWAYS_RUN;
}
if (agent.HasAttachments())
{
flags |= ScriptBaseClass.AGENT_ATTACHMENTS;
if (agent.HasScriptedAttachments())
flags |= ScriptBaseClass.AGENT_SCRIPTED;
}
if ((agent.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0)
{
flags |= ScriptBaseClass.AGENT_FLYING;
flags |= ScriptBaseClass.AGENT_IN_AIR; // flying always implies in-air, even if colliding with e.g. a wall
}
if ((agent.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AWAY) != 0)
{
flags |= ScriptBaseClass.AGENT_AWAY;
}
// seems to get unset, even if in mouselook, when avatar is sitting on a prim???
if ((agent.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0)
{
flags |= ScriptBaseClass.AGENT_MOUSELOOK;
}
if ((agent.State & (byte)AgentState.Typing) != (byte)0)
{
flags |= ScriptBaseClass.AGENT_TYPING;
}
string agentMovementAnimation = agent.Animator.GetMovementAnimation();
if (agentMovementAnimation == "CROUCH")
{
flags |= ScriptBaseClass.AGENT_CROUCHING;
}
if (agentMovementAnimation == "WALK" || agentMovementAnimation == "CROUCHWALK")
{
flags |= ScriptBaseClass.AGENT_WALKING;
}
// not colliding implies in air. Note: flying also implies in-air, even if colliding (see above)
// note: AGENT_IN_AIR and AGENT_WALKING seem to be mutually exclusive states in SL.
// note: this may need some tweaking when walking downhill. you "fall down" for a brief instant
// and don't collide when walking downhill, which instantly registers as in-air, briefly. should
// there be some minimum non-collision threshold time before claiming the avatar is in-air?
if ((flags & ScriptBaseClass.AGENT_WALKING) == 0 &&
agent.PhysicsActor != null &&
!agent.PhysicsActor.IsColliding)
{
flags |= ScriptBaseClass.AGENT_IN_AIR;
}
if (agent.ParentID != 0)
{
flags |= ScriptBaseClass.AGENT_ON_OBJECT;
flags |= ScriptBaseClass.AGENT_SITTING;
}
if (agent.Animator.Animations.DefaultAnimation.AnimID
== AnimationSet.Animations.AnimsUUID["SIT_GROUND_CONSTRAINED"])
{
flags |= ScriptBaseClass.AGENT_SITTING;
}
return flags;
}
public LSL_String llGetAgentLanguage(string id)
{
// This should only return a value if the avatar is in the same region
//ckrinke 1-30-09 : This needs to parse the XMLRPC language field supplied
//by the client at login. Currently returning only en-us until our I18N
//effort gains momentum
m_host.AddScriptLPS(1);
return "en-us";
}
public void llAdjustSoundVolume(double volume)
{
m_host.AddScriptLPS(1);
m_host.AdjustSoundGain(volume);
ScriptSleep(100);
}
public void llSetSoundRadius(double radius)
{
m_host.AddScriptLPS(1);
m_host.SoundRadius = radius;
}
public LSL_String llKey2Name(string id)
{
m_host.AddScriptLPS(1);
UUID key = new UUID();
if (UUID.TryParse(id,out key))
{
ScenePresence presence = World.GetScenePresence(key);
if (presence != null)
{
return presence.ControllingClient.Name;
//return presence.Name;
}
if (World.GetSceneObjectPart(key) != null)
{
return World.GetSceneObjectPart(key).Name;
}
}
return String.Empty;
}
public void llSetTextureAnim(int mode, int face, int sizex, int sizey, double start, double length, double rate)
{
m_host.AddScriptLPS(1);
SetTextureAnim(m_host, mode, face, sizex, sizey, start, length, rate);
}
public void llSetLinkTextureAnim(int linknumber, int mode, int face, int sizex, int sizey, double start, double length, double rate)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
if (parts.Count > 0)
{
try
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (var part in parts)
{
SetTextureAnim(part, mode, face, sizex, sizey, start, length, rate);
}
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
}
private void SetTextureAnim(SceneObjectPart part, int mode, int face, int sizex, int sizey, double start, double length, double rate)
{
Primitive.TextureAnimation pTexAnim = new Primitive.TextureAnimation();
pTexAnim.Flags = (Primitive.TextureAnimMode)mode;
//ALL_SIDES
if (face == ScriptBaseClass.ALL_SIDES)
face = 255;
pTexAnim.Face = (uint)face;
pTexAnim.Length = (float)length;
pTexAnim.Rate = (float)rate;
pTexAnim.SizeX = (uint)sizex;
pTexAnim.SizeY = (uint)sizey;
pTexAnim.Start = (float)start;
part.AddTextureAnimation(pTexAnim);
part.SendFullUpdateToAllClients();
part.ParentGroup.HasGroupChanged = true;
}
public void llTriggerSoundLimited(string sound, double volume, LSL_Vector top_north_east,
LSL_Vector bottom_south_west)
{
m_host.AddScriptLPS(1);
float radius1 = (float)llVecDist(llGetPos(), top_north_east);
float radius2 = (float)llVecDist(llGetPos(), bottom_south_west);
float radius = Math.Abs(radius1 - radius2);
m_host.SendSound(KeyOrName(sound).ToString(), volume, true, 0, radius, false, false);
}
public void llEjectFromLand(string pest)
{
m_host.AddScriptLPS(1);
UUID agentId = new UUID();
if (UUID.TryParse(pest, out agentId))
{
ScenePresence presence = World.GetScenePresence(agentId);
if (presence != null)
{
// agent must be over the owners land
if (m_host.OwnerID == World.LandChannel.GetLandObject(
presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID)
World.TeleportClientHome(agentId, presence.ControllingClient);
}
}
ScriptSleep(5000);
}
public LSL_List llParseString2List(string str, LSL_List separators, LSL_List in_spacers)
{
return ParseString2List(str, separators, in_spacers, false);
}
public LSL_Integer llOverMyLand(string id)
{
m_host.AddScriptLPS(1);
UUID key = new UUID();
if (UUID.TryParse(id, out key))
{
ScenePresence presence = World.GetScenePresence(key);
if (presence != null) // object is an avatar
{
if (m_host.OwnerID
== World.LandChannel.GetLandObject(
presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID)
return 1;
}
else // object is not an avatar
{
SceneObjectPart obj = World.GetSceneObjectPart(key);
if (obj != null)
if (m_host.OwnerID
== World.LandChannel.GetLandObject(
obj.AbsolutePosition.X, obj.AbsolutePosition.Y).LandData.OwnerID)
return 1;
}
}
return 0;
}
public LSL_String llGetLandOwnerAt(LSL_Vector pos)
{
m_host.AddScriptLPS(1);
ILandObject land = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y);
if (land == null)
return UUID.Zero.ToString();
return land.LandData.OwnerID.ToString();
}
///
/// According to http://lslwiki.net/lslwiki/wakka.php?wakka=llGetAgentSize
/// only the height of avatars vary and that says:
/// Width (x) and depth (y) are constant. (0.45m and 0.6m respectively).
///
public LSL_Vector llGetAgentSize(string id)
{
m_host.AddScriptLPS(1);
ScenePresence avatar = World.GetScenePresence((UUID)id);
LSL_Vector agentSize;
if (avatar == null || avatar.IsChildAgent) // Fail if not in the same region
{
agentSize = ScriptBaseClass.ZERO_VECTOR;
}
else
{
agentSize = new LSL_Vector(0.45, 0.6, avatar.Appearance.AvatarHeight);
}
return agentSize;
}
public LSL_Integer llSameGroup(string agent)
{
m_host.AddScriptLPS(1);
UUID agentId = new UUID();
if (!UUID.TryParse(agent, out agentId))
return new LSL_Integer(0);
ScenePresence presence = World.GetScenePresence(agentId);
if (presence == null || presence.IsChildAgent) // Return flase for child agents
return new LSL_Integer(0);
IClientAPI client = presence.ControllingClient;
if (m_host.GroupID == client.ActiveGroupId)
return new LSL_Integer(1);
else
return new LSL_Integer(0);
}
public void llUnSit(string id)
{
m_host.AddScriptLPS(1);
UUID key = new UUID();
if (UUID.TryParse(id, out key))
{
ScenePresence av = World.GetScenePresence(key);
if (av != null)
{
if (llAvatarOnSitTarget() == id)
{
// if the avatar is sitting on this object, then
// we can unsit them. We don't want random scripts unsitting random people
// Lets avoid the popcorn avatar scenario.
av.StandUp();
}
else
{
// If the object owner also owns the parcel
// or
// if the land is group owned and the object is group owned by the same group
// or
// if the object is owned by a person with estate access.
ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition.X, av.AbsolutePosition.Y);
if (parcel != null)
{
if (m_host.OwnerID == parcel.LandData.OwnerID ||
(m_host.OwnerID == m_host.GroupID && m_host.GroupID == parcel.LandData.GroupID
&& parcel.LandData.IsGroupOwned) || World.Permissions.IsGod(m_host.OwnerID))
{
av.StandUp();
}
}
}
}
}
}
public LSL_Vector llGroundSlope(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
//Get the slope normal. This gives us the equation of the plane tangent to the slope.
LSL_Vector vsn = llGroundNormal(offset);
//Plug the x,y coordinates of the slope normal into the equation of the plane to get
//the height of that point on the plane. The resulting vector gives the slope.
Vector3 vsl = new Vector3();
vsl.X = (float)vsn.x;
vsl.Y = (float)vsn.y;
vsl.Z = (float)(((vsn.x * vsn.x) + (vsn.y * vsn.y)) / (-1 * vsn.z));
vsl.Normalize();
//Normalization might be overkill here
return new LSL_Vector(vsl.X, vsl.Y, vsl.Z);
}
public LSL_Vector llGroundNormal(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
Vector3 pos = m_host.GetWorldPosition() + new Vector3((float)offset.x,
(float)offset.y,
(float)offset.z);
// 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;
//Find two points in addition to the position to define a plane
Vector3 p0 = new Vector3(pos.X, pos.Y,
(float)World.Heightmap[(int)pos.X, (int)pos.Y]);
Vector3 p1 = new Vector3();
Vector3 p2 = new Vector3();
if ((pos.X + 1.0f) >= World.Heightmap.Width)
p1 = new Vector3(pos.X + 1.0f, pos.Y,
(float)World.Heightmap[(int)pos.X, (int)pos.Y]);
else
p1 = new Vector3(pos.X + 1.0f, pos.Y,
(float)World.Heightmap[(int)(pos.X + 1.0f), (int)pos.Y]);
if ((pos.Y + 1.0f) >= World.Heightmap.Height)
p2 = new Vector3(pos.X, pos.Y + 1.0f,
(float)World.Heightmap[(int)pos.X, (int)pos.Y]);
else
p2 = new Vector3(pos.X, pos.Y + 1.0f,
(float)World.Heightmap[(int)pos.X, (int)(pos.Y + 1.0f)]);
//Find normalized vectors from p0 to p1 and p0 to p2
Vector3 v0 = new Vector3(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
Vector3 v1 = new Vector3(p2.X - p0.X, p2.Y - p0.Y, p2.Z - p0.Z);
v0.Normalize();
v1.Normalize();
//Find the cross product of the vectors (the slope normal).
Vector3 vsn = new Vector3();
vsn.X = (v0.Y * v1.Z) - (v0.Z * v1.Y);
vsn.Y = (v0.Z * v1.X) - (v0.X * v1.Z);
vsn.Z = (v0.X * v1.Y) - (v0.Y * v1.X);
vsn.Normalize();
//I believe the crossproduct of two normalized vectors is a normalized vector so
//this normalization may be overkill
return new LSL_Vector(vsn.X, vsn.Y, vsn.Z);
}
public LSL_Vector llGroundContour(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
LSL_Vector x = llGroundSlope(offset);
return new LSL_Vector(-x.y, x.x, 0.0);
}
public LSL_Integer llGetAttached()
{
m_host.AddScriptLPS(1);
return m_host.ParentGroup.AttachmentPoint;
}
public virtual LSL_Integer llGetFreeMemory()
{
m_host.AddScriptLPS(1);
// Make scripts designed for LSO happy
return 16384;
}
public LSL_Integer llGetFreeURLs()
{
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
return new LSL_Integer(m_UrlModule.GetFreeUrls());
return new LSL_Integer(0);
}
public LSL_String llGetRegionName()
{
m_host.AddScriptLPS(1);
return World.RegionInfo.RegionName;
}
public LSL_Float llGetRegionTimeDilation()
{
m_host.AddScriptLPS(1);
return (double)World.TimeDilation;
}
///
/// Returns the value reported in the client Statistics window
///
public LSL_Float llGetRegionFPS()
{
m_host.AddScriptLPS(1);
return World.SimulatorFPS;
}
/* particle system rules should be coming into this routine as doubles, that is
rule[0] should be an integer from this list and rule[1] should be the arg
for the same integer. wiki.secondlife.com has most of this mapping, but some
came from http://www.caligari-designs.com/p4u2
We iterate through the list for 'Count' elements, incrementing by two for each
iteration and set the members of Primitive.ParticleSystem, one at a time.
*/
public enum PrimitiveRule : int
{
PSYS_PART_FLAGS = 0,
PSYS_PART_START_COLOR = 1,
PSYS_PART_START_ALPHA = 2,
PSYS_PART_END_COLOR = 3,
PSYS_PART_END_ALPHA = 4,
PSYS_PART_START_SCALE = 5,
PSYS_PART_END_SCALE = 6,
PSYS_PART_MAX_AGE = 7,
PSYS_SRC_ACCEL = 8,
PSYS_SRC_PATTERN = 9,
PSYS_SRC_INNERANGLE = 10,
PSYS_SRC_OUTERANGLE = 11,
PSYS_SRC_TEXTURE = 12,
PSYS_SRC_BURST_RATE = 13,
PSYS_SRC_BURST_PART_COUNT = 15,
PSYS_SRC_BURST_RADIUS = 16,
PSYS_SRC_BURST_SPEED_MIN = 17,
PSYS_SRC_BURST_SPEED_MAX = 18,
PSYS_SRC_MAX_AGE = 19,
PSYS_SRC_TARGET_KEY = 20,
PSYS_SRC_OMEGA = 21,
PSYS_SRC_ANGLE_BEGIN = 22,
PSYS_SRC_ANGLE_END = 23
}
internal Primitive.ParticleSystem.ParticleDataFlags ConvertUINTtoFlags(uint flags)
{
Primitive.ParticleSystem.ParticleDataFlags returnval = Primitive.ParticleSystem.ParticleDataFlags.None;
return returnval;
}
protected Primitive.ParticleSystem getNewParticleSystemWithSLDefaultValues()
{
Primitive.ParticleSystem ps = new Primitive.ParticleSystem();
// TODO find out about the other defaults and add them here
ps.PartStartColor = new Color4(1.0f, 1.0f, 1.0f, 1.0f);
ps.PartEndColor = new Color4(1.0f, 1.0f, 1.0f, 1.0f);
ps.PartStartScaleX = 1.0f;
ps.PartStartScaleY = 1.0f;
ps.PartEndScaleX = 1.0f;
ps.PartEndScaleY = 1.0f;
ps.BurstSpeedMin = 1.0f;
ps.BurstSpeedMax = 1.0f;
ps.BurstRate = 0.1f;
ps.PartMaxAge = 10.0f;
return ps;
}
public void llLinkParticleSystem(int linknumber, LSL_List rules)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
foreach (var part in parts)
{
SetParticleSystem(part, rules);
}
}
public void llParticleSystem(LSL_List rules)
{
m_host.AddScriptLPS(1);
SetParticleSystem(m_host, rules);
}
private void SetParticleSystem(SceneObjectPart part, LSL_List rules)
{
if (rules.Length == 0)
{
part.RemoveParticleSystem();
part.ParentGroup.HasGroupChanged = true;
}
else
{
Primitive.ParticleSystem prules = getNewParticleSystemWithSLDefaultValues();
LSL_Vector tempv = new LSL_Vector();
float tempf = 0;
for (int i = 0; i < rules.Length; i += 2)
{
switch (rules.GetLSLIntegerItem(i))
{
case (int)ScriptBaseClass.PSYS_PART_FLAGS:
prules.PartDataFlags = (Primitive.ParticleSystem.ParticleDataFlags)(uint)rules.GetLSLIntegerItem(i + 1);
break;
case (int)ScriptBaseClass.PSYS_PART_START_COLOR:
tempv = rules.GetVector3Item(i + 1);
prules.PartStartColor.R = (float)tempv.x;
prules.PartStartColor.G = (float)tempv.y;
prules.PartStartColor.B = (float)tempv.z;
break;
case (int)ScriptBaseClass.PSYS_PART_START_ALPHA:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.PartStartColor.A = tempf;
break;
case (int)ScriptBaseClass.PSYS_PART_END_COLOR:
tempv = rules.GetVector3Item(i + 1);
prules.PartEndColor.R = (float)tempv.x;
prules.PartEndColor.G = (float)tempv.y;
prules.PartEndColor.B = (float)tempv.z;
break;
case (int)ScriptBaseClass.PSYS_PART_END_ALPHA:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.PartEndColor.A = tempf;
break;
case (int)ScriptBaseClass.PSYS_PART_START_SCALE:
tempv = rules.GetVector3Item(i + 1);
prules.PartStartScaleX = (float)tempv.x;
prules.PartStartScaleY = (float)tempv.y;
break;
case (int)ScriptBaseClass.PSYS_PART_END_SCALE:
tempv = rules.GetVector3Item(i + 1);
prules.PartEndScaleX = (float)tempv.x;
prules.PartEndScaleY = (float)tempv.y;
break;
case (int)ScriptBaseClass.PSYS_PART_MAX_AGE:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.PartMaxAge = tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_ACCEL:
tempv = rules.GetVector3Item(i + 1);
prules.PartAcceleration.X = (float)tempv.x;
prules.PartAcceleration.Y = (float)tempv.y;
prules.PartAcceleration.Z = (float)tempv.z;
break;
case (int)ScriptBaseClass.PSYS_SRC_PATTERN:
int tmpi = (int)rules.GetLSLIntegerItem(i + 1);
prules.Pattern = (Primitive.ParticleSystem.SourcePattern)tmpi;
break;
// PSYS_SRC_INNERANGLE and PSYS_SRC_ANGLE_BEGIN use the same variables. The
// PSYS_SRC_OUTERANGLE and PSYS_SRC_ANGLE_END also use the same variable. The
// client tells the difference between the two by looking at the 0x02 bit in
// the PartFlags variable.
case (int)ScriptBaseClass.PSYS_SRC_INNERANGLE:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.InnerAngle = (float)tempf;
prules.PartFlags &= 0xFFFFFFFD; // Make sure new angle format is off.
break;
case (int)ScriptBaseClass.PSYS_SRC_OUTERANGLE:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.OuterAngle = (float)tempf;
prules.PartFlags &= 0xFFFFFFFD; // Make sure new angle format is off.
break;
case (int)ScriptBaseClass.PSYS_SRC_TEXTURE:
prules.Texture = KeyOrName(rules.GetLSLStringItem(i + 1));
break;
case (int)ScriptBaseClass.PSYS_SRC_BURST_RATE:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.BurstRate = (float)tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_BURST_PART_COUNT:
prules.BurstPartCount = (byte)(int)rules.GetLSLIntegerItem(i + 1);
break;
case (int)ScriptBaseClass.PSYS_SRC_BURST_RADIUS:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.BurstRadius = (float)tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_BURST_SPEED_MIN:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.BurstSpeedMin = (float)tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_BURST_SPEED_MAX:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.BurstSpeedMax = (float)tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_MAX_AGE:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.MaxAge = (float)tempf;
break;
case (int)ScriptBaseClass.PSYS_SRC_TARGET_KEY:
UUID key = UUID.Zero;
if (UUID.TryParse(rules.Data[i + 1].ToString(), out key))
{
prules.Target = key;
}
else
{
prules.Target = part.UUID;
}
break;
case (int)ScriptBaseClass.PSYS_SRC_OMEGA:
// AL: This is an assumption, since it is the only thing that would match.
tempv = rules.GetVector3Item(i + 1);
prules.AngularVelocity.X = (float)tempv.x;
prules.AngularVelocity.Y = (float)tempv.y;
prules.AngularVelocity.Z = (float)tempv.z;
break;
case (int)ScriptBaseClass.PSYS_SRC_ANGLE_BEGIN:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.InnerAngle = (float)tempf;
prules.PartFlags |= 0x02; // Set new angle format.
break;
case (int)ScriptBaseClass.PSYS_SRC_ANGLE_END:
tempf = (float)rules.GetLSLFloatItem(i + 1);
prules.OuterAngle = (float)tempf;
prules.PartFlags |= 0x02; // Set new angle format.
break;
}
}
prules.CRC = 1;
part.AddNewParticleSystem(prules);
part.ParentGroup.HasGroupChanged = true;
}
part.SendFullUpdateToAllClients();
}
public void llGroundRepel(double height, int water, double tau)
{
m_host.AddScriptLPS(1);
if (m_host.PhysActor != null)
{
float ground = (float)llGround(new LSL_Types.Vector3(0, 0, 0));
float waterLevel = (float)llWater(new LSL_Types.Vector3(0, 0, 0));
PIDHoverType hoverType = PIDHoverType.Ground;
if (water != 0)
{
hoverType = PIDHoverType.GroundAndWater;
if (ground < waterLevel)
height += waterLevel;
else
height += ground;
}
else
{
height += ground;
}
m_host.SetHoverHeight((float)height, hoverType, (float)tau);
}
}
protected UUID GetTaskInventoryItem(string name)
{
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
m_host.TaskInventory.LockItemsForRead(false);
return inv.Key;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero;
}
public void llGiveInventoryList(string destination, string category, LSL_List inventory)
{
m_host.AddScriptLPS(1);
UUID destID;
if (!UUID.TryParse(destination, out destID))
return;
List itemList = new List();
foreach (Object item in inventory.Data)
{
UUID itemID;
if (UUID.TryParse(item.ToString(), out itemID))
{
itemList.Add(itemID);
}
else
{
itemID = GetTaskInventoryItem(item.ToString());
if (itemID != UUID.Zero)
itemList.Add(itemID);
}
}
if (itemList.Count == 0)
return;
UUID folderID = m_ScriptEngine.World.MoveTaskInventoryItems(destID, category, m_host, itemList);
if (folderID == UUID.Zero)
return;
byte[] bucket = new byte[17];
bucket[0] = (byte)AssetType.Folder;
byte[] objBytes = folderID.GetBytes();
Array.Copy(objBytes, 0, bucket, 1, 16);
GridInstantMessage msg = new GridInstantMessage(World,
m_host.UUID, m_host.Name+", an object owned by "+
resolveName(m_host.OwnerID)+",", destID,
(byte)InstantMessageDialog.InventoryOffered,
false, category+"\n"+m_host.Name+" is located at "+
World.RegionInfo.RegionName+" "+
m_host.AbsolutePosition.ToString(),
folderID, true, m_host.AbsolutePosition,
bucket);
if (m_TransferModule != null)
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
}
public void llSetVehicleType(int type)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleType(type);
}
}
//CFK 9/28: Most, but not all of the underlying plumbing between here and the physics modules is in
//CFK 9/28: so these are not complete yet.
public void llSetVehicleFloatParam(int param, LSL_Float value)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleFloatParam(param, (float)value);
}
}
//CFK 9/28: Most, but not all of the underlying plumbing between here and the physics modules is in
//CFK 9/28: so these are not complete yet.
public void llSetVehicleVectorParam(int param, LSL_Vector vec)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleVectorParam(param,
new Vector3((float)vec.x, (float)vec.y, (float)vec.z));
}
}
//CFK 9/28: Most, but not all of the underlying plumbing between here and the physics modules is in
//CFK 9/28: so these are not complete yet.
public void llSetVehicleRotationParam(int param, LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleRotationParam(param, Rot2Quaternion(rot));
}
}
public void llSetVehicleFlags(int flags)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleFlags(flags, false);
}
}
public void llRemoveVehicleFlags(int flags)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
{
m_host.ParentGroup.RootPart.SetVehicleFlags(flags, true);
}
}
public void llSitTarget(LSL_Vector offset, LSL_Rotation rot)
{
m_host.AddScriptLPS(1);
// LSL quaternions can normalize to 0, normal Quaternions can't.
if (rot.s == 0 && rot.x == 0 && rot.y == 0 && rot.z == 0)
rot.z = 1; // ZERO_ROTATION = 0,0,0,1
m_host.SitTargetPosition = new Vector3((float)offset.x, (float)offset.y, (float)offset.z);
m_host.SitTargetOrientation = Rot2Quaternion(rot);
m_host.ParentGroup.HasGroupChanged = true;
}
public LSL_String llAvatarOnSitTarget()
{
m_host.AddScriptLPS(1);
return m_host.GetAvatarOnSitTarget().ToString();
}
// http://wiki.secondlife.com/wiki/LlAvatarOnLinkSitTarget
public LSL_String llAvatarOnLinkSitTarget(int linknum)
{
m_host.AddScriptLPS(1);
if(linknum == ScriptBaseClass.LINK_SET ||
linknum == ScriptBaseClass.LINK_ALL_CHILDREN ||
linknum == ScriptBaseClass.LINK_ALL_OTHERS) return UUID.Zero.ToString();
List parts = GetLinkParts(linknum);
if (parts.Count == 0) return UUID.Zero.ToString();
return parts[0].SitTargetAvatar.ToString();
}
public void llAddToLandPassList(string avatar, double hours)
{
m_host.AddScriptLPS(1);
UUID key;
ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageAllowed))
{
ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry();
if (UUID.TryParse(avatar, out key))
{
if (land.LandData.ParcelAccessList.FindIndex(
delegate(ParcelManager.ParcelAccessEntry e)
{
if (e.AgentID == key && e.Flags == AccessList.Access)
return true;
return false;
}) == -1)
{
entry.AgentID = key;
entry.Flags = AccessList.Access;
entry.Time = DateTime.Now.AddHours(hours);
land.LandData.ParcelAccessList.Add(entry);
}
}
}
ScriptSleep(100);
}
public void llSetTouchText(string text)
{
m_host.AddScriptLPS(1);
m_host.TouchName = text;
}
public void llSetSitText(string text)
{
m_host.AddScriptLPS(1);
m_host.SitName = text;
}
public void llSetCameraEyeOffset(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
m_host.SetCameraEyeOffset(new Vector3((float)offset.x, (float)offset.y, (float)offset.z));
}
public void llSetCameraAtOffset(LSL_Vector offset)
{
m_host.AddScriptLPS(1);
m_host.SetCameraAtOffset(new Vector3((float)offset.x, (float)offset.y, (float)offset.z));
}
public LSL_String llDumpList2String(LSL_List src, string seperator)
{
m_host.AddScriptLPS(1);
if (src.Length == 0)
{
return String.Empty;
}
string ret = String.Empty;
foreach (object o in src.Data)
{
ret = ret + o.ToString() + seperator;
}
ret = ret.Substring(0, ret.Length - seperator.Length);
return ret;
}
public LSL_Integer llScriptDanger(LSL_Vector pos)
{
m_host.AddScriptLPS(1);
bool result = World.ScriptDanger(m_host.LocalId, new Vector3((float)pos.x, (float)pos.y, (float)pos.z));
if (result)
{
return 1;
}
else
{
return 0;
}
}
public void llDialog(string avatar, string message, LSL_List buttons, int chat_channel)
{
IDialogModule dm = World.RequestModuleInterface();
if (dm == null)
return;
m_host.AddScriptLPS(1);
UUID av = new UUID();
if (!UUID.TryParse(avatar,out av))
{
//LSLError("First parameter to llDialog needs to be a key");
return;
}
if (buttons.Length < 1)
{
buttons.Add("OK");
}
if (buttons.Length > 12)
{
LSLError("No more than 12 buttons can be shown");
return;
}
string[] buts = new string[buttons.Length];
for (int i = 0; i < buttons.Length; i++)
{
if (buttons.Data[i].ToString() == String.Empty)
{
LSLError("button label cannot be blank");
return;
}
if (buttons.Data[i].ToString().Length > 24)
{
llWhisper(ScriptBaseClass.DEBUG_CHANNEL, "button label cannot be longer than 24 characters");
return;
}
buts[i] = buttons.Data[i].ToString();
}
dm.SendDialogToUser(
av, m_host.Name, m_host.UUID, m_host.OwnerID,
message, new UUID("00000000-0000-2222-3333-100000001000"), chat_channel, buts);
ScriptSleep(1000);
}
public void llVolumeDetect(int detect)
{
m_host.AddScriptLPS(1);
if (!m_host.ParentGroup.IsDeleted)
m_host.ParentGroup.ScriptSetVolumeDetect(detect != 0);
}
///
/// This is a depecated function so this just replicates the result of
/// invoking it in SL
///
public void llRemoteLoadScript(string target, string name, int running, int start_param)
{
m_host.AddScriptLPS(1);
// Report an error as it does in SL
ShoutError("Deprecated. Please use llRemoteLoadScriptPin instead.");
ScriptSleep(3000);
}
public void llSetRemoteScriptAccessPin(int pin)
{
m_host.AddScriptLPS(1);
m_host.ScriptAccessPin = pin;
}
public void llRemoteLoadScriptPin(string target, string name, int pin, int running, int start_param)
{
m_host.AddScriptLPS(1);
bool found = false;
UUID destId = UUID.Zero;
UUID srcId = UUID.Zero;
if (!UUID.TryParse(target, out destId))
{
llSay(0, "Could not parse key " + target);
return;
}
// target must be a different prim than the one containing the script
if (m_host.UUID == destId)
{
return;
}
// copy the first script found with this inventory name
TaskInventoryItem scriptItem = null;
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
// make sure the object is a script
if (10 == inv.Value.Type)
{
found = true;
srcId = inv.Key;
scriptItem = inv.Value;
break;
}
}
}
m_host.TaskInventory.LockItemsForRead(false);
if (!found)
{
llSay(0, "Could not find script " + name);
return;
}
SceneObjectPart dest = World.GetSceneObjectPart(destId);
if (dest != null)
{
if ((scriptItem.BasePermissions & (uint)PermissionMask.Transfer) != 0 || dest.ParentGroup.RootPart.OwnerID == m_host.ParentGroup.RootPart.OwnerID)
{
// the rest of the permission checks are done in RezScript, so check the pin there as well
World.RezScript(srcId, m_host, destId, pin, running, start_param);
if ((scriptItem.BasePermissions & (uint)PermissionMask.Copy) == 0)
m_host.Inventory.RemoveInventoryItem(srcId);
}
}
// this will cause the delay even if the script pin or permissions were wrong - seems ok
ScriptSleep(3000);
}
public void llOpenRemoteDataChannel()
{
m_host.AddScriptLPS(1);
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface();
if (xmlrpcMod.IsEnabled())
{
UUID channelID = xmlrpcMod.OpenXMLRPCChannel(m_localID, m_itemID, UUID.Zero);
IXmlRpcRouter xmlRpcRouter = m_ScriptEngine.World.RequestModuleInterface();
if (xmlRpcRouter != null)
{
string ExternalHostName = m_ScriptEngine.World.RegionInfo.ExternalHostName;
xmlRpcRouter.RegisterNewReceiver(m_ScriptEngine.ScriptModule, channelID, m_host.UUID,
m_itemID, String.Format("http://{0}:{1}/", ExternalHostName,
xmlrpcMod.Port.ToString()));
}
object[] resobj = new object[]
{
new LSL_Integer(1),
new LSL_String(channelID.ToString()),
new LSL_String(UUID.Zero.ToString()),
new LSL_String(String.Empty),
new LSL_Integer(0),
new LSL_String(String.Empty)
};
m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams("remote_data", resobj,
new DetectParams[0]));
}
ScriptSleep(1000);
}
public LSL_String llSendRemoteData(string channel, string dest, int idata, string sdata)
{
m_host.AddScriptLPS(1);
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface();
ScriptSleep(3000);
return (xmlrpcMod.SendRemoteData(m_localID, m_itemID, channel, dest, idata, sdata)).ToString();
}
public void llRemoteDataReply(string channel, string message_id, string sdata, int idata)
{
m_host.AddScriptLPS(1);
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface();
xmlrpcMod.RemoteDataReply(channel, message_id, sdata, idata);
ScriptSleep(3000);
}
public void llCloseRemoteDataChannel(string channel)
{
m_host.AddScriptLPS(1);
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface();
xmlrpcMod.CloseXMLRPCChannel((UUID)channel);
ScriptSleep(1000);
}
public LSL_String llMD5String(string src, int nonce)
{
m_host.AddScriptLPS(1);
return Util.Md5Hash(String.Format("{0}:{1}", src, nonce.ToString()), Encoding.UTF8);
}
public LSL_String llSHA1String(string src)
{
m_host.AddScriptLPS(1);
return Util.SHA1Hash(src, Encoding.UTF8).ToLower();
}
protected ObjectShapePacket.ObjectDataBlock SetPrimitiveBlockShapeParams(SceneObjectPart part, int holeshape, LSL_Vector cut, float hollow, LSL_Vector twist, byte profileshape, byte pathcurve)
{
float tempFloat; // Use in float expressions below to avoid byte cast precision issues.
ObjectShapePacket.ObjectDataBlock shapeBlock = new ObjectShapePacket.ObjectDataBlock();
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return shapeBlock;
if (holeshape != (int)ScriptBaseClass.PRIM_HOLE_DEFAULT &&
holeshape != (int)ScriptBaseClass.PRIM_HOLE_CIRCLE &&
holeshape != (int)ScriptBaseClass.PRIM_HOLE_SQUARE &&
holeshape != (int)ScriptBaseClass.PRIM_HOLE_TRIANGLE)
{
holeshape = (int)ScriptBaseClass.PRIM_HOLE_DEFAULT;
}
shapeBlock.PathCurve = pathcurve;
shapeBlock.ProfileCurve = (byte)holeshape; // Set the hole shape.
shapeBlock.ProfileCurve += profileshape; // Add in the profile shape.
if (cut.x < 0f)
{
cut.x = 0f;
}
if (cut.x > 1f)
{
cut.x = 1f;
}
if (cut.y < 0f)
{
cut.y = 0f;
}
if (cut.y > 1f)
{
cut.y = 1f;
}
if (cut.y - cut.x < 0.05f)
{
cut.x = cut.y - 0.05f;
if (cut.x < 0.0f)
{
cut.x = 0.0f;
cut.y = 0.05f;
}
}
shapeBlock.ProfileBegin = (ushort)(50000 * cut.x);
shapeBlock.ProfileEnd = (ushort)(50000 * (1 - cut.y));
if (hollow < 0f)
{
hollow = 0f;
}
// If the prim is a Cylinder, Prism, Sphere, Torus or Ring (or not a
// Box or Tube) and the hole shape is a square, hollow is limited to
// a max of 70%. The viewer performs its own check on this value but
// we need to do it here also so llGetPrimitiveParams can have access
// to the correct value.
if (profileshape != (byte)ProfileCurve.Square &&
holeshape == (int)ScriptBaseClass.PRIM_HOLE_SQUARE)
{
if (hollow > 0.70f)
{
hollow = 0.70f;
}
}
// Otherwise, hollow is limited to 95%.
else
{
if (hollow > 0.95f)
{
hollow = 0.95f;
}
}
shapeBlock.ProfileHollow = (ushort)(50000 * hollow);
if (twist.x < -1.0f)
{
twist.x = -1.0f;
}
if (twist.x > 1.0f)
{
twist.x = 1.0f;
}
if (twist.y < -1.0f)
{
twist.y = -1.0f;
}
if (twist.y > 1.0f)
{
twist.y = 1.0f;
}
// A fairly large precision error occurs for some calculations,
// if a float or double is directly cast to a byte or sbyte
// variable, in both .Net and Mono. In .Net, coding
// "(sbyte)(float)(some expression)" corrects the precision
// errors. But this does not work for Mono. This longer coding
// form of creating a tempoary float variable from the
// expression first, then casting that variable to a byte or
// sbyte, works for both .Net and Mono. These types of
// assignments occur in SetPrimtiveBlockShapeParams and
// SetPrimitiveShapeParams in support of llSetPrimitiveParams.
tempFloat = (float)(100.0d * twist.x);
shapeBlock.PathTwistBegin = (sbyte)tempFloat;
tempFloat = (float)(100.0d * twist.y);
shapeBlock.PathTwist = (sbyte)tempFloat;
shapeBlock.ObjectLocalID = part.LocalId;
part.Shape.SculptEntry = false;
return shapeBlock;
}
// Prim type box, cylinder and prism.
protected void SetPrimitiveShapeParams(SceneObjectPart part, int holeshape, LSL_Vector cut, float hollow, LSL_Vector twist, LSL_Vector taper_b, LSL_Vector topshear, byte profileshape, byte pathcurve)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
float tempFloat; // Use in float expressions below to avoid byte cast precision issues.
ObjectShapePacket.ObjectDataBlock shapeBlock;
shapeBlock = SetPrimitiveBlockShapeParams(part, holeshape, cut, hollow, twist, profileshape, pathcurve);
if (taper_b.x < 0f)
{
taper_b.x = 0f;
}
if (taper_b.x > 2f)
{
taper_b.x = 2f;
}
if (taper_b.y < 0f)
{
taper_b.y = 0f;
}
if (taper_b.y > 2f)
{
taper_b.y = 2f;
}
tempFloat = (float)(100.0d * (2.0d - taper_b.x));
shapeBlock.PathScaleX = (byte)tempFloat;
tempFloat = (float)(100.0d * (2.0d - taper_b.y));
shapeBlock.PathScaleY = (byte)tempFloat;
if (topshear.x < -0.5f)
{
topshear.x = -0.5f;
}
if (topshear.x > 0.5f)
{
topshear.x = 0.5f;
}
if (topshear.y < -0.5f)
{
topshear.y = -0.5f;
}
if (topshear.y > 0.5f)
{
topshear.y = 0.5f;
}
tempFloat = (float)(100.0d * topshear.x);
shapeBlock.PathShearX = (byte)tempFloat;
tempFloat = (float)(100.0d * topshear.y);
shapeBlock.PathShearY = (byte)tempFloat;
part.Shape.SculptEntry = false;
part.UpdateShape(shapeBlock);
}
// Prim type sphere.
protected void SetPrimitiveShapeParams(SceneObjectPart part, int holeshape, LSL_Vector cut, float hollow, LSL_Vector twist, LSL_Vector dimple, byte profileshape, byte pathcurve)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
ObjectShapePacket.ObjectDataBlock shapeBlock;
shapeBlock = SetPrimitiveBlockShapeParams(part, holeshape, cut, hollow, twist, profileshape, pathcurve);
// profile/path swapped for a sphere
shapeBlock.PathBegin = shapeBlock.ProfileBegin;
shapeBlock.PathEnd = shapeBlock.ProfileEnd;
shapeBlock.PathScaleX = 100;
shapeBlock.PathScaleY = 100;
if (dimple.x < 0f)
{
dimple.x = 0f;
}
if (dimple.x > 1f)
{
dimple.x = 1f;
}
if (dimple.y < 0f)
{
dimple.y = 0f;
}
if (dimple.y > 1f)
{
dimple.y = 1f;
}
if (dimple.y - cut.x < 0.05f)
{
dimple.x = cut.y - 0.05f;
}
shapeBlock.ProfileBegin = (ushort)(50000 * dimple.x);
shapeBlock.ProfileEnd = (ushort)(50000 * (1 - dimple.y));
part.Shape.SculptEntry = false;
part.UpdateShape(shapeBlock);
}
// Prim type torus, tube and ring.
protected void SetPrimitiveShapeParams(SceneObjectPart part, int holeshape, LSL_Vector cut, float hollow, LSL_Vector twist, LSL_Vector holesize, LSL_Vector topshear, LSL_Vector profilecut, LSL_Vector taper_a, float revolutions, float radiusoffset, float skew, byte profileshape, byte pathcurve)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
float tempFloat; // Use in float expressions below to avoid byte cast precision issues.
ObjectShapePacket.ObjectDataBlock shapeBlock;
shapeBlock = SetPrimitiveBlockShapeParams(part, holeshape, cut, hollow, twist, profileshape, pathcurve);
// profile/path swapped for a torrus, tube, ring
shapeBlock.PathBegin = shapeBlock.ProfileBegin;
shapeBlock.PathEnd = shapeBlock.ProfileEnd;
if (holesize.x < 0.05f)
{
holesize.x = 0.05f;
}
if (holesize.x > 1f)
{
holesize.x = 1f;
}
if (holesize.y < 0.05f)
{
holesize.y = 0.05f;
}
if (holesize.y > 0.5f)
{
holesize.y = 0.5f;
}
tempFloat = (float)(100.0d * (2.0d - holesize.x));
shapeBlock.PathScaleX = (byte)tempFloat;
tempFloat = (float)(100.0d * (2.0d - holesize.y));
shapeBlock.PathScaleY = (byte)tempFloat;
if (topshear.x < -0.5f)
{
topshear.x = -0.5f;
}
if (topshear.x > 0.5f)
{
topshear.x = 0.5f;
}
if (topshear.y < -0.5f)
{
topshear.y = -0.5f;
}
if (topshear.y > 0.5f)
{
topshear.y = 0.5f;
}
tempFloat = (float)(100.0d * topshear.x);
shapeBlock.PathShearX = (byte)tempFloat;
tempFloat = (float)(100.0d * topshear.y);
shapeBlock.PathShearY = (byte)tempFloat;
if (profilecut.x < 0f)
{
profilecut.x = 0f;
}
if (profilecut.x > 1f)
{
profilecut.x = 1f;
}
if (profilecut.y < 0f)
{
profilecut.y = 0f;
}
if (profilecut.y > 1f)
{
profilecut.y = 1f;
}
if (profilecut.y - profilecut.x < 0.05f)
{
profilecut.x = profilecut.y - 0.05f;
if (profilecut.x < 0.0f)
{
profilecut.x = 0.0f;
profilecut.y = 0.05f;
}
}
shapeBlock.ProfileBegin = (ushort)(50000 * profilecut.x);
shapeBlock.ProfileEnd = (ushort)(50000 * (1 - profilecut.y));
if (taper_a.x < -1f)
{
taper_a.x = -1f;
}
if (taper_a.x > 1f)
{
taper_a.x = 1f;
}
if (taper_a.y < -1f)
{
taper_a.y = -1f;
}
if (taper_a.y > 1f)
{
taper_a.y = 1f;
}
tempFloat = (float)(100.0d * taper_a.x);
shapeBlock.PathTaperX = (sbyte)tempFloat;
tempFloat = (float)(100.0d * taper_a.y);
shapeBlock.PathTaperY = (sbyte)tempFloat;
if (revolutions < 1f)
{
revolutions = 1f;
}
if (revolutions > 4f)
{
revolutions = 4f;
}
tempFloat = 66.66667f * (revolutions - 1.0f);
shapeBlock.PathRevolutions = (byte)tempFloat;
// limits on radiusoffset depend on revolutions and hole size (how?) seems like the maximum range is 0 to 1
if (radiusoffset < 0f)
{
radiusoffset = 0f;
}
if (radiusoffset > 1f)
{
radiusoffset = 1f;
}
tempFloat = 100.0f * radiusoffset;
shapeBlock.PathRadiusOffset = (sbyte)tempFloat;
if (skew < -0.95f)
{
skew = -0.95f;
}
if (skew > 0.95f)
{
skew = 0.95f;
}
tempFloat = 100.0f * skew;
shapeBlock.PathSkew = (sbyte)tempFloat;
part.Shape.SculptEntry = false;
part.UpdateShape(shapeBlock);
}
// Prim type sculpt.
protected void SetPrimitiveShapeParams(SceneObjectPart part, string map, int type, byte pathcurve)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
ObjectShapePacket.ObjectDataBlock shapeBlock = new ObjectShapePacket.ObjectDataBlock();
UUID sculptId;
if (!UUID.TryParse(map, out sculptId))
{
sculptId = InventoryKey(map, (int)AssetType.Texture);
}
if (sculptId == UUID.Zero)
return;
shapeBlock.PathCurve = pathcurve;
shapeBlock.ObjectLocalID = part.LocalId;
shapeBlock.PathScaleX = 100;
shapeBlock.PathScaleY = 150;
if ((type & (int)ScriptBaseClass.PRIM_SCULPT_TYPE_CYLINDER) == 0 &&
(type & (int)ScriptBaseClass.PRIM_SCULPT_TYPE_PLANE) == 0 &&
(type & (int)ScriptBaseClass.PRIM_SCULPT_TYPE_SPHERE) == 0 &&
(type & (int)ScriptBaseClass.PRIM_SCULPT_TYPE_TORUS) == 0)
{
// default
type = type | (int)ScriptBaseClass.PRIM_SCULPT_TYPE_SPHERE;
}
part.Shape.SetSculptProperties((byte)type, sculptId);
part.Shape.SculptEntry = true;
part.UpdateShape(shapeBlock);
}
public void llSetPrimitiveParams(LSL_List rules)
{
m_host.AddScriptLPS(1);
SetPrimParams(m_host, rules);
ScriptSleep(200);
}
public void llSetLinkPrimitiveParamsFast(int linknumber, LSL_List rules)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
List avatars = GetLinkAvatars(linknumber);
if (parts.Count>0)
{
try
{
parts[0].ParentGroup.areUpdatesSuspended = true;
foreach (SceneObjectPart part in parts)
SetPrimParams(part, rules);
}
finally
{
parts[0].ParentGroup.areUpdatesSuspended = false;
}
}
if (avatars.Count > 0)
{
foreach (ScenePresence avatar in avatars)
SetPrimParams(avatar, rules);
}
}
public void llSetLinkPrimitiveParams(int linknumber, LSL_List rules)
{
llSetLinkPrimitiveParamsFast(linknumber, rules);
ScriptSleep(200);
}
protected void SetPrimParams(ScenePresence av, LSL_List rules)
{
//This is a special version of SetPrimParams to deal with avatars which are sat on the linkset.
//We only support PRIM_POSITION and PRIM_ROTATION
int idx = 0;
while (idx < rules.Length)
{
int code = rules.GetLSLIntegerItem(idx++);
int remain = rules.Length - idx;
switch (code)
{
case (int)ScriptBaseClass.PRIM_POSITION:
if (remain < 1)
return;
LSL_Vector v;
v = rules.GetVector3Item(idx++);
av.AbsolutePosition = new Vector3((float)v.x, (float)v.y, (float)v.z);
av.SendAvatarDataToAllAgents();
break;
case (int)ScriptBaseClass.PRIM_ROTATION:
if (remain < 1)
return;
LSL_Rotation r;
r = rules.GetQuaternionItem(idx++);
av.OffsetRotation = new Quaternion((float)r.x, (float)r.y, (float)r.z, (float)r.s);
av.SendAvatarDataToAllAgents();
break;
}
}
}
protected void SetPrimParams(SceneObjectPart part, LSL_List rules)
{
if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted)
return;
int idx = 0;
bool positionChanged = false;
LSL_Vector currentPosition = GetPartLocalPos(part);
while (idx < rules.Length)
{
int code = rules.GetLSLIntegerItem(idx++);
int remain = rules.Length - idx;
int face;
LSL_Vector v;
switch (code)
{
case (int)ScriptBaseClass.PRIM_POSITION:
if (remain < 1)
return;
v=rules.GetVector3Item(idx++);
positionChanged = true;
currentPosition = GetSetPosTarget(part, v, currentPosition);
break;
case (int)ScriptBaseClass.PRIM_SIZE:
if (remain < 1)
return;
v=rules.GetVector3Item(idx++);
SetScale(part, v);
break;
case (int)ScriptBaseClass.PRIM_ROTATION:
if (remain < 1)
return;
LSL_Rotation q = rules.GetQuaternionItem(idx++);
// try to let this work as in SL...
if (part.ParentID == 0)
{
// special case: If we are root, rotate complete SOG to new rotation
SetRot(part, Rot2Quaternion(q));
}
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 = part.ParentGroup.RootPart;
SetRot(part, rootPart.RotationOffset * Rot2Quaternion(q));
}
break;
case (int)ScriptBaseClass.PRIM_TYPE:
if (remain < 3)
return;
code = (int)rules.GetLSLIntegerItem(idx++);
remain = rules.Length - idx;
float hollow;
LSL_Vector twist;
LSL_Vector taper_b;
LSL_Vector topshear;
float revolutions;
float radiusoffset;
float skew;
LSL_Vector holesize;
LSL_Vector profilecut;
switch (code)
{
case (int)ScriptBaseClass.PRIM_TYPE_BOX:
if (remain < 6)
return;
face = (int)rules.GetLSLIntegerItem(idx++);
v = rules.GetVector3Item(idx++); // cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear,
(byte)ProfileShape.Square, (byte)Extrusion.Straight);
break;
case (int)ScriptBaseClass.PRIM_TYPE_CYLINDER:
if (remain < 6)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); // cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear,
(byte)ProfileShape.Circle, (byte)Extrusion.Straight);
break;
case (int)ScriptBaseClass.PRIM_TYPE_PRISM:
if (remain < 6)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear,
(byte)ProfileShape.EquilateralTriangle, (byte)Extrusion.Straight);
break;
case (int)ScriptBaseClass.PRIM_TYPE_SPHERE:
if (remain < 5)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); // cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++); // dimple
SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b,
(byte)ProfileShape.HalfCircle, (byte)Extrusion.Curve1);
break;
case (int)ScriptBaseClass.PRIM_TYPE_TORUS:
if (remain < 11)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
holesize = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
profilecut = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++); // taper_a
revolutions = (float)rules.GetLSLFloatItem(idx++);
radiusoffset = (float)rules.GetLSLFloatItem(idx++);
skew = (float)rules.GetLSLFloatItem(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b,
revolutions, radiusoffset, skew, (byte)ProfileShape.Circle, (byte)Extrusion.Curve1);
break;
case (int)ScriptBaseClass.PRIM_TYPE_TUBE:
if (remain < 11)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
holesize = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
profilecut = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++); // taper_a
revolutions = (float)rules.GetLSLFloatItem(idx++);
radiusoffset = (float)rules.GetLSLFloatItem(idx++);
skew = (float)rules.GetLSLFloatItem(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b,
revolutions, radiusoffset, skew, (byte)ProfileShape.Square, (byte)Extrusion.Curve1);
break;
case (int)ScriptBaseClass.PRIM_TYPE_RING:
if (remain < 11)
return;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
hollow = (float)rules.GetLSLFloatItem(idx++);
twist = rules.GetVector3Item(idx++);
holesize = rules.GetVector3Item(idx++);
topshear = rules.GetVector3Item(idx++);
profilecut = rules.GetVector3Item(idx++);
taper_b = rules.GetVector3Item(idx++); // taper_a
revolutions = (float)rules.GetLSLFloatItem(idx++);
radiusoffset = (float)rules.GetLSLFloatItem(idx++);
skew = (float)rules.GetLSLFloatItem(idx++);
SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b,
revolutions, radiusoffset, skew, (byte)ProfileShape.EquilateralTriangle, (byte)Extrusion.Curve1);
break;
case (int)ScriptBaseClass.PRIM_TYPE_SCULPT:
if (remain < 2)
return;
string map = rules.Data[idx++].ToString();
face = (int)rules.GetLSLIntegerItem(idx++); // type
SetPrimitiveShapeParams(part, map, face, (byte)Extrusion.Curve1);
break;
}
break;
case (int)ScriptBaseClass.PRIM_TEXTURE:
if (remain < 5)
return;
face=(int)rules.GetLSLIntegerItem(idx++);
string tex=rules.Data[idx++].ToString();
LSL_Vector repeats=rules.GetVector3Item(idx++);
LSL_Vector offsets=rules.GetVector3Item(idx++);
double rotation=(double)rules.GetLSLFloatItem(idx++);
SetTexture(part, tex, face);
ScaleTexture(part, repeats.x, repeats.y, face);
OffsetTexture(part, offsets.x, offsets.y, face);
RotateTexture(part, rotation, face);
break;
case (int)ScriptBaseClass.PRIM_COLOR:
if (remain < 3)
return;
face=(int)rules.GetLSLIntegerItem(idx++);
LSL_Vector color=rules.GetVector3Item(idx++);
double alpha=(double)rules.GetLSLFloatItem(idx++);
part.SetFaceColor(new Vector3((float)color.x, (float)color.y, (float)color.z), face);
SetAlpha(part, alpha, face);
break;
case (int)ScriptBaseClass.PRIM_FLEXIBLE:
if (remain < 7)
return;
bool flexi = rules.GetLSLIntegerItem(idx++);
int softness = rules.GetLSLIntegerItem(idx++);
float gravity = (float)rules.GetLSLFloatItem(idx++);
float friction = (float)rules.GetLSLFloatItem(idx++);
float wind = (float)rules.GetLSLFloatItem(idx++);
float tension = (float)rules.GetLSLFloatItem(idx++);
LSL_Vector force = rules.GetVector3Item(idx++);
SetFlexi(part, flexi, softness, gravity, friction, wind, tension, force);
break;
case (int)ScriptBaseClass.PRIM_POINT_LIGHT:
if (remain < 5)
return;
bool light = rules.GetLSLIntegerItem(idx++);
LSL_Vector lightcolor = rules.GetVector3Item(idx++);
float intensity = (float)rules.GetLSLFloatItem(idx++);
float radius = (float)rules.GetLSLFloatItem(idx++);
float falloff = (float)rules.GetLSLFloatItem(idx++);
SetPointLight(part, light, lightcolor, intensity, radius, falloff);
break;
case (int)ScriptBaseClass.PRIM_GLOW:
if (remain < 2)
return;
face = rules.GetLSLIntegerItem(idx++);
float glow = (float)rules.GetLSLFloatItem(idx++);
SetGlow(part, face, glow);
break;
case (int)ScriptBaseClass.PRIM_BUMP_SHINY:
if (remain < 3)
return;
face = (int)rules.GetLSLIntegerItem(idx++);
int shiny = (int)rules.GetLSLIntegerItem(idx++);
Bumpiness bump = (Bumpiness)Convert.ToByte((int)rules.GetLSLIntegerItem(idx++));
SetShiny(part, face, shiny, bump);
break;
case (int)ScriptBaseClass.PRIM_FULLBRIGHT:
if (remain < 2)
return;
face = rules.GetLSLIntegerItem(idx++);
bool st = rules.GetLSLIntegerItem(idx++);
SetFullBright(part, face , st);
break;
case (int)ScriptBaseClass.PRIM_MATERIAL:
if (remain < 1)
return;
int mat = rules.GetLSLIntegerItem(idx++);
if (mat < 0 || mat > 7)
return;
part.Material = Convert.ToByte(mat);
break;
case (int)ScriptBaseClass.PRIM_PHANTOM:
if (remain < 1)
return;
string ph = rules.Data[idx++].ToString();
m_host.ParentGroup.ScriptSetPhantomStatus(ph.Equals("1"));
break;
case (int)ScriptBaseClass.PRIM_PHYSICS:
if (remain < 1)
return;
string phy = rules.Data[idx++].ToString();
bool physics;
if (phy.Equals("1"))
physics = true;
else
physics = false;
part.ScriptSetPhysicsStatus(physics);
break;
case (int)ScriptBaseClass.PRIM_TEMP_ON_REZ:
if (remain < 1)
return;
string temp = rules.Data[idx++].ToString();
m_host.ParentGroup.ScriptSetTemporaryStatus(temp.Equals("1"));
break;
case (int)ScriptBaseClass.PRIM_TEXGEN:
if (remain < 2)
return;
//face,type
face = rules.GetLSLIntegerItem(idx++);
int style = rules.GetLSLIntegerItem(idx++);
SetTexGen(part, face, style);
break;
case (int)ScriptBaseClass.PRIM_TEXT:
if (remain < 3)
return;
string primText = rules.GetLSLStringItem(idx++);
LSL_Vector primTextColor = rules.GetVector3Item(idx++);
LSL_Float primTextAlpha = rules.GetLSLFloatItem(idx++);
Vector3 av3 = new Vector3(Util.Clip((float)primTextColor.x, 0.0f, 1.0f),
Util.Clip((float)primTextColor.y, 0.0f, 1.0f),
Util.Clip((float)primTextColor.z, 0.0f, 1.0f));
part.SetText(primText, av3, Util.Clip((float)primTextAlpha, 0.0f, 1.0f));
break;
case (int)ScriptBaseClass.PRIM_NAME:
if (remain < 1)
return;
string primName = rules.GetLSLStringItem(idx++);
part.Name = primName;
break;
case (int)ScriptBaseClass.PRIM_DESC:
if (remain < 1)
return;
string primDesc = rules.GetLSLStringItem(idx++);
part.Description = primDesc;
break;
case (int)ScriptBaseClass.PRIM_ROT_LOCAL:
if (remain < 1)
return;
LSL_Rotation lr = rules.GetQuaternionItem(idx++);
SetRot(part, Rot2Quaternion(lr));
break;
}
}
if (positionChanged)
{
if (part.ParentGroup.RootPart == part)
{
SceneObjectGroup parent = part.ParentGroup;
parent.UpdateGroupPosition(new Vector3((float)currentPosition.x, (float)currentPosition.y, (float)currentPosition.z));
}
else
{
part.OffsetPosition = new Vector3((float)currentPosition.x, (float)currentPosition.y, (float)currentPosition.z);
SceneObjectGroup parent = part.ParentGroup;
parent.HasGroupChanged = true;
parent.ScheduleGroupForTerseUpdate();
}
}
}
public LSL_String llStringToBase64(string str)
{
m_host.AddScriptLPS(1);
try
{
byte[] encData_byte = new byte[str.Length];
encData_byte = Util.UTF8.GetBytes(str);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
}
catch (Exception e)
{
throw new Exception("Error in base64Encode" + e.Message);
}
}
public LSL_String llBase64ToString(string str)
{
m_host.AddScriptLPS(1);
try
{
return Util.Base64ToString(str);
}
catch (Exception e)
{
throw new Exception("Error in base64Decode" + e.Message);
}
}
public LSL_String llXorBase64Strings(string str1, string str2)
{
m_host.AddScriptLPS(1);
Deprecated("llXorBase64Strings");
ScriptSleep(300);
return String.Empty;
}
public void llRemoteDataSetRegion()
{
m_host.AddScriptLPS(1);
Deprecated("llRemoteDataSetRegion");
}
public LSL_Float llLog10(double val)
{
m_host.AddScriptLPS(1);
return (double)Math.Log10(val);
}
public LSL_Float llLog(double val)
{
m_host.AddScriptLPS(1);
return (double)Math.Log(val);
}
public LSL_List llGetAnimationList(string id)
{
m_host.AddScriptLPS(1);
LSL_List l = new LSL_List();
ScenePresence av = World.GetScenePresence((UUID)id);
if (av == null || av.IsChildAgent) // only if in the region
return l;
UUID[] anims;
anims = av.Animator.GetAnimationArray();
foreach (UUID foo in anims)
l.Add(new LSL_Key(foo.ToString()));
return l;
}
public void llSetParcelMusicURL(string url)
{
m_host.AddScriptLPS(1);
ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (land.LandData.OwnerID != m_host.OwnerID)
return;
land.SetMusicUrl(url);
ScriptSleep(2000);
}
public LSL_Vector llGetRootPosition()
{
m_host.AddScriptLPS(1);
return new LSL_Vector(m_host.ParentGroup.AbsolutePosition.X, m_host.ParentGroup.AbsolutePosition.Y,
m_host.ParentGroup.AbsolutePosition.Z);
}
///
/// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetRot
/// http://lslwiki.net/lslwiki/wakka.php?wakka=ChildRotation
/// Also tested in sl in regards to the behaviour in attachments/mouselook
/// In the root prim:-
/// Returns the object rotation if not attached
/// Returns the avatars rotation if attached
/// Returns the camera rotation if attached and the avatar is in mouselook
///
public LSL_Rotation llGetRootRotation()
{
m_host.AddScriptLPS(1);
Quaternion q;
if (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; // Mouselook
else
q = avatar.Rotation; // Currently infrequently updated so may be inaccurate
else
q = m_host.ParentGroup.GroupRotation; // Likely never get here but just in case
}
else
q = m_host.ParentGroup.GroupRotation; // just the group rotation
return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
}
public LSL_String llGetObjectDesc()
{
return m_host.Description!=null?m_host.Description:String.Empty;
}
public void llSetObjectDesc(string desc)
{
m_host.AddScriptLPS(1);
m_host.Description = desc!=null?desc:String.Empty;
}
public LSL_String llGetCreator()
{
m_host.AddScriptLPS(1);
return m_host.CreatorID.ToString();
}
public LSL_String llGetTimestamp()
{
m_host.AddScriptLPS(1);
return DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ");
}
public LSL_Integer llGetNumberOfPrims()
{
m_host.AddScriptLPS(1);
int avatarCount = m_host.ParentGroup.GetLinkedAvatars().Count;
return m_host.ParentGroup.PrimCount + avatarCount;
}
///
/// A partial implementation.
/// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetBoundingBox
/// So far only valid for standing/flying/ground sitting avatars and single prim objects.
/// If the object has multiple prims and/or a sitting avatar then the bounding
/// box is for the root prim only.
///
public LSL_List llGetBoundingBox(string obj)
{
m_host.AddScriptLPS(1);
UUID objID = UUID.Zero;
LSL_List result = new LSL_List();
// If the ID is not valid, return null result
if (!UUID.TryParse(obj, out objID))
{
result.Add(new LSL_Vector());
result.Add(new LSL_Vector());
return result;
}
// Check if this is an attached prim. If so, replace
// the UUID with the avatar UUID and report it's bounding box
SceneObjectPart part = World.GetSceneObjectPart(objID);
if (part != null && part.ParentGroup.IsAttachment)
objID = part.ParentGroup.AttachedAvatar;
// Find out if this is an avatar ID. If so, return it's box
ScenePresence presence = World.GetScenePresence(objID);
if (presence != null)
{
// As per LSL Wiki, there is no difference between sitting
// and standing avatar since server 1.36
LSL_Vector lower;
LSL_Vector upper;
if (presence.Animator.Animations.DefaultAnimation.AnimID
== AnimationSet.Animations.AnimsUUID["SIT_GROUND_CONSTRAINED"])
{
// This is for ground sitting avatars
float height = presence.Appearance.AvatarHeight / 2.66666667f;
lower = new LSL_Vector(-0.3375f, -0.45f, height * -1.0f);
upper = new LSL_Vector(0.3375f, 0.45f, 0.0f);
}
else
{
// This is for standing/flying avatars
float height = presence.Appearance.AvatarHeight / 2.0f;
lower = new LSL_Vector(-0.225f, -0.3f, height * -1.0f);
upper = new LSL_Vector(0.225f, 0.3f, height + 0.05f);
}
// Adjust to the documented error offsets (see LSL Wiki)
lower += new LSL_Vector(0.05f, 0.05f, 0.05f);
upper -= new LSL_Vector(0.05f, 0.05f, 0.05f);
if (lower.x > upper.x)
lower.x = upper.x;
if (lower.y > upper.y)
lower.y = upper.y;
if (lower.z > upper.z)
lower.z = upper.z;
result.Add(lower);
result.Add(upper);
return result;
}
part = World.GetSceneObjectPart(objID);
// Currently only works for single prims without a sitting avatar
if (part != null)
{
float minX;
float maxX;
float minY;
float maxY;
float minZ;
float maxZ;
// This BBox is in sim coordinates, with the offset being
// a contained point.
Vector3[] offsets = Scene.GetCombinedBoundingBox(new List { part.ParentGroup },
out minX, out maxX, out minY, out maxY, out minZ, out maxZ);
minX -= offsets[0].X;
maxX -= offsets[0].X;
minY -= offsets[0].Y;
maxY -= offsets[0].Y;
minZ -= offsets[0].Z;
maxZ -= offsets[0].Z;
LSL_Vector lower;
LSL_Vector upper;
// Adjust to the documented error offsets (see LSL Wiki)
lower = new LSL_Vector(minX + 0.05f, minY + 0.05f, minZ + 0.05f);
upper = new LSL_Vector(maxX - 0.05f, maxY - 0.05f, maxZ - 0.05f);
if (lower.x > upper.x)
lower.x = upper.x;
if (lower.y > upper.y)
lower.y = upper.y;
if (lower.z > upper.z)
lower.z = upper.z;
result.Add(lower);
result.Add(upper);
return result;
}
// Not found so return empty values
result.Add(new LSL_Vector());
result.Add(new LSL_Vector());
return result;
}
public LSL_Vector llGetGeometricCenter()
{
return new LSL_Vector(m_host.GetGeometricCenter().X, m_host.GetGeometricCenter().Y, m_host.GetGeometricCenter().Z);
}
public LSL_List llGetPrimitiveParams(LSL_List rules)
{
m_host.AddScriptLPS(1);
return GetLinkPrimitiveParams(m_host, rules);
}
public LSL_List llGetLinkPrimitiveParams(int linknumber, LSL_List rules)
{
m_host.AddScriptLPS(1);
List parts = GetLinkParts(linknumber);
LSL_List res = new LSL_List();
foreach (var part in parts)
{
LSL_List partRes = GetLinkPrimitiveParams(part, rules);
res += partRes;
}
return res;
}
public LSL_List GetLinkPrimitiveParams(SceneObjectPart part, LSL_List rules)
{
LSL_List res = new LSL_List();
int idx=0;
while (idx < rules.Length)
{
int code=(int)rules.GetLSLIntegerItem(idx++);
int remain=rules.Length-idx;
switch (code)
{
case (int)ScriptBaseClass.PRIM_MATERIAL:
res.Add(new LSL_Integer(part.Material));
break;
case (int)ScriptBaseClass.PRIM_PHYSICS:
if ((part.GetEffectiveObjectFlags() & (uint)PrimFlags.Physics) != 0)
res.Add(new LSL_Integer(1));
else
res.Add(new LSL_Integer(0));
break;
case (int)ScriptBaseClass.PRIM_TEMP_ON_REZ:
if ((part.GetEffectiveObjectFlags() & (uint)PrimFlags.TemporaryOnRez) != 0)
res.Add(new LSL_Integer(1));
else
res.Add(new LSL_Integer(0));
break;
case (int)ScriptBaseClass.PRIM_PHANTOM:
if ((part.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) != 0)
res.Add(new LSL_Integer(1));
else
res.Add(new LSL_Integer(0));
break;
case (int)ScriptBaseClass.PRIM_POSITION:
LSL_Vector v = new LSL_Vector(part.AbsolutePosition.X,
part.AbsolutePosition.Y,
part.AbsolutePosition.Z);
res.Add(v);
break;
case (int)ScriptBaseClass.PRIM_SIZE:
res.Add(new LSL_Vector(part.Scale.X,
part.Scale.Y,
part.Scale.Z));
break;
case (int)ScriptBaseClass.PRIM_ROTATION:
res.Add(GetPartRot(part));
break;
case (int)ScriptBaseClass.PRIM_TYPE:
// implementing box
PrimitiveBaseShape Shape = part.Shape;
int primType = (int)part.GetPrimType();
res.Add(new LSL_Integer(primType));
double topshearx = (double)(sbyte)Shape.PathShearX / 100.0; // Fix negative values for PathShearX
double topsheary = (double)(sbyte)Shape.PathShearY / 100.0; // and PathShearY.
switch (primType)
{
case ScriptBaseClass.PRIM_TYPE_BOX:
case ScriptBaseClass.PRIM_TYPE_CYLINDER:
case ScriptBaseClass.PRIM_TYPE_PRISM:
res.Add(new LSL_Integer(Shape.ProfileCurve) & 0xf0); // Isolate hole shape nibble.
res.Add(new LSL_Vector(Shape.ProfileBegin / 50000.0, 1 - Shape.ProfileEnd / 50000.0, 0));
res.Add(new LSL_Float(Shape.ProfileHollow / 50000.0));
res.Add(new LSL_Vector(Shape.PathTwistBegin / 100.0, Shape.PathTwist / 100.0, 0));
res.Add(new LSL_Vector(1 - (Shape.PathScaleX / 100.0 - 1), 1 - (Shape.PathScaleY / 100.0 - 1), 0));
res.Add(new LSL_Vector(topshearx, topsheary, 0));
break;
case ScriptBaseClass.PRIM_TYPE_SPHERE:
res.Add(new LSL_Integer(Shape.ProfileCurve) & 0xf0); // Isolate hole shape nibble.
res.Add(new LSL_Vector(Shape.PathBegin / 50000.0, 1 - Shape.PathEnd / 50000.0, 0));
res.Add(new LSL_Float(Shape.ProfileHollow / 50000.0));
res.Add(new LSL_Vector(Shape.PathTwistBegin / 100.0, Shape.PathTwist / 100.0, 0));
res.Add(new LSL_Vector(Shape.ProfileBegin / 50000.0, 1 - Shape.ProfileEnd / 50000.0, 0));
break;
case ScriptBaseClass.PRIM_TYPE_SCULPT:
res.Add(Shape.SculptTexture.ToString());
res.Add(new LSL_Integer(Shape.SculptType));
break;
case ScriptBaseClass.PRIM_TYPE_RING:
case ScriptBaseClass.PRIM_TYPE_TUBE:
case ScriptBaseClass.PRIM_TYPE_TORUS:
// holeshape
res.Add(new LSL_Integer(Shape.ProfileCurve) & 0xf0); // Isolate hole shape nibble.
// cut
res.Add(new LSL_Vector(Shape.PathBegin / 50000.0, 1 - Shape.PathEnd / 50000.0, 0));
// hollow
res.Add(new LSL_Float(Shape.ProfileHollow / 50000.0));
// twist
res.Add(new LSL_Vector(Shape.PathTwistBegin / 100.0, Shape.PathTwist / 100.0, 0));
// vector holesize
res.Add(new LSL_Vector(1 - (Shape.PathScaleX / 100.0 - 1), 1 - (Shape.PathScaleY / 100.0 - 1), 0));
// vector topshear
res.Add(new LSL_Vector(topshearx, topsheary, 0));
// vector profilecut
res.Add(new LSL_Vector(Shape.ProfileBegin / 50000.0, 1 - Shape.ProfileEnd / 50000.0, 0));
// vector tapera
res.Add(new LSL_Vector(Shape.PathTaperX / 100.0, Shape.PathTaperY / 100.0, 0));
// float revolutions
res.Add(new LSL_Float(Math.Round(Shape.PathRevolutions * 0.015d, 2, MidpointRounding.AwayFromZero)) + 1.0d);
// Slightly inaccurate, because an unsigned byte is being used to represent
// the entire range of floating-point values from 1.0 through 4.0 (which is how
// SL does it).
//
// Using these formulas to store and retrieve PathRevolutions, it is not
// possible to use all values between 1.00 and 4.00. For instance, you can't
// represent 1.10. You can represent 1.09 and 1.11, but not 1.10. So, if you
// use llSetPrimitiveParams to set revolutions to 1.10 and then retreive them
// with llGetPrimitiveParams, you'll retrieve 1.09. You can also see a similar
// behavior in the viewer as you cannot set 1.10. The viewer jumps to 1.11.
// In SL, llSetPrimitveParams and llGetPrimitiveParams can set and get a value
// such as 1.10. So, SL must store and retreive the actual user input rather
// than only storing the encoded value.
// float radiusoffset
res.Add(new LSL_Float(Shape.PathRadiusOffset / 100.0));
// float skew
res.Add(new LSL_Float(Shape.PathSkew / 100.0));
break;
}
break;
case (int)ScriptBaseClass.PRIM_TEXTURE:
if (remain < 1)
return res;
int face = (int)rules.GetLSLIntegerItem(idx++);
Primitive.TextureEntry tex = part.Shape.Textures;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0 ; face < GetNumberOfSides(part); face++)
{
Primitive.TextureEntryFace texface = tex.GetFace((uint)face);
res.Add(new LSL_String(texface.TextureID.ToString()));
res.Add(new LSL_Vector(texface.RepeatU,
texface.RepeatV,
0));
res.Add(new LSL_Vector(texface.OffsetU,
texface.OffsetV,
0));
res.Add(new LSL_Float(texface.Rotation));
}
}
else
{
if (face >= 0 && face < GetNumberOfSides(part))
{
Primitive.TextureEntryFace texface = tex.GetFace((uint)face);
res.Add(new LSL_String(texface.TextureID.ToString()));
res.Add(new LSL_Vector(texface.RepeatU,
texface.RepeatV,
0));
res.Add(new LSL_Vector(texface.OffsetU,
texface.OffsetV,
0));
res.Add(new LSL_Float(texface.Rotation));
}
}
break;
case (int)ScriptBaseClass.PRIM_COLOR:
if (remain < 1)
return res;
face=(int)rules.GetLSLIntegerItem(idx++);
tex = part.Shape.Textures;
Color4 texcolor;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0 ; face < GetNumberOfSides(part); face++)
{
texcolor = tex.GetFace((uint)face).RGBA;
res.Add(new LSL_Vector(texcolor.R,
texcolor.G,
texcolor.B));
res.Add(new LSL_Float(texcolor.A));
}
}
else
{
texcolor = tex.GetFace((uint)face).RGBA;
res.Add(new LSL_Vector(texcolor.R,
texcolor.G,
texcolor.B));
res.Add(new LSL_Float(texcolor.A));
}
break;
case (int)ScriptBaseClass.PRIM_BUMP_SHINY:
if (remain < 1)
return res;
face = (int)rules.GetLSLIntegerItem(idx++);
tex = part.Shape.Textures;
int shiny;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0; face < GetNumberOfSides(part); face++)
{
Shininess shinyness = tex.GetFace((uint)face).Shiny;
if (shinyness == Shininess.High)
{
shiny = ScriptBaseClass.PRIM_SHINY_HIGH;
}
else if (shinyness == Shininess.Medium)
{
shiny = ScriptBaseClass.PRIM_SHINY_MEDIUM;
}
else if (shinyness == Shininess.Low)
{
shiny = ScriptBaseClass.PRIM_SHINY_LOW;
}
else
{
shiny = ScriptBaseClass.PRIM_SHINY_NONE;
}
res.Add(new LSL_Integer(shiny));
res.Add(new LSL_Integer((int)tex.GetFace((uint)face).Bump));
}
}
else
{
Shininess shinyness = tex.GetFace((uint)face).Shiny;
if (shinyness == Shininess.High)
{
shiny = ScriptBaseClass.PRIM_SHINY_HIGH;
}
else if (shinyness == Shininess.Medium)
{
shiny = ScriptBaseClass.PRIM_SHINY_MEDIUM;
}
else if (shinyness == Shininess.Low)
{
shiny = ScriptBaseClass.PRIM_SHINY_LOW;
}
else
{
shiny = ScriptBaseClass.PRIM_SHINY_NONE;
}
res.Add(new LSL_Integer(shiny));
res.Add(new LSL_Integer((int)tex.GetFace((uint)face).Bump));
}
break;
case (int)ScriptBaseClass.PRIM_FULLBRIGHT:
if (remain < 1)
return res;
face = (int)rules.GetLSLIntegerItem(idx++);
tex = part.Shape.Textures;
int fullbright;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0; face < GetNumberOfSides(part); face++)
{
if (tex.GetFace((uint)face).Fullbright == true)
{
fullbright = ScriptBaseClass.TRUE;
}
else
{
fullbright = ScriptBaseClass.FALSE;
}
res.Add(new LSL_Integer(fullbright));
}
}
else
{
if (tex.GetFace((uint)face).Fullbright == true)
{
fullbright = ScriptBaseClass.TRUE;
}
else
{
fullbright = ScriptBaseClass.FALSE;
}
res.Add(new LSL_Integer(fullbright));
}
break;
case (int)ScriptBaseClass.PRIM_FLEXIBLE:
PrimitiveBaseShape shape = part.Shape;
if (shape.FlexiEntry)
res.Add(new LSL_Integer(1)); // active
else
res.Add(new LSL_Integer(0));
res.Add(new LSL_Integer(shape.FlexiSoftness));// softness
res.Add(new LSL_Float(shape.FlexiGravity)); // gravity
res.Add(new LSL_Float(shape.FlexiDrag)); // friction
res.Add(new LSL_Float(shape.FlexiWind)); // wind
res.Add(new LSL_Float(shape.FlexiTension)); // tension
res.Add(new LSL_Vector(shape.FlexiForceX, // force
shape.FlexiForceY,
shape.FlexiForceZ));
break;
case (int)ScriptBaseClass.PRIM_TEXGEN:
// (PRIM_TEXGEN_DEFAULT, PRIM_TEXGEN_PLANAR)
if (remain < 1)
return res;
face = (int)rules.GetLSLIntegerItem(idx++);
tex = part.Shape.Textures;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0; face < GetNumberOfSides(part); face++)
{
if (tex.GetFace((uint)face).TexMapType == MappingType.Planar)
{
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_TEXGEN_PLANAR));
}
else
{
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_TEXGEN_DEFAULT));
}
}
}
else
{
if (tex.GetFace((uint)face).TexMapType == MappingType.Planar)
{
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_TEXGEN_PLANAR));
}
else
{
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_TEXGEN_DEFAULT));
}
}
break;
case (int)ScriptBaseClass.PRIM_POINT_LIGHT:
shape = part.Shape;
if (shape.LightEntry)
res.Add(new LSL_Integer(1)); // active
else
res.Add(new LSL_Integer(0));
res.Add(new LSL_Vector(shape.LightColorR, // color
shape.LightColorG,
shape.LightColorB));
res.Add(new LSL_Float(shape.LightIntensity)); // intensity
res.Add(new LSL_Float(shape.LightRadius)); // radius
res.Add(new LSL_Float(shape.LightFalloff)); // falloff
break;
case (int)ScriptBaseClass.PRIM_GLOW:
if (remain < 1)
return res;
face = (int)rules.GetLSLIntegerItem(idx++);
tex = part.Shape.Textures;
float primglow;
if (face == ScriptBaseClass.ALL_SIDES)
{
for (face = 0; face < GetNumberOfSides(part); face++)
{
primglow = tex.GetFace((uint)face).Glow;
res.Add(new LSL_Float(primglow));
}
}
else
{
primglow = tex.GetFace((uint)face).Glow;
res.Add(new LSL_Float(primglow));
}
break;
case (int)ScriptBaseClass.PRIM_TEXT:
Color4 textColor = part.GetTextColor();
res.Add(new LSL_String(part.Text));
res.Add(new LSL_Vector(textColor.R,
textColor.G,
textColor.B));
res.Add(new LSL_Float(textColor.A));
break;
case (int)ScriptBaseClass.PRIM_NAME:
res.Add(new LSL_String(part.Name));
break;
case (int)ScriptBaseClass.PRIM_DESC:
res.Add(new LSL_String(part.Description));
break;
case (int)ScriptBaseClass.PRIM_ROT_LOCAL:
res.Add(new LSL_Rotation(part.RotationOffset.X, part.RotationOffset.Y, part.RotationOffset.Z, part.RotationOffset.W));
break;
}
}
return res;
}
public LSL_List llGetPrimMediaParams(int face, LSL_List rules)
{
m_host.AddScriptLPS(1);
ScriptSleep(1000);
// LSL Spec http://wiki.secondlife.com/wiki/LlGetPrimMediaParams says to fail silently if face is invalid
// TODO: Need to correctly handle case where a face has no media (which gives back an empty list).
// Assuming silently fail means give back an empty list. Ideally, need to check this.
if (face < 0 || face > m_host.GetNumberOfSides() - 1)
return new LSL_List();
return GetPrimMediaParams(face, rules);
}
private LSL_List GetPrimMediaParams(int face, LSL_List rules)
{
IMoapModule module = m_ScriptEngine.World.RequestModuleInterface();
if (null == module)
throw new Exception("Media on a prim functions not available");
MediaEntry me = module.GetMediaEntry(m_host, face);
// As per http://wiki.secondlife.com/wiki/LlGetPrimMediaParams
if (null == me)
return new LSL_List();
LSL_List res = new LSL_List();
for (int i = 0; i < rules.Length; i++)
{
int code = (int)rules.GetLSLIntegerItem(i);
switch (code)
{
case ScriptBaseClass.PRIM_MEDIA_ALT_IMAGE_ENABLE:
// Not implemented
res.Add(new LSL_Integer(0));
break;
case ScriptBaseClass.PRIM_MEDIA_CONTROLS:
if (me.Controls == MediaControls.Standard)
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_MEDIA_CONTROLS_STANDARD));
else
res.Add(new LSL_Integer(ScriptBaseClass.PRIM_MEDIA_CONTROLS_MINI));
break;
case ScriptBaseClass.PRIM_MEDIA_CURRENT_URL:
res.Add(new LSL_String(me.CurrentURL));
break;
case ScriptBaseClass.PRIM_MEDIA_HOME_URL:
res.Add(new LSL_String(me.HomeURL));
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_LOOP:
res.Add(me.AutoLoop ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_PLAY:
res.Add(me.AutoPlay ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_SCALE:
res.Add(me.AutoScale ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_ZOOM:
res.Add(me.AutoZoom ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_FIRST_CLICK_INTERACT:
res.Add(me.InteractOnFirstClick ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_WIDTH_PIXELS:
res.Add(new LSL_Integer(me.Width));
break;
case ScriptBaseClass.PRIM_MEDIA_HEIGHT_PIXELS:
res.Add(new LSL_Integer(me.Height));
break;
case ScriptBaseClass.PRIM_MEDIA_WHITELIST_ENABLE:
res.Add(me.EnableWhiteList ? ScriptBaseClass.TRUE : ScriptBaseClass.FALSE);
break;
case ScriptBaseClass.PRIM_MEDIA_WHITELIST:
string[] urls = (string[])me.WhiteList.Clone();
for (int j = 0; j < urls.Length; j++)
urls[j] = Uri.EscapeDataString(urls[j]);
res.Add(new LSL_String(string.Join(", ", urls)));
break;
case ScriptBaseClass.PRIM_MEDIA_PERMS_INTERACT:
res.Add(new LSL_Integer((int)me.InteractPermissions));
break;
case ScriptBaseClass.PRIM_MEDIA_PERMS_CONTROL:
res.Add(new LSL_Integer((int)me.ControlPermissions));
break;
}
}
return res;
}
public LSL_Integer llSetPrimMediaParams(int face, LSL_List rules)
{
m_host.AddScriptLPS(1);
ScriptSleep(1000);
// LSL Spec http://wiki.secondlife.com/wiki/LlSetPrimMediaParams says to fail silently if face is invalid
// Assuming silently fail means sending back LSL_STATUS_OK. Ideally, need to check this.
// Don't perform the media check directly
if (face < 0 || face > m_host.GetNumberOfSides() - 1)
return ScriptBaseClass.LSL_STATUS_OK;
return SetPrimMediaParams(face, rules);
}
private LSL_Integer SetPrimMediaParams(int face, LSL_List rules)
{
IMoapModule module = m_ScriptEngine.World.RequestModuleInterface();
if (null == module)
throw new Exception("Media on a prim functions not available");
MediaEntry me = module.GetMediaEntry(m_host, face);
if (null == me)
me = new MediaEntry();
int i = 0;
while (i < rules.Length - 1)
{
int code = rules.GetLSLIntegerItem(i++);
switch (code)
{
case ScriptBaseClass.PRIM_MEDIA_ALT_IMAGE_ENABLE:
me.EnableAlterntiveImage = (rules.GetLSLIntegerItem(i++) != 0 ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_CONTROLS:
int v = rules.GetLSLIntegerItem(i++);
if (ScriptBaseClass.PRIM_MEDIA_CONTROLS_STANDARD == v)
me.Controls = MediaControls.Standard;
else
me.Controls = MediaControls.Mini;
break;
case ScriptBaseClass.PRIM_MEDIA_CURRENT_URL:
me.CurrentURL = rules.GetLSLStringItem(i++);
break;
case ScriptBaseClass.PRIM_MEDIA_HOME_URL:
me.HomeURL = rules.GetLSLStringItem(i++);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_LOOP:
me.AutoLoop = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_PLAY:
me.AutoPlay = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_SCALE:
me.AutoScale = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_AUTO_ZOOM:
me.AutoZoom = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_FIRST_CLICK_INTERACT:
me.InteractOnFirstClick = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_WIDTH_PIXELS:
me.Width = (int)rules.GetLSLIntegerItem(i++);
break;
case ScriptBaseClass.PRIM_MEDIA_HEIGHT_PIXELS:
me.Height = (int)rules.GetLSLIntegerItem(i++);
break;
case ScriptBaseClass.PRIM_MEDIA_WHITELIST_ENABLE:
me.EnableWhiteList = (ScriptBaseClass.TRUE == rules.GetLSLIntegerItem(i++) ? true : false);
break;
case ScriptBaseClass.PRIM_MEDIA_WHITELIST:
string[] rawWhiteListUrls = rules.GetLSLStringItem(i++).ToString().Split(new char[] { ',' });
List whiteListUrls = new List();
Array.ForEach(
rawWhiteListUrls, delegate(string rawUrl) { whiteListUrls.Add(rawUrl.Trim()); });
me.WhiteList = whiteListUrls.ToArray();
break;
case ScriptBaseClass.PRIM_MEDIA_PERMS_INTERACT:
me.InteractPermissions = (MediaPermission)(byte)(int)rules.GetLSLIntegerItem(i++);
break;
case ScriptBaseClass.PRIM_MEDIA_PERMS_CONTROL:
me.ControlPermissions = (MediaPermission)(byte)(int)rules.GetLSLIntegerItem(i++);
break;
}
}
module.SetMediaEntry(m_host, face, me);
return ScriptBaseClass.LSL_STATUS_OK;
}
public LSL_Integer llClearPrimMedia(LSL_Integer face)
{
m_host.AddScriptLPS(1);
ScriptSleep(1000);
// LSL Spec http://wiki.secondlife.com/wiki/LlClearPrimMedia says to fail silently if face is invalid
// Assuming silently fail means sending back LSL_STATUS_OK. Ideally, need to check this.
// FIXME: Don't perform the media check directly
if (face < 0 || face > m_host.GetNumberOfSides() - 1)
return ScriptBaseClass.LSL_STATUS_OK;
IMoapModule module = m_ScriptEngine.World.RequestModuleInterface();
if (null == module)
throw new Exception("Media on a prim functions not available");
module.ClearMediaEntry(m_host, face);
return ScriptBaseClass.LSL_STATUS_OK;
}
//
//
// The .NET definition of base 64 is:
//
// -
// Significant: A-Z a-z 0-9 + -
//
// -
// Whitespace: \t \n \r ' '
//
// -
// Valueless: =
//
// -
// End-of-string: \0 or '=='
//
//
//
//
// Each point in a base-64 string represents
// a 6 bit value. A 32-bit integer can be
// represented using 6 characters (with some
// redundancy).
//
//
// LSL requires a base64 string to be 8
// characters in length. LSL also uses '/'
// rather than '-' (MIME compliant).
//
//
// RFC 1341 used as a reference (as specified
// by the SecondLife Wiki).
//
//
// SL do not record any kind of exception for
// these functions, so the string to integer
// conversion returns '0' if an invalid
// character is encountered during conversion.
//
//
// References
//
// -
// http://lslwiki.net/lslwiki/wakka.php?wakka=Base64
//
// -
//
//
//
//
//
// Table for converting 6-bit integers into
// base-64 characters
//
protected static readonly char[] i2ctable =
{
'A','B','C','D','E','F','G','H',
'I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X',
'Y','Z',
'a','b','c','d','e','f','g','h',
'i','j','k','l','m','n','o','p',
'q','r','s','t','u','v','w','x',
'y','z',
'0','1','2','3','4','5','6','7',
'8','9',
'+','/'
};
//
// Table for converting base-64 characters
// into 6-bit integers.
//
protected static readonly int[] c2itable =
{
-1,-1,-1,-1,-1,-1,-1,-1, // 0x
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // 1x
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // 2x
-1,-1,-1,63,-1,-1,-1,64,
53,54,55,56,57,58,59,60, // 3x
61,62,-1,-1,-1,0,-1,-1,
-1,1,2,3,4,5,6,7, // 4x
8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23, // 5x
24,25,26,-1,-1,-1,-1,-1,
-1,27,28,29,30,31,32,33, // 6x
34,35,36,37,38,39,40,41,
42,43,44,45,46,47,48,49, // 7x
50,51,52,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // 8x
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // 9x
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Ax
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Bx
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Cx
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Dx
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Ex
-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, // Fx
-1,-1,-1,-1,-1,-1,-1,-1
};
//
// Converts a 32-bit integer into a Base64
// character string. Base64 character strings
// are always 8 characters long. All iinteger
// values are acceptable.
//
//
// 32-bit integer to be converted.
//
//
// 8 character string. The 1st six characters
// contain the encoded number, the last two
// characters are padded with "=".
//
public LSL_String llIntegerToBase64(int number)
{
// uninitialized string
char[] imdt = new char[8];
m_host.AddScriptLPS(1);
// Manually unroll the loop
imdt[7] = '=';
imdt[6] = '=';
imdt[5] = i2ctable[number<<4 & 0x3F];
imdt[4] = i2ctable[number>>2 & 0x3F];
imdt[3] = i2ctable[number>>8 & 0x3F];
imdt[2] = i2ctable[number>>14 & 0x3F];
imdt[1] = i2ctable[number>>20 & 0x3F];
imdt[0] = i2ctable[number>>26 & 0x3F];
return new string(imdt);
}
//
// Converts an eight character base-64 string
// into a 32-bit integer.
//
//
// 8 characters string to be converted. Other
// length strings return zero.
//
//
// Returns an integer representing the
// encoded value providedint he 1st 6
// characters of the string.
//
//
// This is coded to behave like LSL's
// implementation (I think), based upon the
// information available at the Wiki.
// If more than 8 characters are supplied,
// zero is returned.
// If a NULL string is supplied, zero will
// be returned.
// If fewer than 6 characters are supplied, then
// the answer will reflect a partial
// accumulation.
//
// The 6-bit segments are
// extracted left-to-right in big-endian mode,
// which means that segment 6 only contains the
// two low-order bits of the 32 bit integer as
// its high order 2 bits. A short string therefore
// means loss of low-order information. E.g.
//
// |<---------------------- 32-bit integer ----------------------->|<-Pad->|
// |<--Byte 0----->|<--Byte 1----->|<--Byte 2----->|<--Byte 3----->|<-Pad->|
// |3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | |P|P|P|P|
// |1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|P|P|P|P|
// | str[0] | str[1] | str[2] | str[3] | str[4] | str[6] |
//
//
//
public LSL_Integer llBase64ToInteger(string str)
{
int number = 0;
int digit;
m_host.AddScriptLPS(1);
// Require a well-fromed base64 string
if (str.Length > 8)
return 0;
// The loop is unrolled in the interests
// of performance and simple necessity.
//
// MUST find 6 digits to be well formed
// -1 == invalid
// 0 == padding
if ((digit = c2itable[str[0]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit<<26;
if ((digit = c2itable[str[1]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit<<20;
if ((digit = c2itable[str[2]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit<<14;
if ((digit = c2itable[str[3]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit<<8;
if ((digit = c2itable[str[4]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit<<2;
if ((digit = c2itable[str[5]]) <= 0)
{
return digit < 0 ? (int)0 : number;
}
number += --digit>>4;
// ignore trailing padding
return number;
}
public LSL_Float llGetGMTclock()
{
m_host.AddScriptLPS(1);
return DateTime.UtcNow.TimeOfDay.TotalSeconds;
}
public LSL_String llGetHTTPHeader(LSL_Key request_id, string header)
{
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
return m_UrlModule.GetHttpHeader(new UUID(request_id), header);
return String.Empty;
}
public LSL_String llGetSimulatorHostname()
{
m_host.AddScriptLPS(1);
return System.Environment.MachineName;
}
//
// Scan the string supplied in 'src' and
// tokenize it based upon two sets of
// tokenizers provided in two lists,
// separators and spacers.
//
//
//
// Separators demarcate tokens and are
// elided as they are encountered. Spacers
// also demarcate tokens, but are themselves
// retained as tokens.
//
// Both separators and spacers may be arbitrarily
// long strings. i.e. ":::".
//
// The function returns an ordered list
// representing the tokens found in the supplied
// sources string. If two successive tokenizers
// are encountered, then a null-string entry is
// added to the list.
//
// It is a precondition that the source and
// toekizer lisst are non-null. If they are null,
// then a null pointer exception will be thrown
// while their lengths are being determined.
//
// A small amount of working memoryis required
// of approximately 8*#tokenizers + 8*srcstrlen.
//
// There are many ways in which this function
// can be implemented, this implementation is
// fairly naive and assumes that when the
// function is invooked with a short source
// string and/or short lists of tokenizers, then
// performance will not be an issue.
//
// In order to minimize the perofrmance
// effects of long strings, or large numbers
// of tokeizers, the function skips as far as
// possible whenever a toekenizer is found,
// and eliminates redundant tokenizers as soon
// as is possible.
//
// The implementation tries to minimize temporary
// garbage generation.
//
public LSL_List llParseStringKeepNulls(string src, LSL_List separators, LSL_List spacers)
{
return ParseString2List(src, separators, spacers, true);
}
private LSL_List ParseString2List(string src, LSL_List separators, LSL_List spacers, bool keepNulls)
{
int srclen = src.Length;
int seplen = separators.Length;
object[] separray = separators.Data;
int spclen = spacers.Length;
object[] spcarray = spacers.Data;
int dellen = 0;
string[] delarray = new string[seplen+spclen];
int outlen = 0;
string[] outarray = new string[srclen*2+1];
int i, j;
string d;
m_host.AddScriptLPS(1);
/*
* Convert separator and spacer lists to C# strings.
* Also filter out null strings so we don't hang.
*/
for (i = 0; i < seplen; i ++)
{
d = separray[i].ToString();
if (d.Length > 0)
{
delarray[dellen++] = d;
}
}
seplen = dellen;
for (i = 0; i < spclen; i ++)
{
d = spcarray[i].ToString();
if (d.Length > 0)
{
delarray[dellen++] = d;
}
}
/*
* Scan through source string from beginning to end.
*/
for (i = 0;;)
{
/*
* Find earliest delimeter in src starting at i (if any).
*/
int earliestDel = -1;
int earliestSrc = srclen;
string earliestStr = null;
for (j = 0; j < dellen; j ++)
{
d = delarray[j];
if (d != null)
{
int index = src.IndexOf(d, i);
if (index < 0)
{
delarray[j] = null; // delim nowhere in src, don't check it anymore
}
else if (index < earliestSrc)
{
earliestSrc = index; // where delimeter starts in source string
earliestDel = j; // where delimeter is in delarray[]
earliestStr = d; // the delimeter string from delarray[]
if (index == i) break; // can't do any better than found at beg of string
}
}
}
/*
* Output source string starting at i through start of earliest delimeter.
*/
if (keepNulls || (earliestSrc > i))
{
outarray[outlen++] = src.Substring(i, earliestSrc - i);
}
/*
* If no delimeter found at or after i, we're done scanning.
*/
if (earliestDel < 0) break;
/*
* If delimeter was a spacer, output the spacer.
*/
if (earliestDel >= seplen)
{
outarray[outlen++] = earliestStr;
}
/*
* Look at rest of src string following delimeter.
*/
i = earliestSrc + earliestStr.Length;
}
/*
* Make up an exact-sized output array suitable for an LSL_List object.
*/
object[] outlist = new object[outlen];
for (i = 0; i < outlen; i ++)
{
outlist[i] = new LSL_String(outarray[i]);
}
return new LSL_List(outlist);
}
public LSL_Integer llGetObjectPermMask(int mask)
{
m_host.AddScriptLPS(1);
int permmask = 0;
if (mask == ScriptBaseClass.MASK_BASE)//0
{
permmask = (int)m_host.BaseMask;
}
else if (mask == ScriptBaseClass.MASK_OWNER)//1
{
permmask = (int)m_host.OwnerMask;
}
else if (mask == ScriptBaseClass.MASK_GROUP)//2
{
permmask = (int)m_host.GroupMask;
}
else if (mask == ScriptBaseClass.MASK_EVERYONE)//3
{
permmask = (int)m_host.EveryoneMask;
}
else if (mask == ScriptBaseClass.MASK_NEXT)//4
{
permmask = (int)m_host.NextOwnerMask;
}
return permmask;
}
public void llSetObjectPermMask(int mask, int value)
{
m_host.AddScriptLPS(1);
if (m_ScriptEngine.Config.GetBoolean("AllowGodFunctions", false))
{
if (World.Permissions.CanRunConsoleCommand(m_host.OwnerID))
{
if (mask == ScriptBaseClass.MASK_BASE)//0
{
m_host.BaseMask = (uint)value;
}
else if (mask == ScriptBaseClass.MASK_OWNER)//1
{
m_host.OwnerMask = (uint)value;
}
else if (mask == ScriptBaseClass.MASK_GROUP)//2
{
m_host.GroupMask = (uint)value;
}
else if (mask == ScriptBaseClass.MASK_EVERYONE)//3
{
m_host.EveryoneMask = (uint)value;
}
else if (mask == ScriptBaseClass.MASK_NEXT)//4
{
m_host.NextOwnerMask = (uint)value;
}
}
}
}
public LSL_Integer llGetInventoryPermMask(string item, int mask)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == item)
{
m_host.TaskInventory.LockItemsForRead(false);
switch (mask)
{
case 0:
return (int)inv.Value.BasePermissions;
case 1:
return (int)inv.Value.CurrentPermissions;
case 2:
return (int)inv.Value.GroupPermissions;
case 3:
return (int)inv.Value.EveryonePermissions;
case 4:
return (int)inv.Value.NextPermissions;
}
}
}
m_host.TaskInventory.LockItemsForRead(false);
return -1;
}
public void llSetInventoryPermMask(string item, int mask, int value)
{
m_host.AddScriptLPS(1);
if (m_ScriptEngine.Config.GetBoolean("AllowGodFunctions", false))
{
if (World.Permissions.CanRunConsoleCommand(m_host.OwnerID))
{
lock (m_host.TaskInventory)
{
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == item)
{
switch (mask)
{
case 0:
inv.Value.BasePermissions = (uint)value;
break;
case 1:
inv.Value.CurrentPermissions = (uint)value;
break;
case 2:
inv.Value.GroupPermissions = (uint)value;
break;
case 3:
inv.Value.EveryonePermissions = (uint)value;
break;
case 4:
inv.Value.NextPermissions = (uint)value;
break;
}
}
}
}
}
}
}
public LSL_String llGetInventoryCreator(string item)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == item)
{
m_host.TaskInventory.LockItemsForRead(false);
return inv.Value.CreatorID.ToString();
}
}
m_host.TaskInventory.LockItemsForRead(false);
llSay(0, "No item name '" + item + "'");
return String.Empty;
}
public void llOwnerSay(string msg)
{
m_host.AddScriptLPS(1);
World.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Owner, 0,
m_host.AbsolutePosition, m_host.Name, m_host.UUID, false);
// IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface();
// wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg);
}
public LSL_String llRequestSecureURL()
{
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
return m_UrlModule.RequestSecureURL(m_ScriptEngine.ScriptModule, m_host, m_itemID).ToString();
return UUID.Zero.ToString();
}
public LSL_String llRequestSimulatorData(string simulator, int data)
{
IOSSL_Api ossl = (IOSSL_Api)m_ScriptEngine.GetApi(m_itemID, "OSSL");
try
{
m_host.AddScriptLPS(1);
string reply = String.Empty;
GridRegion info;
if (m_ScriptEngine.World.RegionInfo.RegionName == simulator)
info = new GridRegion(m_ScriptEngine.World.RegionInfo);
else
info = m_ScriptEngine.World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator);
switch (data)
{
case ScriptBaseClass.DATA_SIM_POS:
if (info == null)
{
ScriptSleep(1000);
return UUID.Zero.ToString();
}
reply = new LSL_Vector(
info.RegionLocX,
info.RegionLocY,
0).ToString();
break;
case ScriptBaseClass.DATA_SIM_STATUS:
if (info != null)
reply = "up"; // Duh!
else
reply = "unknown";
break;
case ScriptBaseClass.DATA_SIM_RATING:
if (info == null)
{
ScriptSleep(1000);
return UUID.Zero.ToString();
}
int access = info.Maturity;
if (access == 0)
reply = "PG";
else if (access == 1)
reply = "MATURE";
else if (access == 2)
reply = "ADULT";
else
reply = "UNKNOWN";
break;
case ScriptBaseClass.DATA_SIM_RELEASE:
if (ossl != null)
ossl.CheckThreatLevel(ThreatLevel.High, "llRequestSimulatorData");
reply = "OpenSim";
break;
default:
ScriptSleep(1000);
return UUID.Zero.ToString(); // Raise no event
}
UUID rq = UUID.Random();
UUID tid = AsyncCommands.
DataserverPlugin.RegisterRequest(m_localID, m_itemID, rq.ToString());
AsyncCommands.
DataserverPlugin.DataserverReply(rq.ToString(), reply);
ScriptSleep(1000);
return tid.ToString();
}
catch(Exception)
{
//m_log.Error("[LSL_API]: llRequestSimulatorData" + e.ToString());
return UUID.Zero.ToString();
}
}
public LSL_String llRequestURL()
{
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
return m_UrlModule.RequestURL(m_ScriptEngine.ScriptModule, m_host, m_itemID).ToString();
return UUID.Zero.ToString();
}
public void llForceMouselook(int mouselook)
{
m_host.AddScriptLPS(1);
m_host.SetForceMouselook(mouselook != 0);
}
public LSL_Float llGetObjectMass(string id)
{
m_host.AddScriptLPS(1);
UUID key = new UUID();
if (UUID.TryParse(id, out key))
{
try
{
SceneObjectPart obj = World.GetSceneObjectPart(World.Entities[key].LocalId);
if (obj != null)
return (double)obj.GetMass();
// the object is null so the key is for an avatar
ScenePresence avatar = World.GetScenePresence(key);
if (avatar != null)
if (avatar.IsChildAgent)
// reference http://www.lslwiki.net/lslwiki/wakka.php?wakka=llGetObjectMass
// child agents have a mass of 1.0
return 1;
else
return (double)avatar.PhysicsActor.Mass;
}
catch (KeyNotFoundException)
{
return 0; // The Object/Agent not in the region so just return zero
}
}
return 0;
}
///
/// llListReplaceList removes the sub-list defined by the inclusive indices
/// start and end and inserts the src list in its place. The inclusive
/// nature of the indices means that at least one element must be deleted
/// if the indices are within the bounds of the existing list. I.e. 2,2
/// will remove the element at index 2 and replace it with the source
/// list. Both indices may be negative, with the usual interpretation. An
/// interesting case is where end is lower than start. As these indices
/// bound the list to be removed, then 0->end, and start->lim are removed
/// and the source list is added as a suffix.
///
public LSL_List llListReplaceList(LSL_List dest, LSL_List src, int start, int end)
{
LSL_List pref = null;
m_host.AddScriptLPS(1);
// Note that although we have normalized, both
// indices could still be negative.
if (start < 0)
{
start = start+dest.Length;
}
if (end < 0)
{
end = end+dest.Length;
}
// The comventional case, remove a sequence starting with
// start and ending with end. And then insert the source
// list.
if (start <= end)
{
// If greater than zero, then there is going to be a
// surviving prefix. Otherwise the inclusive nature
// of the indices mean that we're going to add the
// source list as a prefix.
if (start > 0)
{
pref = dest.GetSublist(0,start-1);
// Only add a suffix if there is something
// beyond the end index (it's inclusive too).
if (end + 1 < dest.Length)
{
return pref + src + dest.GetSublist(end + 1, -1);
}
else
{
return pref + src;
}
}
// If start is less than or equal to zero, then
// the new list is simply a prefix. We still need to
// figure out any necessary surgery to the destination
// based upon end. Note that if end exceeds the upper
// bound in this case, the entire destination list
// is removed.
else if (start == 0)
{
if (end + 1 < dest.Length)
return src + dest.GetSublist(end + 1, -1);
else
return src;
}
else // Start < 0
{
if (end + 1 < dest.Length)
return dest.GetSublist(end + 1, -1);
else
return new LSL_List();
}
}
// Finally, if start > end, we strip away a prefix and
// a suffix, to leave the list that sits ens
// and start, and then tag on the src list. AT least
// that's my interpretation. We can get sublist to do
// this for us. Note that one, or both of the indices
// might have been negative.
else
{
return dest.GetSublist(end + 1, start - 1) + src;
}
}
public void llLoadURL(string avatar_id, string message, string url)
{
m_host.AddScriptLPS(1);
IDialogModule dm = World.RequestModuleInterface();
if (null != dm)
dm.SendUrlToUser(
new UUID(avatar_id), m_host.Name, m_host.UUID, m_host.OwnerID, false, message, url);
ScriptSleep(10000);
}
public void llParcelMediaCommandList(LSL_List commandList)
{
// TODO: Not implemented yet (missing in libomv?):
// PARCEL_MEDIA_COMMAND_LOOP_SET float loop Use this to get or set the parcel's media loop duration. (1.19.1 RC0 or later)
m_host.AddScriptLPS(1);
// according to the docs, this command only works if script owner and land owner are the same
// lets add estate owners and gods, too, and use the generic permission check.
ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (!World.Permissions.CanEditParcelProperties(m_host.OwnerID, landObject, GroupPowers.ChangeMedia)) return;
bool update = false; // send a ParcelMediaUpdate (and possibly change the land's media URL)?
byte loop = 0;
LandData landData = landObject.LandData;
string url = landData.MediaURL;
string texture = landData.MediaID.ToString();
bool autoAlign = landData.MediaAutoScale != 0;
string mediaType = ""; // TODO these have to be added as soon as LandData supports it
string description = "";
int width = 0;
int height = 0;
uint commandToSend = 0;
float time = 0.0f; // default is from start
ScenePresence presence = null;
for (int i = 0; i < commandList.Data.Length; i++)
{
uint command = (uint)(commandList.GetLSLIntegerItem(i));
switch (command)
{
case (uint)ParcelMediaCommandEnum.Agent:
// we send only to one agent
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_String)
{
UUID agentID;
if (UUID.TryParse((LSL_String)commandList.Data[i + 1], out agentID))
{
presence = World.GetScenePresence(agentID);
}
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_AGENT must be a key");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Loop:
loop = 1;
commandToSend = command;
update = true; //need to send the media update packet to set looping
break;
case (uint)ParcelMediaCommandEnum.Play:
loop = 0;
commandToSend = command;
update = true; //need to send the media update packet to make sure it doesn't loop
break;
case (uint)ParcelMediaCommandEnum.Pause:
case (uint)ParcelMediaCommandEnum.Stop:
case (uint)ParcelMediaCommandEnum.Unload:
commandToSend = command;
break;
case (uint)ParcelMediaCommandEnum.Url:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_String)
{
url = (LSL_String)commandList.Data[i + 1];
update = true;
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_URL must be a string.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Texture:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_String)
{
texture = (LSL_String)commandList.Data[i + 1];
update = true;
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TEXTURE must be a string or key.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Time:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_Float)
{
time = (float)(LSL_Float)commandList.Data[i + 1];
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TIME must be a float.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.AutoAlign:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_Integer)
{
autoAlign = (LSL_Integer)commandList.Data[i + 1];
update = true;
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_AUTO_ALIGN must be an integer.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Type:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_String)
{
mediaType = (LSL_String)commandList.Data[i + 1];
update = true;
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TYPE must be a string.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Desc:
if ((i + 1) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_String)
{
description = (LSL_String)commandList.Data[i + 1];
update = true;
}
else ShoutError("The argument of PARCEL_MEDIA_COMMAND_DESC must be a string.");
++i;
}
break;
case (uint)ParcelMediaCommandEnum.Size:
if ((i + 2) < commandList.Length)
{
if (commandList.Data[i + 1] is LSL_Integer)
{
if (commandList.Data[i + 2] is LSL_Integer)
{
width = (LSL_Integer)commandList.Data[i + 1];
height = (LSL_Integer)commandList.Data[i + 2];
update = true;
}
else ShoutError("The second argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer.");
}
else ShoutError("The first argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer.");
i += 2;
}
break;
default:
NotImplemented("llParcelMediaCommandList parameter not supported yet: " + Enum.Parse(typeof(ParcelMediaCommandEnum), commandList.Data[i].ToString()).ToString());
break;
}//end switch
}//end for
// if we didn't get a presence, we send to all and change the url
// if we did get a presence, we only send to the agent specified, and *don't change the land settings*!
// did something important change or do we only start/stop/pause?
if (update)
{
if (presence == null)
{
// we send to all
landData.MediaID = new UUID(texture);
landData.MediaAutoScale = autoAlign ? (byte)1 : (byte)0;
landData.MediaWidth = width;
landData.MediaHeight = height;
landData.MediaType = mediaType;
// do that one last, it will cause a ParcelPropertiesUpdate
landObject.SetMediaUrl(url);
// now send to all (non-child) agents in the parcel
World.ForEachScenePresence(delegate(ScenePresence sp)
{
if (!sp.IsChildAgent && (sp.currentParcelUUID == landData.GlobalID))
{
sp.ControllingClient.SendParcelMediaUpdate(landData.MediaURL,
landData.MediaID,
landData.MediaAutoScale,
mediaType,
description,
width, height,
loop);
}
});
}
else if (!presence.IsChildAgent)
{
// we only send to one (root) agent
presence.ControllingClient.SendParcelMediaUpdate(url,
new UUID(texture),
autoAlign ? (byte)1 : (byte)0,
mediaType,
description,
width, height,
loop);
}
}
if (commandToSend != 0)
{
// the commandList contained a start/stop/... command, too
if (presence == null)
{
// send to all (non-child) agents in the parcel
World.ForEachScenePresence(delegate(ScenePresence sp)
{
if (!sp.IsChildAgent && (sp.currentParcelUUID == landData.GlobalID))
{
sp.ControllingClient.SendParcelMediaCommand(0x4, // TODO what is this?
(ParcelMediaCommandEnum)commandToSend,
time);
}
});
}
else if (!presence.IsChildAgent)
{
presence.ControllingClient.SendParcelMediaCommand(0x4, // TODO what is this?
(ParcelMediaCommandEnum)commandToSend,
time);
}
}
ScriptSleep(2000);
}
public LSL_List llParcelMediaQuery(LSL_List aList)
{
m_host.AddScriptLPS(1);
LSL_List list = new LSL_List();
//TO DO: make the implementation for the missing commands
//PARCEL_MEDIA_COMMAND_LOOP_SET float loop Use this to get or set the parcel's media loop duration. (1.19.1 RC0 or later)
for (int i = 0; i < aList.Data.Length; i++)
{
if (aList.Data[i] != null)
{
switch ((ParcelMediaCommandEnum) Convert.ToInt32(aList.Data[i].ToString()))
{
case ParcelMediaCommandEnum.Url:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaURL));
break;
case ParcelMediaCommandEnum.Desc:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).Description));
break;
case ParcelMediaCommandEnum.Texture:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaID.ToString()));
break;
case ParcelMediaCommandEnum.Type:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaType));
break;
case ParcelMediaCommandEnum.Size:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaWidth));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaHeight));
break;
default:
ParcelMediaCommandEnum mediaCommandEnum = ParcelMediaCommandEnum.Url;
NotImplemented("llParcelMediaQuery parameter do not supported yet: " + Enum.Parse(mediaCommandEnum.GetType() , aList.Data[i].ToString()).ToString());
break;
}
}
}
ScriptSleep(2000);
return list;
}
public LSL_Integer llModPow(int a, int b, int c)
{
m_host.AddScriptLPS(1);
Int64 tmp = 0;
Math.DivRem(Convert.ToInt64(Math.Pow(a, b)), c, out tmp);
ScriptSleep(1000);
return Convert.ToInt32(tmp);
}
public LSL_Integer llGetInventoryType(string name)
{
m_host.AddScriptLPS(1);
m_host.TaskInventory.LockItemsForRead(true);
foreach (KeyValuePair inv in m_host.TaskInventory)
{
if (inv.Value.Name == name)
{
m_host.TaskInventory.LockItemsForRead(false);
return inv.Value.Type;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return -1;
}
public void llSetPayPrice(int price, LSL_List quick_pay_buttons)
{
m_host.AddScriptLPS(1);
if (quick_pay_buttons.Data.Length < 4)
{
int x;
for (x=quick_pay_buttons.Data.Length; x<= 4; x++)
{
quick_pay_buttons.Add(ScriptBaseClass.PAY_HIDE);
}
}
int[] nPrice = new int[5];
nPrice[0] = price;
nPrice[1] = quick_pay_buttons.GetLSLIntegerItem(0);
nPrice[2] = quick_pay_buttons.GetLSLIntegerItem(1);
nPrice[3] = quick_pay_buttons.GetLSLIntegerItem(2);
nPrice[4] = quick_pay_buttons.GetLSLIntegerItem(3);
m_host.ParentGroup.RootPart.PayPrice = nPrice;
m_host.ParentGroup.HasGroupChanged = true;
}
public LSL_Vector llGetCameraPos()
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero)
return new LSL_Vector();
m_host.TaskInventory.LockItemsForRead(true);
if (m_host.TaskInventory[invItemID].PermsGranter == UUID.Zero)
{
m_host.TaskInventory.LockItemsForRead(false);
return new LSL_Vector();
}
if ((m_host.TaskInventory[invItemID].PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0)
{
ShoutError("No permissions to track the camera");
m_host.TaskInventory.LockItemsForRead(false);
return new LSL_Vector();
}
m_host.TaskInventory.LockItemsForRead(false);
ScenePresence presence = World.GetScenePresence(m_host.OwnerID);
if (presence != null)
{
LSL_Vector pos = new LSL_Vector(presence.CameraPosition.X, presence.CameraPosition.Y, presence.CameraPosition.Z);
return pos;
}
return new LSL_Vector();
}
public LSL_Rotation llGetCameraRot()
{
m_host.AddScriptLPS(1);
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero)
return new LSL_Rotation();
m_host.TaskInventory.LockItemsForRead(true);
if (m_host.TaskInventory[invItemID].PermsGranter == UUID.Zero)
{
m_host.TaskInventory.LockItemsForRead(false);
return new LSL_Rotation();
}
if ((m_host.TaskInventory[invItemID].PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0)
{
ShoutError("No permissions to track the camera");
m_host.TaskInventory.LockItemsForRead(false);
return new LSL_Rotation();
}
m_host.TaskInventory.LockItemsForRead(false);
ScenePresence presence = World.GetScenePresence(m_host.OwnerID);
if (presence != null)
{
return new LSL_Rotation(presence.CameraRotation.X, presence.CameraRotation.Y, presence.CameraRotation.Z, presence.CameraRotation.W);
}
return new LSL_Rotation();
}
///
/// The SL implementation does nothing, it is deprecated
/// This duplicates SL
///
public void llSetPrimURL(string url)
{
m_host.AddScriptLPS(1);
ScriptSleep(2000);
}
///
/// The SL implementation shouts an error, it is deprecated
/// This duplicates SL
///
public void llRefreshPrimURL()
{
m_host.AddScriptLPS(1);
ShoutError("llRefreshPrimURL - not yet supported");
ScriptSleep(20000);
}
public LSL_String llEscapeURL(string url)
{
m_host.AddScriptLPS(1);
try
{
return Uri.EscapeDataString(url);
}
catch (Exception ex)
{
return "llEscapeURL: " + ex.ToString();
}
}
public LSL_String llUnescapeURL(string url)
{
m_host.AddScriptLPS(1);
try
{
return Uri.UnescapeDataString(url);
}
catch (Exception ex)
{
return "llUnescapeURL: " + ex.ToString();
}
}
public void llMapDestination(string simname, LSL_Vector pos, LSL_Vector lookAt)
{
m_host.AddScriptLPS(1);
DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_itemID, 0);
if (detectedParams == null)
{
if (m_host.ParentGroup.IsAttachment == true)
{
detectedParams = new DetectParams();
detectedParams.Key = m_host.OwnerID;
}
else
{
return;
}
}
ScenePresence avatar = World.GetScenePresence(detectedParams.Key);
if (avatar != null)
{
avatar.ControllingClient.SendScriptTeleportRequest(m_host.Name, simname,
new Vector3((float)pos.x, (float)pos.y, (float)pos.z),
new Vector3((float)lookAt.x, (float)lookAt.y, (float)lookAt.z));
}
ScriptSleep(1000);
}
public void llAddToLandBanList(string avatar, double hours)
{
m_host.AddScriptLPS(1);
UUID key;
ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned))
{
ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry();
if (UUID.TryParse(avatar, out key))
{
if (land.LandData.ParcelAccessList.FindIndex(
delegate(ParcelManager.ParcelAccessEntry e)
{
if (e.AgentID == key && e.Flags == AccessList.Ban)
return true;
return false;
}) == -1)
{
entry.AgentID = key;
entry.Flags = AccessList.Ban;
entry.Time = DateTime.Now.AddHours(hours);
land.LandData.ParcelAccessList.Add(entry);
}
}
}
ScriptSleep(100);
}
public void llRemoveFromLandPassList(string avatar)
{
m_host.AddScriptLPS(1);
UUID key;
ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageAllowed))
{
if (UUID.TryParse(avatar, out key))
{
int idx = land.LandData.ParcelAccessList.FindIndex(
delegate(ParcelManager.ParcelAccessEntry e)
{
if (e.AgentID == key && e.Flags == AccessList.Access)
return true;
return false;
});
if (idx != -1)
land.LandData.ParcelAccessList.RemoveAt(idx);
}
}
ScriptSleep(100);
}
public void llRemoveFromLandBanList(string avatar)
{
m_host.AddScriptLPS(1);
UUID key;
ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y);
if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned))
{
if (UUID.TryParse(avatar, out key))
{
int idx = land.LandData.ParcelAccessList.FindIndex(
delegate(ParcelManager.ParcelAccessEntry e)
{
if (e.AgentID == key && e.Flags == AccessList.Ban)
return true;
return false;
});
if (idx != -1)
land.LandData.ParcelAccessList.RemoveAt(idx);
}
}
ScriptSleep(100);
}
public void llSetCameraParams(LSL_List rules)
{
m_host.AddScriptLPS(1);
// our key in the object we are in
UUID invItemID = InventorySelf();
if (invItemID == UUID.Zero) return;
// the object we are in
UUID objectID = m_host.ParentUUID;
if (objectID == UUID.Zero) return;
UUID agentID;
m_host.TaskInventory.LockItemsForRead(true);
// we need the permission first, to know which avatar we want to set the camera for
agentID = m_host.TaskInventory[invItemID].PermsGranter;
if (agentID == UUID.Zero)
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
if ((m_host.TaskInventory[invItemID].PermsMask & ScriptBaseClass.PERMISSION_CONTROL_CAMERA) == 0)
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
m_host.TaskInventory.LockItemsForRead(false);
ScenePresence presence = World.GetScenePresence(agentID);
// we are not interested in child-agents
if (presence.IsChildAgent) return;
SortedDictionary parameters = new SortedDictionary();
object[] data = rules.Data;
for (int i = 0; i < data.Length; ++i)
{
int type = Convert.ToInt32(data[i++].ToString());
if (i >= data.Length) break; // odd number of entries => ignore the last
// some special cases: Vector parameters are split into 3 float parameters (with type+1, type+2, type+3)
switch (type)
{
case ScriptBaseClass.CAMERA_FOCUS:
case ScriptBaseClass.CAMERA_FOCUS_OFFSET:
case ScriptBaseClass.CAMERA_POSITION:
LSL_Vector v = (LSL_Vector)data[i];
parameters.Add(type + 1, (float)v.x);
parameters.Add(type + 2, (float)v.y);
parameters.Add(type + 3, (float)v.z);
break;
default:
// TODO: clean that up as soon as the implicit casts are in
if (data[i] is LSL_Float)
parameters.Add(type, (float)((LSL_Float)data[i]).value);
else if (data[i] is LSL_Integer)
parameters.Add(type, (float)((LSL_Integer)data[i]).value);
else parameters.Add(type, Convert.ToSingle(data[i]));
break;
}
}
if (parameters.Count > 0) presence.ControllingClient.SendSetFollowCamProperties(objectID, parameters);
}
public void llClearCameraParams()
{
m_host.AddScriptLPS(1);
// our key in the object we are in
UUID invItemID=InventorySelf();
if (invItemID == UUID.Zero) return;
// the object we are in
UUID objectID = m_host.ParentUUID;
if (objectID == UUID.Zero) return;
// we need the permission first, to know which avatar we want to clear the camera for
UUID agentID;
m_host.TaskInventory.LockItemsForRead(true);
agentID = m_host.TaskInventory[invItemID].PermsGranter;
if (agentID == UUID.Zero)
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
if ((m_host.TaskInventory[invItemID].PermsMask & ScriptBaseClass.PERMISSION_CONTROL_CAMERA) == 0)
{
m_host.TaskInventory.LockItemsForRead(false);
return;
}
m_host.TaskInventory.LockItemsForRead(false);
ScenePresence presence = World.GetScenePresence(agentID);
// we are not interested in child-agents
if (presence.IsChildAgent) return;
presence.ControllingClient.SendClearFollowCamProperties(objectID);
}
public LSL_Float llListStatistics(int operation, LSL_List src)
{
m_host.AddScriptLPS(1);
LSL_List nums = LSL_List.ToDoubleList(src);
switch (operation)
{
case ScriptBaseClass.LIST_STAT_RANGE:
return nums.Range();
case ScriptBaseClass.LIST_STAT_MIN:
return nums.Min();
case ScriptBaseClass.LIST_STAT_MAX:
return nums.Max();
case ScriptBaseClass.LIST_STAT_MEAN:
return nums.Mean();
case ScriptBaseClass.LIST_STAT_MEDIAN:
return nums.Median();
case ScriptBaseClass.LIST_STAT_NUM_COUNT:
return nums.NumericLength();
case ScriptBaseClass.LIST_STAT_STD_DEV:
return nums.StdDev();
case ScriptBaseClass.LIST_STAT_SUM:
return nums.Sum();
case ScriptBaseClass.LIST_STAT_SUM_SQUARES:
return nums.SumSqrs();
case ScriptBaseClass.LIST_STAT_GEOMETRIC_MEAN:
return nums.GeometricMean();
case ScriptBaseClass.LIST_STAT_HARMONIC_MEAN:
return nums.HarmonicMean();
default:
return 0.0;
}
}
public LSL_Integer llGetUnixTime()
{
m_host.AddScriptLPS(1);
return Util.UnixTimeSinceEpoch();
}
public LSL_Integer llGetParcelFlags(LSL_Vector pos)
{
m_host.AddScriptLPS(1);
return (int)World.LandChannel.GetLandObject((float)pos.x, (float)pos.y).LandData.Flags;
}
public LSL_Integer llGetRegionFlags()
{
m_host.AddScriptLPS(1);
IEstateModule estate = World.RequestModuleInterface();
if (estate == null)
return 67108864;
return (int)estate.GetRegionFlags();
}
public LSL_String llXorBase64StringsCorrect(string str1, string str2)
{
m_host.AddScriptLPS(1);
if (str1 == String.Empty)
return String.Empty;
if (str2 == String.Empty)
return str1;
int len = str2.Length;
if ((len % 4) != 0) // LL is EVIL!!!!
{
while (str2.EndsWith("="))
str2 = str2.Substring(0, str2.Length - 1);
len = str2.Length;
int mod = len % 4;
if (mod == 1)
str2 = str2.Substring(0, str2.Length - 1);
else if (mod == 2)
str2 += "==";
else if (mod == 3)
str2 += "=";
}
byte[] data1;
byte[] data2;
try
{
data1 = Convert.FromBase64String(str1);
data2 = Convert.FromBase64String(str2);
}
catch (Exception)
{
return new LSL_String(String.Empty);
}
byte[] d2 = new Byte[data1.Length];
int pos = 0;
if (data1.Length <= data2.Length)
{
Array.Copy(data2, 0, d2, 0, data1.Length);
}
else
{
while (pos < data1.Length)
{
len = data1.Length - pos;
if (len > data2.Length)
len = data2.Length;
Array.Copy(data2, 0, d2, pos, len);
pos += len;
}
}
for (pos = 0 ; pos < data1.Length ; pos++ )
data1[pos] ^= d2[pos];
return Convert.ToBase64String(data1);
}
public LSL_String llHTTPRequest(string url, LSL_List parameters, string body)
{
// Partial implementation: support for parameter flags needed
// see http://wiki.secondlife.com/wiki/LlHTTPRequest
// parameter flags support are implemented in ScriptsHttpRequests.cs
// in StartHttpRequest
m_host.AddScriptLPS(1);
IHttpRequestModule httpScriptMod =
m_ScriptEngine.World.RequestModuleInterface();
List param = new List();
foreach (object o in parameters.Data)
{
param.Add(o.ToString());
}
Vector3 position = m_host.AbsolutePosition;
Vector3 velocity = m_host.Velocity;
Quaternion rotation = m_host.RotationOffset;
string ownerName = String.Empty;
ScenePresence scenePresence = World.GetScenePresence(m_host.OwnerID);
if (scenePresence == null)
ownerName = resolveName(m_host.OwnerID);
else
ownerName = scenePresence.Name;
RegionInfo regionInfo = World.RegionInfo;
Dictionary httpHeaders = new Dictionary();
string shard = "OpenSim";
IConfigSource config = m_ScriptEngine.ConfigSource;
if (config.Configs["Network"] != null)
{
shard = config.Configs["Network"].GetString(
"user_server_url", "http://127.0.0.1:" + ConfigSettings.DefaultUserServerHttpPort.ToString());
shard = config.Configs["Network"].GetString("shard", shard);
}
httpHeaders["X-SecondLife-Shard"] = shard;
httpHeaders["X-SecondLife-Object-Name"] = m_host.Name;
httpHeaders["X-SecondLife-Object-Key"] = m_host.UUID.ToString();
httpHeaders["X-SecondLife-Region"] = string.Format("{0} ({1}, {2})", regionInfo.RegionName, regionInfo.RegionLocX, regionInfo.RegionLocY);
httpHeaders["X-SecondLife-Local-Position"] = string.Format("({0:0.000000}, {1:0.000000}, {2:0.000000})", position.X, position.Y, position.Z);
httpHeaders["X-SecondLife-Local-Velocity"] = string.Format("({0:0.000000}, {1:0.000000}, {2:0.000000})", velocity.X, velocity.Y, velocity.Z);
httpHeaders["X-SecondLife-Local-Rotation"] = string.Format("({0:0.000000}, {1:0.000000}, {2:0.000000}, {3:0.000000})", rotation.X, rotation.Y, rotation.Z, rotation.W);
httpHeaders["X-SecondLife-Owner-Name"] = ownerName;
httpHeaders["X-SecondLife-Owner-Key"] = m_host.OwnerID.ToString();
string userAgent = config.Configs["Network"].GetString("user_agent", null);
if (userAgent != null)
httpHeaders["User-Agent"] = userAgent;
string authregex = @"^(https?:\/\/)(\w+):(\w+)@(.*)$";
Regex r = new Regex(authregex);
int[] gnums = r.GetGroupNumbers();
Match m = r.Match(url);
if (m.Success)
{
for (int i = 1; i < gnums.Length; i++)
{
//System.Text.RegularExpressions.Group g = m.Groups[gnums[i]];
//CaptureCollection cc = g.Captures;
}
if (m.Groups.Count == 5)
{
httpHeaders["Authorization"] = String.Format("Basic {0}", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(m.Groups[2].ToString() + ":" + m.Groups[3].ToString())));
url = m.Groups[1].ToString() + m.Groups[4].ToString();
}
}
UUID reqID = httpScriptMod.
StartHttpRequest(m_localID, m_itemID, url, param, httpHeaders, body);
if (reqID != UUID.Zero)
return reqID.ToString();
else
return null;
}
public void llHTTPResponse(LSL_Key id, int status, string body)
{
// Partial implementation: support for parameter flags needed
// see http://wiki.secondlife.com/wiki/llHTTPResponse
m_host.AddScriptLPS(1);
if (m_UrlModule != null)
m_UrlModule.HttpResponse(new UUID(id), status,body);
}
public void llResetLandBanList()
{
m_host.AddScriptLPS(1);
LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData;
if (land.OwnerID == m_host.OwnerID)
{
foreach (ParcelManager.ParcelAccessEntry entry in land.ParcelAccessList)
{
if (entry.Flags == AccessList.Ban)
{
land.ParcelAccessList.Remove(entry);
}
}
}
ScriptSleep(100);
}
public void llResetLandPassList()
{
m_host.AddScriptLPS(1);
LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData;
if (land.OwnerID == m_host.OwnerID)
{
foreach (ParcelManager.ParcelAccessEntry entry in land.ParcelAccessList)
{
if (entry.Flags == AccessList.Access)
{
land.ParcelAccessList.Remove(entry);
}
}
}
ScriptSleep(100);
}
public LSL_Integer llGetParcelPrimCount(LSL_Vector pos, int category, int sim_wide)
{
m_host.AddScriptLPS(1);
ILandObject lo = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y);
if (lo == null)
return 0;
IPrimCounts pc = lo.PrimCounts;
if (sim_wide != ScriptBaseClass.FALSE)
{
if (category == ScriptBaseClass.PARCEL_COUNT_TOTAL)
{
return pc.Simulator;
}
else
{
// counts not implemented yet
return 0;
}
}
else
{
if (category == ScriptBaseClass.PARCEL_COUNT_TOTAL)
return pc.Total;
else if (category == ScriptBaseClass.PARCEL_COUNT_OWNER)
return pc.Owner;
else if (category == ScriptBaseClass.PARCEL_COUNT_GROUP)
return pc.Group;
else if (category == ScriptBaseClass.PARCEL_COUNT_OTHER)
return pc.Others;
else if (category == ScriptBaseClass.PARCEL_COUNT_SELECTED)
return pc.Selected;
else if (category == ScriptBaseClass.PARCEL_COUNT_TEMP)
return 0; // counts not implemented yet
}
return 0;
}
public LSL_List llGetParcelPrimOwners(LSL_Vector pos)
{
m_host.AddScriptLPS(1);
LandObject land = (LandObject)World.LandChannel.GetLandObject((float)pos.x, (float)pos.y);
LSL_List ret = new LSL_List();
if (land != null)
{
foreach (KeyValuePair detectedParams in land.GetLandObjectOwners())
{
ret.Add(new LSL_String(detectedParams.Key.ToString()));
ret.Add(new LSL_Integer(detectedParams.Value));
}
}
ScriptSleep(2000);
return ret;
}
public LSL_Integer llGetObjectPrimCount(string object_id)
{
m_host.AddScriptLPS(1);
SceneObjectPart part = World.GetSceneObjectPart(new UUID(object_id));
if (part == null)
{
return 0;
}
else
{
return part.ParentGroup.PrimCount;
}
}
public LSL_Integer llGetParcelMaxPrims(LSL_Vector pos, int sim_wide)
{
m_host.AddScriptLPS(1);
ILandObject lo = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y);
if (lo == null)
return 0;
if (sim_wide != 0)
return lo.GetSimulatorMaxPrimCount();
else
return lo.GetParcelMaxPrimCount();
}
public LSL_List llGetParcelDetails(LSL_Vector pos, LSL_List param)
{
m_host.AddScriptLPS(1);
LandData land = World.GetLandData((float)pos.x, (float)pos.y);
if (land == null)
{
return new LSL_List(0);
}
LSL_List ret = new LSL_List();
foreach (object o in param.Data)
{
switch (o.ToString())
{
case "0":
ret.Add(new LSL_String(land.Name));
break;
case "1":
ret.Add(new LSL_String(land.Description));
break;
case "2":
ret.Add(new LSL_Key(land.OwnerID.ToString()));
break;
case "3":
ret.Add(new LSL_Key(land.GroupID.ToString()));
break;
case "4":
ret.Add(new LSL_Integer(land.Area));
break;
case "5":
ret.Add(new LSL_Key(land.GlobalID.ToString()));
break;
default:
ret.Add(new LSL_Integer(0));
break;
}
}
return ret;
}
public LSL_String llStringTrim(string src, int type)
{
m_host.AddScriptLPS(1);
if (type == (int)ScriptBaseClass.STRING_TRIM_HEAD) { return src.TrimStart(); }
if (type == (int)ScriptBaseClass.STRING_TRIM_TAIL) { return src.TrimEnd(); }
if (type == (int)ScriptBaseClass.STRING_TRIM) { return src.Trim(); }
return src;
}
public LSL_List llGetObjectDetails(string id, LSL_List args)
{
m_host.AddScriptLPS(1);
LSL_List ret = new LSL_List();
UUID key = new UUID();
if (UUID.TryParse(id, out key))
{
ScenePresence av = World.GetScenePresence(key);
if (av != null)
{
foreach (object o in args.Data)
{
switch (int.Parse(o.ToString()))
{
case ScriptBaseClass.OBJECT_NAME:
ret.Add(new LSL_String(av.Firstname + " " + av.Lastname));
break;
case ScriptBaseClass.OBJECT_DESC:
ret.Add(new LSL_String(""));
break;
case ScriptBaseClass.OBJECT_POS:
ret.Add(new LSL_Vector((double)av.AbsolutePosition.X, (double)av.AbsolutePosition.Y, (double)av.AbsolutePosition.Z));
break;
case ScriptBaseClass.OBJECT_ROT:
ret.Add(new LSL_Rotation((double)av.Rotation.X, (double)av.Rotation.Y, (double)av.Rotation.Z, (double)av.Rotation.W));
break;
case ScriptBaseClass.OBJECT_VELOCITY:
ret.Add(new LSL_Vector(av.Velocity.X, av.Velocity.Y, av.Velocity.Z));
break;
case ScriptBaseClass.OBJECT_OWNER:
ret.Add(new LSL_String(id));
break;
case ScriptBaseClass.OBJECT_GROUP:
ret.Add(new LSL_String(UUID.Zero.ToString()));
break;
case ScriptBaseClass.OBJECT_CREATOR:
ret.Add(new LSL_String(UUID.Zero.ToString()));
break;
}
}
return ret;
}
SceneObjectPart obj = World.GetSceneObjectPart(key);
if (obj != null)
{
foreach (object o in args.Data)
{
switch (int.Parse(o.ToString()))
{
case ScriptBaseClass.OBJECT_NAME:
ret.Add(new LSL_String(obj.Name));
break;
case ScriptBaseClass.OBJECT_DESC:
ret.Add(new LSL_String(obj.Description));
break;
case ScriptBaseClass.OBJECT_POS:
ret.Add(new LSL_Vector(obj.AbsolutePosition.X, obj.AbsolutePosition.Y, obj.AbsolutePosition.Z));
break;
case ScriptBaseClass.OBJECT_ROT:
ret.Add(new LSL_Rotation(obj.RotationOffset.X, obj.RotationOffset.Y, obj.RotationOffset.Z, obj.RotationOffset.W));
break;
case ScriptBaseClass.OBJECT_VELOCITY:
ret.Add(new LSL_Vector(obj.Velocity.X, obj.Velocity.Y, obj.Velocity.Z));
break;
case ScriptBaseClass.OBJECT_OWNER:
ret.Add(new LSL_String(obj.OwnerID.ToString()));
break;
case ScriptBaseClass.OBJECT_GROUP:
ret.Add(new LSL_String(obj.GroupID.ToString()));
break;
case ScriptBaseClass.OBJECT_CREATOR:
ret.Add(new LSL_String(obj.CreatorID.ToString()));
break;
}
}
return ret;
}
}
return new LSL_List();
}
internal UUID ScriptByName(string name)
{
m_host.TaskInventory.LockItemsForRead(true);
foreach (TaskInventoryItem item in m_host.TaskInventory.Values)
{
if (item.Type == 10 && item.Name == name)
{
m_host.TaskInventory.LockItemsForRead(false);
return item.ItemID;
}
}
m_host.TaskInventory.LockItemsForRead(false);
return UUID.Zero;
}
internal void ShoutError(string msg)
{
llShout(ScriptBaseClass.DEBUG_CHANNEL, msg);
}
internal void NotImplemented(string command)
{
if (throwErrorOnNotImplemented)
throw new NotImplementedException("Command not implemented: " + command);
}
internal void Deprecated(string command)
{
throw new Exception("Command deprecated: " + command);
}
internal void LSLError(string msg)
{
throw new Exception("LSL Runtime Error: " + msg);
}
public delegate void AssetRequestCallback(UUID assetID, AssetBase asset);
protected void WithNotecard(UUID assetID, AssetRequestCallback cb)
{
World.AssetService.Get(assetID.ToString(), this,
delegate(string i, object sender, AssetBase a)
{
UUID uuid = UUID.Zero;
UUID.TryParse(i, out uuid);
cb(uuid, a);
});
}
public LSL_String llGetNumberOfNotecardLines(string name)
{
m_host.AddScriptLPS(1);
//Clone is thread safe
TaskInventoryDictionary itemsDictionary = (TaskInventoryDictionary)m_host.TaskInventory.Clone();
UUID assetID = UUID.Zero;
if (!UUID.TryParse(name, out assetID))
{
foreach (TaskInventoryItem item in itemsDictionary.Values)
{
if (item.Type == 7 && item.Name == name)
{
assetID = item.AssetID;
break;
}
}
}
if (assetID == UUID.Zero)
{
// => complain loudly, as specified by the LSL docs
ShoutError("Notecard '" + name + "' could not be found.");
return UUID.Zero.ToString();
}
// was: UUID tid = tid = AsyncCommands.
UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, assetID.ToString());
if (NotecardCache.IsCached(assetID))
{
AsyncCommands.
DataserverPlugin.DataserverReply(assetID.ToString(),
NotecardCache.GetLines(assetID).ToString());
ScriptSleep(100);
return tid.ToString();
}
WithNotecard(assetID, delegate (UUID id, AssetBase a)
{
if (a == null || a.Type != 7)
{
ShoutError("Notecard '" + name + "' could not be found.");
return;
}
System.Text.UTF8Encoding enc =
new System.Text.UTF8Encoding();
string data = enc.GetString(a.Data);
//m_log.Debug(data);
NotecardCache.Cache(id, data);
AsyncCommands.
DataserverPlugin.DataserverReply(id.ToString(),
NotecardCache.GetLines(id).ToString());
});
ScriptSleep(100);
return tid.ToString();
}
public LSL_String llGetNotecardLine(string name, int line)
{
m_host.AddScriptLPS(1);
//Clone is thread safe
TaskInventoryDictionary itemsDictionary = (TaskInventoryDictionary)m_host.TaskInventory.Clone();
UUID assetID = UUID.Zero;
if (!UUID.TryParse(name, out assetID))
{
foreach (TaskInventoryItem item in itemsDictionary.Values)
{
if (item.Type == 7 && item.Name == name)
{
assetID = item.AssetID;
break;
}
}
}
if (assetID == UUID.Zero)
{
// => complain loudly, as specified by the LSL docs
ShoutError("Notecard '" + name + "' could not be found.");
return UUID.Zero.ToString();
}
// was: UUID tid = tid = AsyncCommands.
UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, assetID.ToString());
if (NotecardCache.IsCached(assetID))
{
AsyncCommands.DataserverPlugin.DataserverReply(assetID.ToString(),
NotecardCache.GetLine(assetID, line, m_notecardLineReadCharsMax));
ScriptSleep(100);
return tid.ToString();
}
WithNotecard(assetID, delegate (UUID id, AssetBase a)
{
if (a == null || a.Type != 7)
{
ShoutError("Notecard '" + name + "' could not be found.");
return;
}
System.Text.UTF8Encoding enc =
new System.Text.UTF8Encoding();
string data = enc.GetString(a.Data);
//m_log.Debug(data);
NotecardCache.Cache(id, data);
AsyncCommands.DataserverPlugin.DataserverReply(id.ToString(),
NotecardCache.GetLine(id, line, m_notecardLineReadCharsMax));
});
ScriptSleep(100);
return tid.ToString();
}
public void SetPrimitiveParamsEx(LSL_Key prim, LSL_List rules)
{
SceneObjectPart obj = World.GetSceneObjectPart(new UUID(prim));
if (obj == null)
return;
if (obj.OwnerID != m_host.OwnerID)
return;
SetPrimParams(obj, rules);
}
public LSL_List GetLinkPrimitiveParamsEx(LSL_Key prim, LSL_List rules)
{
SceneObjectPart obj = World.GetSceneObjectPart(new UUID(prim));
if (obj == null)
return new LSL_List();
if (obj.OwnerID != m_host.OwnerID)
return new LSL_List();
return GetLinkPrimitiveParams(obj, rules);
}
public LSL_Integer llGetLinkNumberOfSides(LSL_Integer link)
{
List parts = GetLinkParts(link);
if (parts.Count < 1)
return 0;
return GetNumberOfSides(parts[0]);
}
private string Name2Username(string name)
{
string[] parts = name.Split(new char[] {' '});
if (parts.Length < 2)
return name.ToLower();
if (parts[1] == "Resident")
return parts[0].ToLower();
return name.Replace(" ", ".").ToLower();
}
public LSL_String llGetUsername(string id)
{
return Name2Username(llKey2Name(id));
}
public LSL_String llRequestUsername(string id)
{
UUID rq = UUID.Random();
AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, rq.ToString());
AsyncCommands.DataserverPlugin.DataserverReply(rq.ToString(), Name2Username(llKey2Name(id)));
return rq.ToString();
}
public LSL_String llGetDisplayName(string id)
{
return llKey2Name(id);
}
public LSL_String llRequestDisplayName(string id)
{
UUID rq = UUID.Random();
AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, rq.ToString());
AsyncCommands.DataserverPlugin.DataserverReply(rq.ToString(), llKey2Name(id));
return rq.ToString();
}
private void SayShoutTimerElapsed(Object sender, ElapsedEventArgs args)
{
m_SayShoutCount = 0;
}
public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options)
{
m_host.AddScriptLPS(1);
Vector3 dir = new Vector3((float)(end-start).x, (float)(end-start).y, (float)(end-start).z);
Vector3 startvector = new Vector3((float)start.x, (float)start.y, (float)start.z);
Vector3 endvector = new Vector3((float)end.x, (float)end.y, (float)end.z);
int count = 0;
// int detectPhantom = 0;
int dataFlags = 0;
int rejectTypes = 0;
for (int i = 0; i < options.Length; i += 2)
{
if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS)
{
count = options.GetLSLIntegerItem(i + 1);
}
// else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM)
// {
// detectPhantom = options.GetLSLIntegerItem(i + 1);
// }
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS)
{
dataFlags = options.GetLSLIntegerItem(i + 1);
}
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES)
{
rejectTypes = options.GetLSLIntegerItem(i + 1);
}
}
LSL_List list = new LSL_List();
List results = World.PhysicsScene.RaycastWorld(startvector, dir, dir.Length(), count);
double distance = Util.GetDistanceTo(startvector, endvector);
if (distance == 0)
distance = 0.001;
Vector3 posToCheck = startvector;
ITerrainChannel channel = World.RequestModuleInterface();
bool checkTerrain = !((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) == ScriptBaseClass.RC_REJECT_LAND);
bool checkAgents = !((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) == ScriptBaseClass.RC_REJECT_AGENTS);
bool checkNonPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) == ScriptBaseClass.RC_REJECT_NONPHYSICAL);
bool checkPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) == ScriptBaseClass.RC_REJECT_PHYSICAL);
for (float i = 0; i <= distance; i += 0.1f)
{
posToCheck = startvector + (dir * (i / (float)distance));
if (checkTerrain && channel[(int)(posToCheck.X + startvector.X), (int)(posToCheck.Y + startvector.Y)] < posToCheck.Z)
{
ContactResult result = new ContactResult();
result.ConsumerID = 0;
result.Depth = 0;
result.Normal = Vector3.Zero;
result.Pos = posToCheck;
results.Add(result);
checkTerrain = false;
}
if (checkAgents)
{
World.ForEachScenePresence(delegate(ScenePresence sp)
{
if (sp.AbsolutePosition.ApproxEquals(posToCheck, sp.PhysicsActor.Size.X))
{
ContactResult result = new ContactResult ();
result.ConsumerID = sp.LocalId;
result.Depth = 0;
result.Normal = Vector3.Zero;
result.Pos = posToCheck;
results.Add(result);
}
});
}
}
int refcount = 0;
foreach (ContactResult result in results)
{
if ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND)
== ScriptBaseClass.RC_REJECT_LAND && result.ConsumerID == 0)
continue;
ISceneEntity entity = World.GetSceneObjectPart(result.ConsumerID);
if (entity == null && (rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != ScriptBaseClass.RC_REJECT_AGENTS)
entity = World.GetScenePresence(result.ConsumerID); //Only check if we should be looking for agents
if (entity == null)
{
list.Add(UUID.Zero);
if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM)
list.Add(0);
list.Add(result.Pos);
if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL)
list.Add(result.Normal);
continue; //Can't find it, so add UUID.Zero
}
/*if (detectPhantom == 0 && intersection.obj is ISceneChildEntity &&
((ISceneChildEntity)intersection.obj).PhysActor == null)
continue;*/ //Can't do this ATM, physics engine knows only of non phantom objects
if (entity is SceneObjectPart)
{
if (((SceneObjectPart)entity).PhysActor != null && ((SceneObjectPart)entity).PhysActor.IsPhysical)
{
if (!checkPhysical)
continue;
}
else
{
if (!checkNonPhysical)
continue;
}
}
refcount++;
if ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) == ScriptBaseClass.RC_GET_ROOT_KEY && entity is SceneObjectPart)
list.Add(((SceneObjectPart)entity).ParentGroup.UUID);
else
list.Add(entity.UUID);
if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM)
{
if (entity is SceneObjectPart)
list.Add(((SceneObjectPart)entity).LinkNum);
else
list.Add(0);
}
list.Add(result.Pos);
if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL)
list.Add(result.Normal);
}
list.Add(refcount); //The status code, either the # of contacts, RCERR_SIM_PERF_LOW, or RCERR_CAST_TIME_EXCEEDED
return list;
}
#region Not Implemented
//
// Listing the unimplemented lsl functions here, please move
// them from this region as they are completed
//
public void llGetEnv(LSL_String name)
{
m_host.AddScriptLPS(1);
NotImplemented("llGetEnv");
}
public void llGetSPMaxMemory()
{
m_host.AddScriptLPS(1);
NotImplemented("llGetSPMaxMemory");
}
public virtual LSL_Integer llGetUsedMemory()
{
m_host.AddScriptLPS(1);
NotImplemented("llGetUsedMemory");
return 0;
}
public void llScriptProfiler(LSL_Integer flags)
{
m_host.AddScriptLPS(1);
//NotImplemented("llScriptProfiler");
}
public void llSetSoundQueueing(int queue)
{
m_host.AddScriptLPS(1);
}
public void llCollisionSprite(string impact_sprite)
{
m_host.AddScriptLPS(1);
NotImplemented("llCollisionSprite");
}
public void llGodLikeRezObject(string inventory, LSL_Vector pos)
{
m_host.AddScriptLPS(1);
if (!World.Permissions.IsGod(m_host.OwnerID))
NotImplemented("llGodLikeRezObject");
AssetBase rezAsset = World.AssetService.Get(inventory);
if (rezAsset == null)
{
llSay(0, "Asset not found");
return;
}
SceneObjectGroup group = null;
try
{
string xmlData = Utils.BytesToString(rezAsset.Data);
group = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
}
catch
{
llSay(0, "Asset not found");
return;
}
if (group == null)
{
llSay(0, "Asset not found");
return;
}
group.RootPart.AttachPoint = group.RootPart.Shape.State;
group.RootPart.AttachOffset = group.AbsolutePosition;
group.ResetIDs();
Vector3 llpos = new Vector3((float)pos.x, (float)pos.y, (float)pos.z);
World.AddNewSceneObject(group, true, llpos, Quaternion.Identity, Vector3.Zero);
group.CreateScriptInstances(0, true, World.DefaultScriptEngine, 3);
group.ScheduleGroupForFullUpdate();
// 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]));
}
#endregion
}
public class NotecardCache
{
protected class Notecard
{
public string[] text;
public DateTime lastRef;
}
protected static Dictionary m_Notecards =
new Dictionary();
public static void Cache(UUID assetID, string text)
{
CacheCheck();
lock (m_Notecards)
{
if (m_Notecards.ContainsKey(assetID))
return;
Notecard nc = new Notecard();
nc.lastRef = DateTime.Now;
nc.text = SLUtil.ParseNotecardToList(text).ToArray();
m_Notecards[assetID] = nc;
}
}
public static bool IsCached(UUID assetID)
{
lock (m_Notecards)
{
return m_Notecards.ContainsKey(assetID);
}
}
public static int GetLines(UUID assetID)
{
if (!IsCached(assetID))
return -1;
lock (m_Notecards)
{
m_Notecards[assetID].lastRef = DateTime.Now;
return m_Notecards[assetID].text.Length;
}
}
///
/// Get a notecard line.
///
///
/// Lines start at index 0
///
public static string GetLine(UUID assetID, int lineNumber)
{
if (lineNumber < 0)
return "";
string data;
if (!IsCached(assetID))
return "";
lock (m_Notecards)
{
m_Notecards[assetID].lastRef = DateTime.Now;
if (lineNumber >= m_Notecards[assetID].text.Length)
return "\n\n\n";
data = m_Notecards[assetID].text[lineNumber];
return data;
}
}
///
/// Get a notecard line.
///
///
/// Lines start at index 0
/// Maximum length of the returned line. Longer lines will be truncated
///
public static string GetLine(UUID assetID, int lineNumber, int maxLength)
{
string line = GetLine(assetID, lineNumber);
if (line.Length > maxLength)
line = line.Substring(0, maxLength);
return line;
}
public static void CacheCheck()
{
foreach (UUID key in new List(m_Notecards.Keys))
{
Notecard nc = m_Notecards[key];
if (nc.lastRef.AddSeconds(30) < DateTime.Now)
m_Notecards.Remove(key);
}
}
}
}