//////////////////////////////////////////////////////////
//normal game functions
//Used in all aspects of coding.
//By Mostlikely, sebastiaan_keek@hotmail.com
//If help, deduction or usefullness is required, contact me.
//////////////////////////////////////////////////////////

//Error value checking.
function ValidateVal(%val) {
	if (strStr(%val,"nan") != -1 || strStr(%val,"inf") != -1) {
		%val = "";
		error("Rot error no. " @ $RotErrors++);
	}
	return %val;
}

///////////////////////////////////////////////////////////
//////////////////////Basic Operators//////////////////////
///////////////////////////////////////////////////////////

//Returns the sign of [number] %num.
function Lev(%num) {
	if (%num < 0)
		return -1;
	else if (%num > 0)
		return 1;
	else
		return 0;
}

//Returns the highest value of [number] %num1 and [number] %num2.
function Min(%num1,%num2) {
	return (%num1 < %num2 ? %num1 : %num2);
}

//Returns the highest value of [number] %num1 and [number] %num2.
function Max(%num1,%num2) {
	return (%num1 > %num2 ? %num1 : %num2);
}

//Savely returns the division of [number] %num1 with [number] %num2.
function Div(%num1,%num2) {
	%error = (%num1 ==0 || %num2 ==0);
	return (%error ? 0 : %num1/%num2);
}

//Rounds down [number] %num to [number] %size.
function Round(%num,%size) {
	%size = %size ? %size : 1;
	return ((mFloor(mAbs(%num * %size))/%size)*Lev(%num));
}

//Returns the amount [number] %num is outside [border] %up and [border] %down.
function Breach(%num,%up,%down) {
	%up = %up ? %up : 0;
	%down = %down ? %down : 0;
	if (%num > %up)
		return (%num-%up); 
	else if (%num < %down)
                return (%num-%down); 		
        else
		return 0;
}

//Returns [number] %num limited between [border] %up and [border] %down.
function Limit(%num,%min,%max) {
	if (%min > %max)
		return (%min+%max)/2;
        return %num-Breach(%num,%max,%min);
}

//Returns randonly -1 or 1.

function getRandomLev() {
	if(getRandom(1))
		return 1;
	else
		return -1;
}


///////////////////////////////////////////////////////////
//////////////////////Vector Operators/////////////////////
///////////////////////////////////////////////////////////

//Returns the zero vector with [number] %num dimensions.
function ZeroV(%num) {
	for (%i=0;%i<%num;%i++) {
		%result = setWord(%result,%i,"0");
	}
	return trim(%result);
}

//Returns the unity vector with the same dimensions as [vector] %vec.
function vZeroV(%vec) {
	return ZeroV(getWordCount(%vec));
}

//Returns the unity vector with [number] %num dimensions.
function Unity(%num) {
	for (%i=0;%i<%num;%i++) {
		%result = setWord(%result,%i,"1");
	}
	return trim(%result);
}

//Returns the unity vector with the same dimensions as [vector] %vec.
function vUnity(%vec) {
	return Unity(getWordCount(%vec));
}

//Applies lev on [vector] %vec's content.
function vLev(%vec) {
	for (%i=0;%i<getWordCount(%vec);%i++) {
		%result = setWord(%result,%i,Lev(getWord(%vec,%i)));
	}
	return trim(%result);
}

//Applies abs on [vector] %vec's content.
function vAbs(%vec) {
	for (%i=0;%i<getWordCount(%vec);%i++) {
		%result = setWord(%result,%i,mAbs(getWord(%vec,%i)));
	}
	return trim(%result);
}

//Applies Round on [vector] %vec's content.
function vRound(%vec,%size) {
	for (%i=0;%i<getWordCount(%vec);%i++) {
		%result = setWord(%result,%i,Round(getWord(%vec,%i),%size));
	}
	return trim(%result);
}

//Returns the product of [vector] %vec1 with [vector] %vec2.
function vMul(%vec1,%vec2) {
	%count = Min(getWordCount(%vec1),getWordCount(%vec2));
	for (%i=0;%i<%count;%i++) {
		%result = setWord(%result,%i,getWord(%vec1,%i) * getWord(%vec2,%i));
	}
	return trim(%result);
}

