LSLEditor/trunk/SecondLife/SecondLifeMain.cs
2013-07-16 16:47:13 +01:00

716 lines
23 KiB
C#

// /**
// ********
// *
// * ORIGINAL CODE BASE IS Copyright (C) 2006-2010 by Alphons van der Heijden
// * The code was donated on 4/28/2010 by Alphons van der Heijden
// * To Brandon 'Dimentox Travanti' Husbands & Malcolm J. Kudra, who in turn License under the GPLv2.
// * In agreement with Alphons van der Heijden's wishes.
// *
// * The community would like to thank Alphons for all of his hard work, blood sweat and tears.
// * Without his work the community would be stuck with crappy editors.
// *
// * The source code in this file ("Source Code") is provided by The LSLEditor Group
// * to you under the terms of the GNU General Public License, version 2.0
// * ("GPL"), unless you have obtained a separate licensing agreement
// * ("Other License"), formally executed by you and The LSLEditor Group. Terms of
// * the GPL can be found in the gplv2.txt document.
// *
// ********
// * GPLv2 Header
// ********
// * LSLEditor, a External editor for the LSL Language.
// * Copyright (C) 2010 The LSLEditor Group.
//
// * This program is free software; you can redistribute it and/or
// * modify it under the terms of the GNU General Public License
// * as published by the Free Software Foundation; either version 2
// * of the License, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with this program; if not, write to the Free Software
// * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// ********
// *
// * The above copyright notice and this permission notice shall be included in all
// * copies or substantial portions of the Software.
// *
// ********
// */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Text.RegularExpressions;
/*
// (C) 2006,2007 Alphons van der Heijden
// mail: alphons@heijden.com
*/
namespace LSLEditor
{
/// <summary>
/// Enumeration of Communication Types that the host understands.
/// </summary>
public enum CommunicationType
{
/// <summary>
/// Indicates the message can be heard only within 5m.
/// </summary>
Whisper,
/// <summary>
/// Indicates the message can be heard only within 20m.
/// </summary>
Say,
/// <summary>
/// Indicates the message can be heard only within 100m.
/// </summary>
Shout,
/// <summary>
/// Indicates the message can be only heard by the owner any where within the same simulator.
/// </summary>
OwnerSay,
/// <summary>
/// Indicates the message can be heard any where within the same simulator.
/// </summary>
RegionSay,
/// <summary>
/// Indicates the message can be heard any where within the same simulator, by a specific object.
/// </summary>
RegionSayTo
}
/// <summary>
/// Partial definition of SecondLife class to handle running scripts.
/// </summary>
public partial class SecondLife
{
/// <summary>
/// Holds the host object.
/// </summary>
private SecondLifeHost slhHost;
#region members
/// <summary>
/// Random generator.
/// </summary>
private Random rdmRandom;
/// <summary>
/// Holds the time of the script starting execution.
/// </summary>
private DateTime dtDateTimeScriptStarted;
/// <summary>
/// Contains a boolean value indicating whether this object accepts other items to be dropped into it.
/// </summary>
private bool blnAllowDrop = false;
/// <summary>
/// Contains a list of keys of avatars that may enter a parcel.
/// </summary>
private Hashtable htLandPassList;
/// <summary>
/// Contains a list of keys of avatars that may NOT enter a parcel.
/// </summary>
private Hashtable htLandBanList;
/// <summary>
/// Volume of sound played by this prim.
/// </summary>
private Float fVolume;
/// <summary>
/// Name of the object/prim.
/// </summary>
private String sObjectName;
/// <summary>
/// URL for parcel's music stream.
/// </summary>
private String sParcelMusicURL;
/// <summary>
/// Position of object/prim placement in a simulator.
/// </summary>
private vector vPosition;
/// <summary>
/// Rotation of the object/prim's placement in the simulator.
/// </summary>
private rotation rRotation;
/// <summary>
/// Local rotation of the prim with respect to the object root.
/// </summary>
private rotation rRotationlocal;
/// <summary>
/// Scale of the object/prim.
/// </summary>
private vector vScale;
/// <summary>
/// Text for the "Sit" entry on object menu.
/// </summary>
private String sSitText;
/// <summary>
/// Radius that sound may be heard around the object/prim.
/// </summary>
private Float fSoundRadius;
/// <summary>
/// Region Coordinates of the simulator's bottom-left corner.
/// </summary>
private vector vRegionCorner;
/// <summary>
/// Parameter passed to the script on startup.
/// </summary>
private integer iStartParameter;
#endregion
#region Constructor
/// <summary>
/// Initialises a new instance of the <see cref="SecondLife"/> class.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText", Justification = "Reviewed.")]
public SecondLife()
{
this.host = null;
rdmRandom = new Random();
dtDateTimeScriptStarted = DateTime.Now.ToUniversalTime();
htLandPassList = new Hashtable();
htLandBanList = new Hashtable();
fVolume = 0.0;
sObjectName = null;
vPosition = new vector(127, 128, 20);
rRotation = rotation.ZERO_ROTATION;
rRotationlocal = rotation.ZERO_ROTATION;
vScale = vector.ZERO_VECTOR;
sSitText = "sittext";
fSoundRadius = 1.0;
iStartParameter = 0;
vRegionCorner = vector.ZERO_VECTOR;
}
#endregion
#region Properties
/// <summary>
/// Gets the position of the object within the simulator.
/// </summary>
/// <value>Gets the vPosition data member.</value>
public vector GetLocalPos
{
// TODO change this to use a call to llGetLocalPos specifying NOT to be Verbose. After all functions have been changed to allow verbose/silent option.
get { return vPosition; }
}
/// <summary>
/// Gets or sets the SecondLifeHost object.
/// </summary>
/// <value>The host property gets/sets the slhHost data member.</value>
public SecondLifeHost host
{
get { return this.slhHost; }
set { this.slhHost = value; }
}
#endregion
#region internal routines
/// <summary>
/// Outputs messages during execution of the script.
/// </summary>
/// <param name="strLine">The text (with possible placeholders) to be output.</param>
/// <param name="parameters">Values to be put into corresponding placeholders.</param>
private void Verbose(string strLine, params object[] parameters)
{
if (parameters.Length == 0) {
host.VerboseMessage(strLine);
} else {
this.host.VerboseMessage(string.Format(strLine, parameters));
}
}
/// <summary>
/// Wrapper to call the SecondLifeHost.Chat method.
/// </summary>
/// <param name="iChannel">Channel for message to be sent on.</param>
/// <param name="sText">Text of message to send.</param>
/// <param name="ctHow">Type of communication (from CommunicationType enumerator).</param>
private void Chat(integer iChannel, string sText, CommunicationType ctHow)
{
this.host.Chat(this.host, iChannel, this.host.GetObjectName(), this.host.GetKey(), sText, ctHow);
}
/// <summary>
/// Method to trigger change in script state.
/// </summary>
/// <param name="strStateName">Name of state to switch to.</param>
public void state(string strStateName)
{
Verbose("state->" + strStateName);
host.State(strStateName, false);
System.Threading.Thread.Sleep(1000);
System.Windows.Forms.MessageBox.Show("If you see this, something is wrong", "Oops...");
}
#endregion
#region Helper Functions
#region List Functions
/// <summary>
/// Takes the possibly negative value list indexes and corrects them to be absolute indexes.
/// </summary>
/// <param name="intLength">Length of list.</param>
/// <param name="intStart">Start index.</param>
/// <param name="intEnd">End index.</param>
/// <returns>True, unless an error occurred, then false.</returns>
private bool CorrectIt(int intLength, ref int intStart, ref int intEnd)
{
bool bResult = true;
if (intStart < 0) {
intStart = intLength + intStart;
}
if (intEnd < 0) {
intEnd = intLength + intEnd;
}
if (intStart <= intEnd) {
if (intStart >= intLength) {
bResult = false;
}
if (intEnd < 0) {
bResult = false;
}
}
intStart = Math.Max(0, intStart);
intEnd = Math.Min(intLength - 1, intEnd);
return bResult;
}
/// <summary>
/// Randomly rearranges the list of items.
/// </summary>
/// <param name="alCollection">List to shuffle.</param>
/// <returns>The reordered list.</returns>
public ArrayList RandomShuffle(ArrayList alCollection)
{
/* We have to copy all items anyway, and there isn't a way to produce the items
on the fly that is linear. So copying to an array and shuffling it is as efficient as we can get.
*/
if (alCollection == null) {
throw new ArgumentNullException("collection");
}
int intCount = alCollection.Count;
for (int i = intCount - 1; i >= 1; --i) {
// Pick an random number 0 through i inclusive.
int j = rdmRandom.Next(i + 1);
// Swap array[i] and array[j]
object temp = alCollection[i];
alCollection[i] = alCollection[j];
alCollection[j] = temp;
}
return alCollection;
}
/// <summary>
/// Convert a list into an array of buckets containing Stride elements.
/// </summary>
/// <param name="lSource">List of items.</param>
/// <param name="intStride">Number of element for each bucket.</param>
/// <returns>The list separated into elements of Stride length.</returns>
private ArrayList List2Buckets(list lSource, int intStride)
{
ArrayList alBuckets = null;
if ((lSource.Count % intStride) == 0 && intStride <= lSource.Count) {
alBuckets = new ArrayList();
for (int intI = 0; intI < lSource.Count; intI += intStride) {
object[] bucket = new object[intStride];
for (int intJ = 0; intJ < intStride; intJ++) {
bucket[intJ] = lSource[intI + intJ];
}
alBuckets.Add(bucket);
}
}
return alBuckets;
}
/// <summary>
/// Converts buckets back into a list.
/// </summary>
/// <param name="alBuckets">Array of buckets.</param>
/// <param name="intStride">Stride value.</param>
/// <returns>The items recombined into a simple list.</returns>
private list Buckets2List(ArrayList alBuckets, int intStride)
{
object[] items = new object[alBuckets.Count * intStride];
for (int intI = 0; intI < alBuckets.Count; intI++) {
for (int intJ = 0; intJ < intStride; intJ++) {
items[(intI * intStride) + intJ] = ((object[])alBuckets[intI])[intJ];
}
}
return new list(items);
}
/// <summary>
/// Implements the Comparer Interface for our custom types (Float, Integer, and String).
/// </summary>
private class BucketComparer : IComparer
{
/// <summary>
/// True/false parameter indicating whether the comparison should be done in ascending or descending order.
/// </summary>
private integer iAscending;
/// <summary>
/// Initialises a new instance of the <see cref="BucketComparer"/> class.
/// </summary>
/// <param name="ascending"></param>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText", Justification = "Reviewed.")]
public BucketComparer(integer ascending)
{
this.iAscending = ascending;
}
/// <summary>
/// Perform the comparison between two objects.
/// </summary>
/// <param name="x">First object.</param>
/// <param name="y">Second object.</param>
/// <returns>An integer (-1, 0, or 1), indicating whether the first item is less than, same as, or greater than the second.</returns>
public int Compare(object x, object y)
{
int iResult = 0;
object objA, objB;
object[] xx = x as object[];
object[] yy = y as object[];
if (iAscending == TRUE) {
objA = xx[0];
objB = yy[0];
} else {
objB = xx[0];
objA = yy[0];
}
string strType = objA.GetType().ToString();
if (((objA is string) && (objB is string)) || ((objA is SecondLife.String) && (objB is SecondLife.String))) {
iResult = string.Compare(objA.ToString(), objB.ToString());
} else if ((objA is SecondLife.integer) && (objB is SecondLife.integer)) {
iResult = SecondLife.integer.Compare((SecondLife.integer)objA, (SecondLife.integer)objB);
} else if ((objA is SecondLife.Float) && (objB is SecondLife.Float)) {
iResult = SecondLife.Float.Compare((SecondLife.Float)objA, (SecondLife.Float)objB);
}
return iResult;
}
}
#endregion
#region String Functions
/// <summary>
/// Takes a string and splits it into a list based upon separators (which are not kept) and spacers (which are).
/// </summary>
/// <param name="sSource">The source text.</param>
/// <param name="lSeparators">Separators to split on.</param>
/// <param name="lSpacers">Spacers to split on.</param>
/// <param name="blnKeepNulls">True/false indicating whether nulls are kept in the resulting list.</param>
/// <returns>A new list of the split string.</returns>
private list ParseString(String sSource, list lSeparators, list lSpacers, bool blnKeepNulls)
{
list lResult = new list();
int intFrom = 0;
string s = sSource;
for (int intI = 0; intI < s.Length; intI++) {
string strTmp = s.Substring(intI);
bool blnFound = false;
for (int intJ = 0; intJ < lSeparators.Count; intJ++) {
string strSeparator = lSeparators[intJ].ToString();
if (strSeparator.Length == 0) {
continue; // check this
}
if (strTmp.IndexOf(strSeparator) == 0) {
string strBefore = s.Substring(intFrom, intI - intFrom);
if (strBefore != "" || blnKeepNulls) {
lResult.Add(strBefore);
}
intI += strSeparator.Length - 1;
intFrom = intI + 1;
blnFound = true;
break;
}
}
if (blnFound) {
continue;
}
for (int intJ = 0; intJ < lSpacers.Count; intJ++) {
string strSpacer = lSpacers[intJ].ToString();
if (strSpacer.Length == 0) {
continue; // check this
}
if (strTmp.IndexOf(strSpacer) == 0) {
string strBefore = s.Substring(intFrom, intI - intFrom);
if (strBefore != "" || blnKeepNulls) {
lResult.Add(strBefore);
}
lResult.Add(strSpacer);
intI += strSpacer.Length - 1;
intFrom = intI + 1;
break;
}
}
}
string strLast = s.Substring(intFrom);
if (strLast != "" || blnKeepNulls) {
lResult.Add(strLast);
}
return lResult;
}
/// <summary>
/// Convert string to Base64 representation.
/// </summary>
/// <param name="strText">Source string.</param>
/// <returns>Base64 encoding of the source string.</returns>
private string StringToBase64(string strText)
{
byte[] data = Encoding.UTF8.GetBytes(strText);
return Convert.ToBase64String(data);
}
/// <summary>
/// Converts Base64 encoded string back to UTF-8.
/// </summary>
/// <param name="strText">Base64 encoded string.</param>
/// <returns>UTF-8 string.</returns>
private string Base64ToString(string strText)
{
byte[] data = Convert.FromBase64String(strText);
int intLen = Array.IndexOf(data, (byte)0x00);
if (intLen < 0) {
intLen = data.Length;
}
return Encoding.UTF8.GetString(data, 0, intLen);
}
/// <summary>
/// Performs the lookup.
/// </summary>
/// <param name="s">String source.</param>
/// <param name="intIndex">Byte within string.</param>
/// <returns>The Base64 value of the element.</returns>
private int LookupBase64(string s, int intIndex)
{
int intReturn = 0;
if (intIndex < s.Length) {
intReturn = FastLookupBase64[s[intIndex]];
if (intReturn == 0) {
if (s[intIndex] != 'A') {
throw new Exception();
}
}
} else {
intReturn = 0;
}
return intReturn;
}
/// <summary>
/// Pre-generated matrix for quicker lookup.
/// </summary>
////[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "Stays with other string functions.")]
private static readonly int[] FastLookupBase64 =
{ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, // 20
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, // 30
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, // 50
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, // 70
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // F0
#endregion
#region Math Functions
/// <summary>
/// Performs (iA ** iB) % iC.
/// </summary>
/// <param name="iA">Base value.</param>
/// <param name="iB">Exponent value .</param>
/// <param name="iC">Modulus value.</param>
/// <returns>The calculated result.</returns>
private integer ModPow1(integer iA, integer iB, integer iC)
{
return (int)Math.Pow((int)iA, (int)iB & (int)0xffff) % (int)iC;
}
/// <summary>
/// Performs the llModPow2() function.
/// </summary>
/// <param name="iValueX">Value one.</param>
/// <param name="iValueY">Value two.</param>
/// <param name="iModulus">The modulus value.</param>
/// <returns>The calculated result.</returns>
private integer ModPow2(integer iValueX, integer iValueY, integer iModulus)
{
integer iResult = 0;
if (iValueX != 0) {
integer k = 1 + (int)Math.Ceiling(Math.Log(Math.Abs(iValueX)) / 0.69314718055994530941723212145818); // ceil(log2(x))
integer w = 32;
integer p = w / k;
integer r = iValueY / p;
integer f = iValueY % p;
integer z = 1;
if (r) {
z = ModPow2(ModPow1(iValueX, p, iModulus), r, iModulus);
}
if (f) {
z = (z * ModPow1(iValueX, f, iModulus)) % iModulus;
}
iResult = z;
}
return iResult;
}
#endregion Math Functions
/// <summary>
/// Used by llListStatistics in generating the statistics.
/// </summary>
/// <param name="lInput">List of numbers (mixed types possible) to use.</param>
/// <returns>The numbers added together as a Float type.</returns>
private List<double> GetListOfNumbers(list lInput)
{
List<double> lResult = new List<double>();
for (int intI = 0; intI < lInput.Count; intI++) {
object objI = lInput[intI];
string strType = objI.GetType().ToString().Replace("LSLEditor.SecondLife+", "");
switch (strType) {
case "Float":
lResult.Add(Convert.ToDouble((Float)objI));
break;
case "System.Int32":
lResult.Add(Convert.ToDouble((int)objI));
break;
case "System.Double":
lResult.Add(Convert.ToDouble((double)objI));
break;
case "integer":
lResult.Add(Convert.ToDouble((integer)objI));
break;
default:
break;
}
}
return lResult;
}
/// <summary>
/// Calculates the mean of the supplied numbers.
/// </summary>
/// <param name="data">Array of numbers.</param>
/// <returns>The mean of the supplied numbers as a double type.</returns>
private double GetAverage(double[] data)
{
try {
double dblDataTotal = 0;
for (int i = 0; i < data.Length; i++) {
dblDataTotal += data[i];
}
return SafeDivide(dblDataTotal, data.Length);
} catch (Exception) {
throw;
}
}
/// <summary>
/// Calculates standard deviation.
/// </summary>
/// <param name="dblNumbers">Array of numbers to work with.</param>
/// <returns>The standard deviation of the supplied numbers as a double type.</returns>
public double GetStandardDeviation(double[] dblNumbers)
{
double dblSum = 0.0, dblSumOfSqrs = 0.0;
for (int i = 0; i < dblNumbers.Length; i++) {
dblSum += dblNumbers[i];
dblSumOfSqrs += Math.Pow(dblNumbers[i], 2);
}
double dblTopSum = (dblNumbers.Length * dblSumOfSqrs) - Math.Pow(dblSum, 2);
double dblN = (double)dblNumbers.Length;
return Math.Sqrt(dblTopSum / (dblN * (dblN - 1)));
}
/// <summary>
/// Performs division, only if both values are non-zero.
/// </summary>
/// <param name="dblValue1">First number.</param>
/// <param name="dblValue2">Second number.</param>
/// <returns>The result of the division, or zero if not performed.</returns>
private double SafeDivide(double dblValue1, double dblValue2)
{
double dblResult = 0.0F;
try {
if ((dblValue1 != 0) && (dblValue2 != 0)) {
dblResult = dblValue1 / dblValue2;
}
} catch {
}
return dblResult;
}
/// <summary>
/// Takes a hexadecimal representation of a number and coverts it to an integer.
/// </summary>
/// <param name="b">Byte to convert.</param>
/// <returns>Integer value of the supplied byte.</returns>
private byte HexToInt(byte b)
{
byte bResult;
if (b >= '0' && b <= '9') {
bResult = (byte)(b - '0');
} else if ((b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F')) {
bResult = (byte)((b & 0x5f) - 0x37);
} else {
bResult = 0; // error
}
return bResult;
}
#endregion
}
}