/*============================================================================
	Karma Script Interface
    
    - Karma UnrealScript native functions
============================================================================*/

#include "EnginePrivate.h"

IMPLEMENT_CLASS(UKMeshProps);
IMPLEMENT_CLASS(UKarmaParamsCollision);
IMPLEMENT_CLASS(UKarmaParams);
IMPLEMENT_CLASS(UKarmaParamsRBFull);
IMPLEMENT_CLASS(UKarmaParamsSkel);
IMPLEMENT_CLASS(AKActor);
IMPLEMENT_CLASS(AKTire);
IMPLEMENT_CLASS(AKVehicle);


// // //

void AActor::execKGetRBQuaternion( FFrame& Stack, RESULT_DECL )
{
	guardSlow(AActor::execKGetRBQuaternion);
	P_FINISH;

	FQuat Res(0, 0, 0, 1);

	McdModelID model = this->getKModel();
	if(!model)
	{
		*(FQuat*)Result = Res;
		return;
	}

	MdtBodyID body = McdModelGetBody(model);
	if(!body)
	{
		*(FQuat*)Result = Res;
		return;
	}

	MeVector4 q;
	MdtBodyGetQuaternion(body, q);
	Res.W = q[0];
	Res.X = q[1];
	Res.Y = q[2];
	Res.Z = q[3];

	*(FQuat*)Result = Res; 

	unguardexecSlow;
}

void AActor::execKGetRigidBodyState( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetRigidBodyState);

	P_GET_STRUCT_REF(FKRigidBodyState, RBstate);
	P_FINISH;

	McdModelID model = this->getKModel();
    if(!model)
        return;

	MdtBodyID body = McdModelGetBody(model);
	if(!body)
		return;

	MeVector3 tmp;
	MdtBodyGetPosition(body, tmp);
	RBstate->Position.X = tmp[0] * K_ME2UScale;
	RBstate->Position.Y = tmp[1] * K_ME2UScale;
	RBstate->Position.Z = tmp[2] * K_ME2UScale;

	MeVector4 q;
	MdtBodyGetQuaternion(body, q);
	RBstate->Quaternion.W = q[0];
	RBstate->Quaternion.X = q[1];
	RBstate->Quaternion.Y = q[2];
	RBstate->Quaternion.Z = q[3];

	MdtBodyGetLinearVelocity(body, tmp);
	RBstate->LinVel.X = tmp[0] * K_ME2UScale;
	RBstate->LinVel.Y = tmp[1] * K_ME2UScale;
	RBstate->LinVel.Z = tmp[2] * K_ME2UScale;

	MdtBodyGetAngularVelocity(body, tmp);
	RBstate->AngVel.X = tmp[0];
	RBstate->AngVel.Y = tmp[1];
	RBstate->AngVel.Z = tmp[2];

	unguard;
}

void AActor::execKDrawRigidBodyState( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKDrawRigidBodyState);

	P_GET_STRUCT(FKRigidBodyState, RBstate);
	P_GET_UBOOL(AltColour);
	P_FINISH;

	FVector pos(RBstate.Position.X, RBstate.Position.Y, RBstate.Position.Z);

	MeVector4 quat = {RBstate.Quaternion.W, RBstate.Quaternion.X, RBstate.Quaternion.Y, RBstate.Quaternion.Z};
	MeMatrix4 tm;
	MeQuaternionToTM(tm, quat);

	FVector x, y, z;
	KME2UPosition(&x, tm[0]);
	KME2UPosition(&y, tm[1]);
	KME2UPosition(&z, tm[2]);

	const float AxisLength = 1.8f;

	if(!AltColour)
	{
		GTempLineBatcher->AddLine(pos, pos + AxisLength * x, FColor(255, 0, 0));
		GTempLineBatcher->AddLine(pos, pos + AxisLength * y, FColor(0, 255, 0));
		GTempLineBatcher->AddLine(pos, pos + AxisLength * z, FColor(0, 0, 255));
	}
	else
	{
		GTempLineBatcher->AddLine(pos, pos + 0.8f * AxisLength * x, FColor(255, 128, 128));
		GTempLineBatcher->AddLine(pos, pos + 0.8f * AxisLength * y, FColor(128, 255, 128));
		GTempLineBatcher->AddLine(pos, pos + 0.8f * AxisLength * z, FColor(128, 128, 255));
	}

	unguard;
}