//Retuns the division of [vector] %vec1 with [vector] %vec2.
function vDiv(%vec1,%vec2) {
	%count = Min(getWordCount(%vec1),getWordCount(%vec2));
	for (%i=0;%i<%count;%i++) {
		%result = setWord(%result,%i,Div(getWord(%vec1,%i),getWord(%vec2,%i)));
	}
	return trim(%result);
}

//Inversly scales [vector] %vec with [number] %num.
function VectorDeScale(%vec,%num) {
        return  vDiv(VectorScale(vUnity(%vec2),%num),%vec2);
}

//Returns which part is highest on the [vector] %vec.
function vMax(%vec) {        
        %ci = 0;
        for (%i=1;%i<getWordCount(%vec);%i++) {
		%ci = getWord(%vec,%i) > getWord(%vec,%ci) ? %i : %ci;
	}
}

//Returns which part is lowest on the [vector] %vec.
function vMin(%vec) {        
        %ci = 0;
        for (%i=1;%i<getWordCount(%vec);%i++) {
		%ci = getWord(%vec,%i) < getWord(%vec,%ci) ? %i : %ci;
	}
}

//Makes all but the highest part of the [vector] vec, zero.
function TopVec(%vec) {
        %id = vMax(vAbs(%vec));
	return setWord(vZeroV(%vec),%id,getWord(%vec,%id));
}	
	
//Makes all but the lowest part of the [vector] vec, zero.
function BottomVec(%vec) {
        %id = vMin(vAbs(%vec));
	return setWord(vZeroV(%vec),%id,getWord(%vec,%id));
}		

//Corrects [vector] %corv to straighten with [vector] %mainv.
function vRight(%mainv,%corv) {
	return VectorCross(VectorCross(%mainv,%corv),%mainv);
}

//Returns the length of [vector] %vec that's in [direction] %dir.
function vDir(%vec,%dir) {
	return VectorDot(%vec,VectorNormalize(%dir));
}

//Returns the projection of [vector] %vec on [direction] %dir.
function vProject(%vec,%dir) {
	return VectorScale(VectorNormalize(%dir),vDir(%vec,%dir));
}

//Returns the part in wich [vector] %vec is in [direction] %dir and the part in wich it is not.
function vCoPlane(%vec,%dir) {
	%dir2 = VectorNormalize(VRight(VectorNormalize(%dir),%vec));
	return vDir(%vec,%dir) SPC vDir(%vec,%dir2);
}

//Returns the [vector] %vec mirrored on an surface with [Normal] %nrm.
function vMirror(%vec,%nrm) {
	%plane = VectorNormalize(VRight(VectorNormalize(%nrm),%vec));
        return VectorAdd(VectorScale(VectorNormalize(%nrm),-1*VectorLen(%vec)),VectorScale(VectorNormalize(%plane),VectorLen(%vec)));
}

//Returns the angle between [vector] %vec1 and [vector] %vec2 in radians.
function VectorAngle(%vec1,%vec2) {
	return MACos(VectorDot(%vec1,%vec2)/(VectorLen(%vec1)*VectorLen(%vec2)));
}

//Returns the [face] that corresponds to the [vector] %vec.
function invFace(%vec) {
	return VectorSub("1 1 1",vAbs(vLev(%vec)));
}

//Returns the Vector Cross of 2-dimensional [vector] %vec in [direction] %dir.
function VectorCross2D(%vector,%dir) {
	%dir = %dir ? %dir : 1;
	return %dir*GetWord(%vector,1) SPC %dir*-1*GetWord(%vector,0);
}

//Scales the [vector] %vec to fit inside an unity cube.
function VectorCubeFit(%vec) {
        %scale = getWord(%vec,vMax(vAbs(%vec)));
        return %scale ? VectorScale(%vec,Div(1,%scale)) : vZeroV(%vec);
}

///////////////////////////////////////////////////////////
//////////////////////Rotationial math/////////////////////
///////////////////////////////////////////////////////////

//Returns the [vector] that corresponds with the [angle] %theta and [angle] %phi,
function radsphere(%theta,%phi) {
	return mSin(%theta) * mCos(%phi) SPC mSin(%theta) * mSin(%phi) SPC mCos(%theta);
}

//Same as radsphere only in degrees so t2 emitter dispersion system can be used.
function degsphere(%theta,%phi) {
	return radsphere(mDegtoRad(%theta),mDegtoRad(%phi));
}

//The inverse of radsphere, returns the [angle] theta and [angle] phi that corresponds with the [vector] %vec.
function vecsphere(%vec) {
	%vec = VectorNormalize(%vec);
	%xy = mPow(mPow(getWord(%vec,0),2)+mPow(getWord(%vec,1),2),0.5);
	%theta = mAtan(GetWord(%vec,2),%xy);
	%phi = mAtan(GetWord(%vec,0),GetWord(%vec,1));
	return %theta SPC %phi;
}

//Applies new [rotation] %rot2 on old [rotation] %rot1.
function RotAdd(%rot1,%rot2) {
	%rot = ValidateVal(MatrixMultiply("0 0 0" SPC %rot1,"0 0 0" SPC %rot2));
	%axis = vectorNormalize(getWords(%rot,3,5));
	return %axis SPC GetWord(%rot,6);
}

//Returns the inverse of [rotation] %rot.
function InvRot(%rot) {
	return VectorScale(%rot,-1) SPC getWord(%rot,3);
}

//Rotates [vector] %vec from [system] %trans to [base sytem].
function RealVec(%trans,%vec) {
	%rot = getWords(%trans,3,6);
	return validateVal(MatrixMulVector("0 0 0" SPC %rot ,%vec));
}

//Rotates [vector] %vec from [base sytem] to [system] %trans.
function VirVec(%trans,%vec) {
	%rot = InvRot(getWords(%trans,3,6));
	return validateVal(MatrixMulVector("0 0 0" SPC %rot ,%vec));
}

function GameBase::RealVec(%obj,%vec) {
	return RealVec(%obj.getTransform(),%vec);
}
function GameBase::VirVec(%obj,%vec) {
	return VirVec(%obj.getTransform(),%vec);
}

//Rotates [rotation] %rot from [system] %trans to [base sytem].
function RealRot(%trans,%rot) {
	%axis = RealVec(%trans,getWords(%rot,0,2));
	return %axis SPC getWord(%rot,3);
}

//Rotates [rotation] %rot from [base sytem] to [system] %trans.
function virRot(%trans,%rot) {
	%axis = virvec(%trans,getWords(%rot,0,2));
	return %axis SPC getWord(%rot,3);
}

function GameBase::RealRot(%trans,%rot) {
	return RealRot(%obj.getTransform(),%rot);
}
function GameBase::VirRot(%trans,%rot) {
	return VirRot(%obj.getTransform(),%rot);
}

//!OLD FUNCTION!
function remoteRotate(%mobj,%rot,%cobj,%mcenter)
{
%oldrot = rot(%mObj);
%oldpos = vectorAdd(pos(%mObj),%center);
%center = vectorAdd(pos(%cObj),Realvec(%cobj,VMul(%mcenter,vectorScale(realSize(%cobj),-1))));
%rrot = realrot(%cobj,%rot);
%newpos = vectorAdd(validateVal(MatrixMulPoint("0 0 0" SPC %rrot,vectorSub(%oldpos,%center))),%center);
return %newpos SPC rotAdd(%rrot,%oldrot);
}

///////////////////////////////////////////////////////////
//////////////////Basic Object Functions///////////////////
///////////////////////////////////////////////////////////

function GameBase::getPosition(%obj) {
	return getWords(%obj.getTransform(),0,2);
}

function GameBase::getRotation(%obj) {
	return getWords(%obj.getTransform(),3,6);
}

function GameBase::setPosition(%obj,%pos) {
	%obj.setTransform(%pos SPC %obj.getRotation());
}