void AActor::execKRBVecToVector( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKRBVecToVector);

	P_GET_STRUCT(FKRBVec, RBvec);
	P_FINISH;

	FVector vec(RBvec.X, RBvec.Y, RBvec.Z);
	*(FVector*)Result = vec;

	unguard;
}

void AActor::execKRBVecFromVector( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKRBVecFromVector);

	P_GET_VECTOR(v);
	P_FINISH;

	FKRBVec RBvec;
	RBvec.X = v.X;
	RBvec.Y = v.Y;
	RBvec.Z = v.Z;
	*(FKRBVec*)Result = RBvec;

	unguard;
}

/*** MASS ***/

void AActor::execKSetMass( FFrame& Stack, RESULT_DECL )
{
    guard(AActor::execKSetMass);

    P_GET_FLOAT(mass);
    P_FINISH;
    
    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    kparams->KMass = mass;

#ifdef WITH_KARMA
    kparams->PostEditChange(); // sync with Karma
#endif

    unguard;
}

void AActor::execKGetMass( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetMass);

	P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    *(FLOAT*)Result = kparams->KMass;

    unguard;
}

/*** (Normalized) INERTIA TENSOR ***/

void AActor::execKGetInertiaTensor( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetInertiaTensor);

    P_GET_VECTOR_REF(it2);
    P_GET_VECTOR_REF(it1);
    P_FINISH;
    
    if(!this->KParams)
        return;


	// If this actor has the full (inertia/com-pos override) KParams, return that.
	UKarmaParamsRBFull* fullParams = Cast<UKarmaParamsRBFull>(this->KParams);
	if(fullParams)
	{
		it1->X = fullParams->KInertiaTensor[0];
		it1->Y = fullParams->KInertiaTensor[1];
		it1->Z = fullParams->KInertiaTensor[2];
		
		it2->X = fullParams->KInertiaTensor[3];
		it2->Y = fullParams->KInertiaTensor[4];
		it2->Z = fullParams->KInertiaTensor[5];
	}
	else // otherwise get inertia tensor params from static mesh
	{
		UStaticMesh* smesh = this->StaticMesh;
		if(smesh && smesh->KPhysicsProps)
		{
			it1->X = smesh->KPhysicsProps->InertiaTensor[0];
			it1->Y = smesh->KPhysicsProps->InertiaTensor[1];
			it1->Z = smesh->KPhysicsProps->InertiaTensor[2];
			
			it2->X = smesh->KPhysicsProps->InertiaTensor[3];
			it2->Y = smesh->KPhysicsProps->InertiaTensor[4];
			it2->Z = smesh->KPhysicsProps->InertiaTensor[5];
		}
	}

    unguard;
}

// Inertia tensor of object, if it had a mass of 1:
// (0 1 2)
// (1 3 4)
// (2 4 5)
void AActor::execKSetInertiaTensor( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetInertiaTensor);

	P_GET_VECTOR(it1);
    P_GET_VECTOR(it2);
	P_FINISH;

    if(!this->KParams)
        return;

	// we can only do this on the fly if this actor uses a UKarmaParamsRBFull for its KParams.
	// KTODO: We could just spawn one here if its not?
	UKarmaParamsRBFull* fullParams = Cast<UKarmaParamsRBFull>(this->KParams);
	if(fullParams)
	{
		fullParams->KInertiaTensor[0] = it1.X;
		fullParams->KInertiaTensor[1] = it1.Y;
		fullParams->KInertiaTensor[2] = it1.Z;
		
		fullParams->KInertiaTensor[3] = it2.X;
		fullParams->KInertiaTensor[4] = it2.Y;
		fullParams->KInertiaTensor[5] = it2.Z;
		
#ifdef WITH_KARMA
		this->KParams->PostEditChange(); // sync with Karma
#endif
	}

    unguard;
}

/*** CENTRE OF MASS OFFSET ***/