function GameBase::setRotation(%obj,%rot) {
	%obj.setTransform(%obj.getPosition() SPC %rot);
}

//These are now handled inside the engine. ;)

//Returns the size of the unscaled [object] %obj.
function GameBase::GetShapeSize(%obj) {
	return VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2));
}

//Returns the actual size of [object] %obj in game-space.
function GameBase::GetRealSize(%obj) {
	return vMul(%obj.getScale(),%obj.getShapeSize());
}

//Sets the actual size of [object] %obj in game-space.
function GameBase::SetRealSize(%obj,%size) {
	%obj.setScale(vDiv(%size,%obj.getShapeSize()));
}

//Gets the position of the internal [offset] %offset from [object] %obj.
function GameBase::GetEdge(%obj,%offset) {
	%VirCenter = vectorScale(vectorAdd(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)),0.5);
	%VirOffset = VMul(vectorScale(%offset,1),%obj.getShapeSize());
	%realoffset = %obj.RealVec(VMul(vectorAdd(%VirCenter,%VirOffset),%obj.getScale()));
	return vectorAdd(GetWords(%obj.getTransform(),0,2),%realoffset);
}

//Sets the position of the internal [offset] %offset from [object] %obj.
function GameBase::SetEdge(%obj,%location,%offset) {
	%VirCenter = vectorScale(vectorAdd(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)),0.5);
	%VirOffset = vMul(vectorScale(%offset,1),%obj.getShapeSize());
	%realoffset = %obj.RealVec(vMul(vectorAdd(%VirCenter,%VirOffset),%obj.getScale()));
	%pos = vectorAdd(%location,vectorScale(%realoffset,-1));
	%obj.setTransform(%pos SPC %obj.getRotation());
}

//!OLD FUNCTION!
function GetFaceSize(%obj,%nrm) {
	return VectorMultiply(vAbs(invface(%nrm)),%obj.getrealSize());
}

///////////////////////////////////////////////////////////
//////////////////////Advanced functions///////////////////
///////////////////////////////////////////////////////////

//Need comments.

function GameBase::BestDir(%obj,%nrm) {
%face = invFace(%nrm);
return vAbs(VLev(topVec(vMul(%face,%obj.getRealSize()))));
}

function GameBase::BestSide(%obj,%point,%normal) {
%selection = %obj.findEdge(%point);
%selection = VMul(%selection,invFace(%normal));
%selection = TopVec(%selection);
return VectorNormalize(%selection);
}

function GameBase::SideLink(%obj,%point,%normal,%width) {
%selection = %obj.findEdge(%point);
%selection = VectorNormalize(BottomVec(%selection));
%line = vMul(vAbs(%selection),%obj.getRealSize());
%line = VectorLen(%line);
%pos = VectorDot(%obj.findEdge(%point),vMul(%selection,%obj.getRealSize()));
%pos = Limit(%pos,(%line-%width)*-0.5,(%line-%width)*0.5);
%result = VectorScale(vDiv(%selection,%obj.getRealSize()),%pos);
%result = VectorAdd(%result,VectorScale(%obj.BestSide(%point,%normal),0.5));
return %result;
}


function GameBase::SetDynamicRealSize(%obj,%size,%time,%start)
{
%scale = vectordivide(%size,%obj.getShapeSize());
%obj.setDynamicScale(%scale,%time,%start);
}

function GameBase::RelMount(%this,%that,%slot) {
%that.setDynamicTransform(VectorScale(%this.findEdge(%that.getTransform()),1),0);%this.MountObject(%that,%slot);
}

datablock ParticleEmitterNodeData(EmitterNode)
{
   timeMultiple = 1;
};

function createEmitter(%pos, %emitter, %rot,%scale)
{
    %dummy = new ParticleEmitterNode()
    {
          position = %pos;
          rotation = %rot;
          scale = %scale;
          dataBlock = EmitterNode;
          emitter = %emitter;
          velocity = "1";
    };
    MissionCleanup.add(%dummy);
    %dummy.setRotation(%rot);

    if(isObject(%dummy))
       return %dummy;
}