void AActor::execKSetCOMOffset( FFrame& Stack, RESULT_DECL )
{
    guard(AActor::execKSetCOMOffset);
    
    P_GET_VECTOR(offset);
    P_FINISH;

    if(!this->KParams)
        return;

	// we can only do this on the fly if this actor uses a UKarmaParamsRBFull for its KParams.
	// KTODO: We could just spawn one here if its not?
	UKarmaParamsRBFull* fullParams = Cast<UKarmaParamsRBFull>(this->KParams);
	if(fullParams)
	{
		fullParams->KCOMOffset = offset;

#ifdef WITH_KARMA
		this->KParams->PostEditChange(); // sync with Karma
#endif
	}

    unguard;
}

void AActor::execKGetCOMOffset( FFrame& Stack, RESULT_DECL )
{
    guard(AActor::execKGetCOMOffset);
    
    P_GET_VECTOR_REF(offset);
    P_FINISH;
    
    if(!this->KParams)
        return;

	// If this actor has the full (inertia/com-pos override) KParams, return that.
	UKarmaParamsRBFull* fullParams = Cast<UKarmaParamsRBFull>(this->KParams);
	if(fullParams)
	{
		*offset = fullParams->KCOMOffset;
	}
	else // otherwise get inertia tensor params from static mesh
	{
		UStaticMesh* smesh = this->StaticMesh;
		if(smesh && smesh->KPhysicsProps)
			*offset = smesh->KPhysicsProps->COMOffset;
	}
    
    unguard;
}

void AActor::execKGetCOMPosition( FFrame& Stack, RESULT_DECL )
{
    guard(AActor::execKGetCOMPosition);
    
    P_GET_VECTOR_REF(pos);
    P_FINISH;

#ifdef WITH_KARMA
	McdModelID model = this->getKModel();
    if(!model)
        return;

	MdtBodyID body = McdModelGetBody(model);
	if(!body)
		return;

	MeVector3 mePos;
	MdtBodyGetCenterOfMassPosition(body, mePos);
	KME2UPosition(pos, mePos);
#else
	pos->X = pos->Y = pos->Z = 0;
#endif

    unguard;
}


/*** DAMPING ***/

void AActor::execKSetDampingProps( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetDampingProps);

    P_GET_FLOAT(lindamp);
    P_GET_FLOAT(angdamp);
	P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    kparams->KLinearDamping = lindamp;
    kparams->KAngularDamping = angdamp;

#ifdef WITH_KARMA
    kparams->PostEditChange(); // sync with Karma
#endif

    unguard;
}

void AActor::execKGetDampingProps( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetDampingProps);

	P_GET_FLOAT_REF(lindamp);
    P_GET_FLOAT_REF(angdamp);
	P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    *lindamp = kparams->KLinearDamping;
    *angdamp = kparams->KAngularDamping;

    unguard;
}

/*** RESTITUTION ***/

void AActor::execKSetRestitution( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetRestitution);

	P_GET_FLOAT(rest);
    P_FINISH;

    if(!this->KParams)
        return;

    this->KParams->KRestitution = rest;

    unguard;
}

void AActor::execKGetRestitution( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetRestitution);

	P_FINISH;

    if(!this->KParams)
        return;

    *(FLOAT*)Result = this->KParams->KRestitution;

    unguard;
}

/*** FRICTION ***/

void AActor::execKSetFriction( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetFriction);

	P_GET_FLOAT(friction);
    P_FINISH;

    if(!this->KParams)
        return;

    this->KParams->KFriction = friction;

    unguard;
}

void AActor::execKGetFriction( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetFriction);

	P_FINISH;

    if(!this->KParams)
        return;

    *(FLOAT*)Result = this->KParams->KFriction;

    unguard;
}

/*** IMPACT THRESHOLD ***/

void AActor::execKSetImpactThreshold( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetImpactThreshold);

	P_GET_FLOAT(thresh);
    P_FINISH;

    if(!this->KParams)
        return;

    this->KParams->KImpactThreshold = thresh;

    unguard;
}

void AActor::execKGetImpactThreshold( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetImpactThreshold);

	P_FINISH;

    if(!this->KParams)
        return;

    *(FLOAT*)Result = this->KParams->KImpactThreshold;

    unguard;
}

/*** ACTOR GRAV SCALE ***/
void AActor::execKSetActorGravScale( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetActorGravScale);

	P_GET_FLOAT(ActorGravScale);
    P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    kparams->KActorGravScale = ActorGravScale;

    unguard;
}

void AActor::execKGetActorGravScale( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetActorGravScale);

	P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    *(FLOAT*)Result = kparams->KActorGravScale;

    unguard;
}


/*********************************************/

// Change the state of bBlockKarma
void AActor::execKSetBlockKarma( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetBlockKarma);

	P_GET_UBOOL(newBlock);
	P_FINISH;

#ifdef WITH_KARMA
	// Do nothing if state is already right,or is a constraint.
	if(bBlockKarma == newBlock || this->IsA(AKConstraint::StaticClass()))
		return;

	// This will do the dirty work of changing the model geometry, 
	// or creating a new one if necessary.
	KSetActorCollision(this, newBlock);

	bBlockKarma = newBlock;
#endif
	
	unguard;
}

// When Actors come to rest, they are automatically removed from simulation.
// This function can be called to 'force' things back into simulation.
void AActor::execKWake( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKWake);

	P_FINISH;

#ifdef WITH_KARMA
	// If this is an actor with Karma dynamics, make sure they are enabled.
    McdModelID m = this->getKModel();
    if(m && McdModelGetBody(m))
	{
		if(!MdtBodyIsEnabled(McdModelGetBody(m)))
			MdtBodyEnable(McdModelGetBody(m));
		return;
	}

	// If this is a set-up rag-doll, enable the first model.
	if(this->Physics == PHYS_KarmaRagDoll && this->MeshInstance)
	{
		USkeletalMeshInstance *skelInst = Cast<USkeletalMeshInstance>(this->MeshInstance);
		if(skelInst && skelInst->KSkelIsInitialised)
		{
			m = skelInst->KSkelModels(0);
			if(m && McdModelGetBody(m))
			{
				MdtBodyEnable(McdModelGetBody(m));
				return;
			}
		}
	}
#endif

    unguard;
}

// Return if this actor's body is currently being simualted
// JTODO: Fix for ragdolls.
void AActor::execKIsAwake( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKIsAwake);

	P_FINISH;

#ifdef WITH_KARMA
    McdModelID model = this->getKModel();
	if(!model)
	{
		*(UBOOL*)Result = 0;
		return;
	}

	MdtBodyID body = McdModelGetBody(model);
	if(!body)
	{
		*(UBOOL*)Result = 0;
		return;
	}

	if(MdtBodyIsEnabled(body))
		*(UBOOL*)Result = 1;
	else
		*(UBOOL*)Result = 0;
#endif

    unguard;
}

// Add an instantaneous impulse to the Karma physics off this Actor.
// This is useful for shooting stuff.
void AActor::execKAddImpulse( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKAddImpulse);

	P_GET_VECTOR(Impulse);
    P_GET_VECTOR(Position);
	P_GET_NAME_OPTX(BoneName, NAME_None);

	P_FINISH;

#ifdef WITH_KARMA

    MdtBodyID body = 0;
	UBOOL isSkel = 0;
	
    if(KGData && !KGData->bAutoEvolve)
        return;

	USkeletalMeshInstance* inst = 0;
	if(this->MeshInstance)
		inst = Cast<USkeletalMeshInstance>(this->MeshInstance);

    // If this is a basic, one-body Actor
    if(this->getKModel() && this->Physics == PHYS_Karma)
    {
        body = McdModelGetBody(this->getKModel());
    }
    // If this is a rag-doll, find the right bone.
    else if(this->Physics == PHYS_KarmaRagDoll && inst)
    {
		// If we have a specific name for the bone we want to set the velocity of,
		// find that bone.
		if(BoneName != NAME_None)
		{
			INT boneIx = inst->MatchRefBone(BoneName);
			
			McdModelID model = inst->KSkelModels(boneIx);
			if(model)
				body = McdModelGetBody(model);
		}
		else if (inst->KLastTraceHit != -1)// Otherwise, use bone from last ray check.
		{
			// If the hack isn't valid for some reason... bail out
			if(inst->KLastTraceHit < inst->KSkelModels.Num() && inst->KLastTraceHit >= 0 &&
				inst->KSkelModels(inst->KLastTraceHit) != 0)
			{
				body = McdModelGetBody(inst->KSkelModels(inst->KLastTraceHit));
				//debugf(TEXT("Hit Bone: %s Impulse: <%f %f %f>"), 
				//	*((USkeletalMesh*)inst->GetMesh())->RefSkeleton(inst->KLastTraceHit).Name,
				//	Impulse[0], Impulse[1], Impulse[2]);
			}
		}
#if 0
		else // If all else fails, kick the root (if present)
		{
			McdModelID model = inst->KSkelModels(0);
			if(model)
				body = McdModelGetBody(model);
		}
#endif

		inst->KLastTraceHit = -1;
		isSkel = 1;

		//debugf(TEXT("Impulse: %f %f %f"), Impulse.X, Impulse.Y, Impulse.Z);
    }
    
    // Scale both to ME sizes - for both mass AND velocity!
    MeVector3 kpos, kimpulse;
    KU2MEPosition(kpos, Position);
    KU2MEPosition(kimpulse, Impulse);
    MeVector3Scale(kimpulse, K_U2MEMassScale);
    

	// for skeleton shots - just apply impulse to centre-of-mass
	if(isSkel)
	{
		// If we hit an actual bone - apply full impulse to it.
		if(body)
		{
			MdtBodyAddImpulse(body, kimpulse[0], kimpulse[1], kimpulse[2]);
			MdtBodyEnable(body);
		}

#if 0
		// walk the heirarchy - applying impulses to other bones.
		MeVector3Scale(kimpulse, (MeReal)0.1);
		//kimpulse[2] += MeSqrt(kimpulse[0] * kimpulse[0] + kimpulse[1] * kimpulse[1]);
		
		for(INT i=0; i<inst->KSkelModels.Num(); i++)
		{
			McdModelID boneModel = inst->KSkelModels(i);
			if(!boneModel)
				continue;
			
			MdtBodyID boneBody = McdModelGetBody(boneModel);
			if(!boneBody || boneBody == body)
				continue;
			
			MdtBodyAddImpulse(boneBody, kimpulse[0], kimpulse[1], kimpulse[2]);
			
			MdtBodyEnable(boneBody);
		}
#endif
	}
	else
	{
		if(body)
		{
		MdtBodyAddImpulseAtPosition(body, 
			kimpulse[0], kimpulse[1], kimpulse[2],
			kpos[0], kpos[1], kpos[2]);

			MdtBodyEnable(body);
		}
	}

#endif
    unguard;
}

// Set the velocity (in world ref frame) of all bones in the ragdoll to Velocity.
// AddToCurrent indicates whether to add the linear part to the current linear velocity of ragdoll.
void AActor::execKSetSkelVel( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetSkelVel);

	P_GET_VECTOR(Velocity);
	P_GET_VECTOR_OPTX(AngVelocity, FVector(0, 0, 0));
	P_GET_UBOOL_OPTX(AddToCurrent, 0);
	P_FINISH;

#ifdef WITH_KARMA
	RTN_WITH_ERR_IF(!MeshInstance || !MeshInstance->IsA(USkeletalMeshInstance::StaticClass()), 
		"(Karma:) execKSetSkelVel: No skeletal mesh.");

	USkeletalMeshInstance* inst = Cast<USkeletalMeshInstance>(MeshInstance);

	KSetSkelVel(inst, Velocity, AngVelocity);
	
#endif

	unguard;
}

// Apply a constant force to a particular bone, until turned off.
// RampTime indicates the time taken for force to reach full strength.
void AActor::execKAddBoneLifter( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKAddBoneLifter);

	P_GET_NAME(BoneName);
	P_GET_STRUCT(FInterpCurve, LiftVel);
	P_GET_FLOAT(LateralFriction);
	P_GET_STRUCT(FInterpCurve, Softness);
	P_FINISH;

#ifdef WITH_KARMA
	RTN_WITH_ERR_IF(!MeshInstance || !MeshInstance->IsA(USkeletalMeshInstance::StaticClass()), 
		"(Karma:) execKAddBoneLifter: No skeletal mesh.");

	USkeletalMeshInstance* inst = Cast<USkeletalMeshInstance>(this->MeshInstance);
	RTN_WITH_ERR_IF(!inst->KSkelIsInitialised, "(Karma:) execKAddBoneLifter: Ragdoll not initialised.");

	INT boneIx = inst->MatchRefBone(BoneName);
	RTN_WITH_ERR_IF(boneIx == INDEX_NONE, "(Karma:) execKAddBoneLifter: Bone not found.");

	McdModelID model = inst->KSkelModels(boneIx);
	RTN_WITH_ERR_IF(!model, "(Karma:) execKAddBoneLifter: Bone has no Karma model.");

	MdtBodyID body = McdModelGetBody(model);
	RTN_WITH_ERR_IF(!body, "(Karma:) execKAddBoneLifter: Bone has no dynamics.");

	// Ok - so the bone is fine, add a new contact-powered bone lifter.
	FKBoneLifter* lifter = new(inst->KBoneLifters)FKBoneLifter(boneIx, &LiftVel, LateralFriction, &Softness);

	// Create contact group for lifting.
	ULevel* level = GetLevel();
	MdtContactGroupID cg = MdtContactGroupCreate(level->KWorld);
	MdtContactGroupSetBodies(cg, body, 0);
	MdtContactGroupEnable(cg);
	lifter->LiftContact = cg;
#endif

	unguard;
}

// Remove all constant forces from a particular bone.
void AActor::execKRemoveLifterFromBone( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKRemoveLifterFromBone);

	P_GET_NAME(BoneName);
	P_FINISH;

#ifdef WITH_KARMA
	RTN_WITH_ERR_IF(!MeshInstance || !MeshInstance->IsA(USkeletalMeshInstance::StaticClass()), 
		"(Karma:) execKRemoveLifterFromBone: No skeletal mesh.");

	USkeletalMeshInstance* inst = Cast<USkeletalMeshInstance>(this->MeshInstance);

	INT boneIx = inst->MatchRefBone(BoneName);
	RTN_WITH_ERR_IF(boneIx == INDEX_NONE, "(Karma:) execKRemoveLifterFromBone: Bone not found.");

	// Go through list, removing any forces that apply to this bone.
	INT i=0;
	while(i < inst->KBoneLifters.Num())
	{
		FKBoneLifter* f = &inst->KBoneLifters(i);
		if(f->BoneIndex == boneIx)
		{
			MdtContactGroupID cg = inst->KBoneLifters(i).LiftContact;
			check(cg);
			MdtContactGroupDisable(cg);
			MdtContactGroupDestroy(cg);

			inst->KBoneLifters.Remove(i);
		}
		else
			i++;
	}
#endif

	unguard;
}

// Remove all constant forces from all bones.
void AActor::execKRemoveAllBoneLifters( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKRemoveAllBoneLifters);

	P_FINISH;

#ifdef WITH_KARMA
	RTN_WITH_ERR_IF(!MeshInstance || !MeshInstance->IsA(USkeletalMeshInstance::StaticClass()), 
		"(Karma:) execKRemoveAllBoneLifters: No skeletal mesh.");

	USkeletalMeshInstance* inst = Cast<USkeletalMeshInstance>(this->MeshInstance);

	for(INT i=0; i<inst->KBoneLifters.Num(); i++)
	{
		MdtContactGroupID cg = inst->KBoneLifters(i).LiftContact;
		check(cg);
		MdtContactGroupDisable(cg);
		MdtContactGroupDestroy(cg);
	}
	inst->KBoneLifters.Empty(); // This calls MdtContactGroupDestroy from the destructor.
#endif

	unguard;
}


// Get entire mass of skeleton physics
void AActor::execKGetSkelMass( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKGetSkelMass);

	P_FINISH;

	*(FLOAT*)Result = 0.f;

#ifdef WITH_KARMA
	RTN_WITH_ERR_IF(!MeshInstance || !MeshInstance->IsA(USkeletalMeshInstance::StaticClass()), 
		"(Karma:) execKGetSkelMass: No skeletal mesh.");

	USkeletalMeshInstance* inst = Cast<USkeletalMeshInstance>(this->MeshInstance);
	RTN_WITH_ERR_IF(!inst->KSkelIsInitialised, 
		"(Karma:) execKGetSkelMass: Ragdoll not initialised.");

	FLOAT totalMass = 0.f;
	for(INT i=0; i<inst->KSkelModels.Num(); i++)
	{
		McdModelID model = inst->KSkelModels(i);
		if(model)
		{
			MdtBodyID body = McdModelGetBody(model);
			if(body)
			{
				totalMass += MdtBodyGetMass(body);
			}
		}
	}

	RTN_WITH_ERR_IF(totalMass == 0.f, 
		"(Karma:) execKGetSkelMass: Total mass of zero.");

	*(FLOAT*)Result = totalMass;
#endif

	unguard;
}

// Turn off physics for this ragdoll (doesn't change Physics from PHYS_KarmaRagdoll though), 
// but keep it in its current pose.
void AActor::execKFreezeRagdoll( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKFreezeRagdoll);

	P_FINISH;

#ifdef WITH_KARMA
	this->KFreezeRagdoll();
#endif

	unguard;
}

// Find if there is a free ragdoll
void AActor::execKIsRagdollAvailable( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKIsRagdollAvailable);

	P_FINISH;

#ifdef WITH_KARMA
	if(XLevel && Level && (XLevel->Ragdolls.Num() < Level->MaxRagdolls) )
		*(DWORD*)Result = 1;
	else
		*(DWORD*)Result = 0;
#else
	*(DWORD*)Result = 0;
#endif

	unguard;
}

// Try and free up a ragdoll.
// If there is not a ragdoll 'slot' available, it will take the oldest ragdoll and 
// freeze it, freeing up a slot. 
void AActor::execKMakeRagdollAvailable( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKMakeRagdollAvailable);

	P_FINISH;

#ifdef WITH_KARMA
	if(!Level || !XLevel)
		return;

	// We can never make a ragdoll available if the max is zero.
	if(Level->MaxRagdolls == 0)
		return;

	// If there is already one available, we don't need to do anything.
	if(XLevel->Ragdolls.Num() < Level->MaxRagdolls)
		return;

	check(XLevel->Ragdolls.Num() > 0);

	// Freeze the oldest, non-'important' ragdoll. Array is in age order (oldest first)
	UBOOL doneFrozen = 0;
	for(INT i=0; i<XLevel->Ragdolls.Num() && !doneFrozen; i++)
	{
		AActor* ragdoll = XLevel->Ragdolls(i);
		check(ragdoll->KParams);
		UKarmaParamsSkel* skelParams = Cast<UKarmaParamsSkel>(ragdoll->KParams);
		check(skelParams);
		if(!skelParams->bKImportantRagdoll)
		{
			ragdoll->KFreezeRagdoll();
			doneFrozen = 1;
		}
	}

	// If we still dont have a free ragdoll - warn
	if(XLevel->Ragdolls.Num() >= Level->MaxRagdolls)
		debugf(TEXT("execKMakeRagdollAvailable: No Ragdoll Available."));
#endif

	unguard;
}

// Disable Karma contact generation between this actor, and another actor.
void AActor::execKDisableCollision( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKDisableCollision);

	P_GET_ACTOR(Other);
	P_FINISH;

	if(!Other)
		return;

#ifdef WITH_KARMA
	ULevel* level = this->GetLevel();

	if(!level)
		return;

	McdModelID m1 = this->getKModel();
	McdModelID m2 = Other->getKModel();

	if(m1 && m2 && m1 != m2)
		KDisablePairCollision(m1, m2, level);
#endif

	unguard;
}

// Enable Karma contact generation between this actor, and another actor.
// By default, collision detection is on between all Karma actors.
void AActor::execKEnableCollision( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKEnableCollision);

	P_GET_ACTOR(Other);
	P_FINISH;

	if(!Other)
		return;

#ifdef WITH_KARMA
	ULevel* level = this->GetLevel();

	if(!level)
		return;

	McdModelID m1 = this->getKModel();
	McdModelID m2 = Other->getKModel();

	if(m1 && m2 && m1 != m2)
		KEnablePairCollision(m1, m2, level);
#endif

	unguard;
}

// 
void AActor::execKSetStayUpright( FFrame& Stack, RESULT_DECL )
{
	guard(AActor::execKSetStayUpright);

	P_GET_UBOOL(stayUpright);
	P_GET_UBOOL(allowRotate);
	P_FINISH;

    if(!this->KParams)
        return;

	UKarmaParams* kparams = Cast<UKarmaParams>(this->KParams);
	if(!kparams)
		return;

    kparams->bKStayUpright = stayUpright;
    kparams->bKAllowRotate = allowRotate;
	
#ifdef WITH_KARMA
    kparams->PostEditChange(); // sync with Karma
#endif

	unguard;
}