//===========================================================================//
// File:	AnimationState.cpp											
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 02/23/999  JSE  Inital coding
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Microsoft Corp.										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"


#include "Vehicle.hpp"
#include "AnimationState.hpp"

//#############################################################################
//##########################    AnimationStateEngine    ########################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	

int AnimationStateEngine::GetAnimHolderType(const char *type_name)
{
	Check_Pointer(type_name);

	if (!_stricmp(type_name, "PoseType"))
	{
		return PoseType;
	}
	else if (!_stricmp(type_name, "CycleType"))
	{
		return CycleType;
	}
	else if (!_stricmp(type_name, "SpeedCycleType"))
	{
		return SpeedCycleType;
	}
	else if (!_stricmp(type_name, "LerpCycleType"))
	{
		return LerpCycleType;
	}
	else if (!_stricmp(type_name, "FullHeightSpeedBlenderCycleType"))
	{
		return FullHeightSpeedBlenderCycleType;
	}
	else if (!_stricmp(type_name, "FullHeightBlenderCycleType"))
	{
		return FullHeightBlenderCycleType;
	}
	else if (!_stricmp(type_name, "SpeedBlenderCycleType"))
	{
		return SpeedBlenderCycleType;
	}
	else if  (!_stricmp(type_name, "FullHeightPoseHolderType"))
	{
		return FullHeightPoseHolderType;
	}
	else
	{
		STOP(("AnimationStateEngine::GetAnimHolderType : %s Not a valid type", type_name));
	}
	
	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	

size_t AnimationStateEngine::GetAnimHolderSize(int type)
{
	Verify(type < AnimTypeCount);
	Verify(type > 0);

	switch (type)
	{
		case PoseType:
		return sizeof(PoseHolder);
	
		case CycleType:
			return sizeof(CycleHolder);
	
		case SpeedCycleType:
			return sizeof(SpeedCycleHolder);
	
		case LerpCycleType:
			return sizeof(LerpCycleHolder);
	
		case FullHeightSpeedBlenderCycleType:
			return sizeof(FullHeightSpeedBlenderCycleHolder);
	
		case FullHeightBlenderCycleType:
			return sizeof(FullHeightBlenderCycleHolder);

		case SpeedBlenderCycleType:
			return sizeof(SpeedBlenderCycleHolder);

		case FullHeightPoseHolderType:
			return sizeof(FullHeightPoseHolder);


		default:
			STOP(("AnimationStateEngine::GetAnimHolderSize - All types not defined!"));
	}

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

AnimHolder *AnimationStateEngine::MakeAnimHolder(int type)
{
	Verify(type < AnimTypeCount);
	Verify(type > 0);

	switch (type)
	{
		case PoseType:
			return new PoseHolder;
	
		case CycleType:
			return new CycleHolder;
	
		case SpeedCycleType:
			return new SpeedCycleHolder();
	
		case LerpCycleType:
			return new LerpCycleHolder();
	
		case FullHeightSpeedBlenderCycleType:
			return new FullHeightSpeedBlenderCycleHolder();
	
		case FullHeightBlenderCycleType:
			return new FullHeightBlenderCycleHolder();
	
		case SpeedBlenderCycleType:
			return new SpeedBlenderCycleHolder();

		case FullHeightPoseHolderType:
			return new FullHeightPoseHolder();

		default:
			STOP(("AnimationStateEngine::MakeAnimHolder - All types not defined!"));
	}

	return 0;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

MW4Animation::AnimInstance *AnimationStateEngine::LoadOrMakeAnimInstance(const char *animation_name)
{
	Stuff::ChainIteratorOf<MW4Animation::AnimInstance*> iterator(&animInstancesLoaded);
	MW4Animation::AnimInstance *anim_instance;

	while ((anim_instance = iterator.ReadAndNext()) != NULL)
	{
		if(!_stricmp(anim_instance->animData->animHeaderBlock->animationName, animation_name))
		{
			return anim_instance;
		}
	}

	// make anim_instance
	anim_instance = MW4Animation::AnimInstanceManager::Anim_Instance_Manager->MakeAnimInstance(
		animation_name,
		(MW4Animation::AnimInstanceManager::UserDefinedSearchFunction)Vehicle::FindChildMoverIndexUserData,
		(void *)vehicleParent
	);

	Check_Object(anim_instance);
	Check_Object(anim_instance);

	animInstancesLoaded.Add(anim_instance);

	return anim_instance;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimationStateEngine::LoadScript(const char *filename, bool run_minimal)
{

	//SPEW(("jerryeds", "LOAD SCRIPT : %s", filename));

	Verify(!alreadyLoaded);
	alreadyLoaded = true;

	// open the notation file
	Check_Pointer(filename);

	gos_PushCurrentHeap(g_AnimScript);

	Adept::Resource anim_res(filename);
	if(!anim_res.DoesResourceExist())
	{
		SPEW((0,"AnimationScript not in props.build : %s", filename));
		return;
	}
	anim_res.LoadData();
	
	NotationFile script_notefile(&anim_res);
	
	// spin thru and load appropriate type of anim holder
	NotationFile::PageIterator *pages = script_notefile.MakePageIterator();
	Check_Object(pages);


	int    anim_holder_count = 0;
	int	   total_curve_count = 0;

	Page *page;
	while ((page = pages->ReadAndNext()) != NULL)
	{
		Check_Object(page);
		const char* anim_state_name = page->GetName();
		Check_Pointer(anim_state_name);

		//SPEW(("jerryeds", "CREATE : %s", anim_state_name));

		// load the states
		if (strstr(anim_state_name, "::") == NULL)
		{
			// load the state based off of type (anim, transition or expression)			
			AnimationState *new_anim_state = new AnimationState(this);
			Check_Object(new_anim_state);

			new_anim_state->PreLoad(page);
			anim_holder_count += new_anim_state->GetAnimHolderCount(page);
			total_curve_count += new_anim_state->GetCurveCount(page);

			animStatesLoaded.Add(new_anim_state);

		}
		else if (strstr(anim_state_name, "::") != NULL) // load the transitions
		{
			// load the transition based off of type (anim, transition or expression)			
			TransitionState *new_anim_state = new TransitionState(this);
			Check_Object(new_anim_state);

			new_anim_state->PreLoad(page);
			anim_holder_count += new_anim_state->GetAnimHolderCount(page);
			total_curve_count += new_anim_state->GetCurveCount(page);

			transitionsLoaded.Add(new_anim_state);

		}
		else
		{
			STOP(("AnimationStateEngine::LoadScript : INVALID CODE - SHOULD NEVER REACH HERE"));
		}
	}

	//allocate curves and animholders

	animHoldersCount = anim_holder_count;
	Verify(animHoldersCount > 0);

	animHolderArray = new AnimHolder*[animHoldersCount];
	Register_Pointer(animHolderArray);


	animCurvesCount = total_curve_count;
	if (animCurvesCount != 0)
	{
		animCurves = new AnimCurve[animCurvesCount];
		Register_Pointer(animCurves);
	}

	// Now load each animHolder which will:
	//		load each animinstances
	//		load the curves



	int current_curve = 0;
	int current_holder = 0;

	Stuff::ChainIteratorOf<AnimationState*> s_iterator(&animStatesLoaded);
	Stuff::ChainIteratorOf<TransitionState*> t_iterator(&transitionsLoaded);


	AnimationState *current_state;
	AnimationState *current_transition;

	pages->First();
	while ((page = pages->ReadAndNext()) != NULL)
	{
		const char* anim_state_name = page->GetName();
		Check_Pointer(anim_state_name);

		//SPEW(("jerryeds", "LOAD : %s", anim_state_name));

		if (strstr(anim_state_name, "::") == NULL)
		{
			current_state = s_iterator.ReadAndNext();
			Check_Object(current_state);

			current_state->LoadScriptEntry(page, current_holder, current_curve, run_minimal);
			current_state->CalculateChainTimes();
			current_state->LoadIterators(run_minimal);
		}
		else if (strstr(anim_state_name, "::") != NULL)
		{
			current_transition = t_iterator.ReadAndNext();
			Check_Object(current_transition);

			current_transition->LoadScriptEntry(page, current_holder, current_curve, run_minimal);
			current_transition->CalculateChainTimes();
			current_transition->LoadIterators(run_minimal);
		}
		else
		{
			STOP(("AnimationStateEngine::LoadScript : INVALID CODE - SHOULD NEVER REACH HERE"));
		}
	}

	Verify(s_iterator.GetCurrent() == NULL);
	Verify(t_iterator.GetCurrent() == NULL);


	Check_Object(pages);
	delete pages;

	anim_res.UnloadData();

	gos_PopCurrentHeap();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimationState::PreLoad(Page *page)
{
	Check_Object(page);
	Check_Object(animStateEngine);

	// look up state
	StateEngine::ClassData *state_class_data;
	state_class_data = animStateEngine->GetClassData();
	Check_Object(state_class_data);
	
	const StateEngine::StateEntry *state_entry = state_class_data->FindStateEntry(page->GetName());

	if(state_entry != NULL)
	{
		Check_Object(state_entry);
		myState = state_entry->stateNumber;
	}
	else
	{
		myState = -1;
	}

}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void TransitionState::PreLoad(Page *page)
{
	
	Check_Object(page);

	char *copy_name;
	char *first_name;
	char *second_name;

	const char* page_name = page->GetName();
	Check_Pointer(page_name);
	copy_name = new char [strlen(page_name)+1];
	Register_Pointer(copy_name);

	Str_Copy(copy_name, page_name, strlen(page_name)+1);

	first_name = copy_name;
	second_name = strstr(copy_name, "::");
	*second_name = '\0';
	second_name++;
	*second_name = '\0';
	second_name++;

	
	// look up state
	StateEngine::ClassData *state_class_data;
	state_class_data = animStateEngine->GetClassData();
	Check_Object(state_class_data);
	
	const StateEngine::StateEntry *state_entry = 
			state_class_data->FindStateEntry(first_name);

	if(state_entry != NULL)
	{
		Check_Object(state_entry);
		myState = state_entry->stateNumber;
	}
	else
	{
		myState = -1;
	}

	state_entry = 
			state_class_data->FindStateEntry(second_name);

	if(state_entry != NULL)
	{
		Check_Object(state_entry);
		transitionToState = state_entry->stateNumber;
	}
	else
	{
		transitionToState = -1;
	}


	const char *entry_data;

	page->GetEntry("TransitionStart", &entry_data, true);
	if(!_strnicmp(entry_data, "any", 3))
	{
		transitionStart = StartAnyWhereToken;
	}
	else
	{
		transitionStart = AtoF(entry_data);
		Verify(transitionStart >= 0.0f);
		Verify(transitionStart <= 1.0f);
	}

	
	page->GetEntry("PlayOldStateTill", &playOldStateTill, true);
	page->GetEntry("StartNewState", &startNewState, true);

	const char *override_text;
	page->GetEntry("OverrideNewStatePosition", &override_text, true);
	if (!_stricmp(override_text, "default"))
	{
		overrideNewStatePosition = -1;
	}
	else
	{
		overrideNewStatePosition = AtoF(override_text);
		Verify(overrideNewStatePosition >= 0.0f);
		Verify(overrideNewStatePosition <= 1.0f);
	}





	Verify(playOldStateTill >= 0.0f);
	Verify(playOldStateTill <= 1.0f);
	Verify(startNewState >= 0.0f);
	Verify(startNewState <= 1.0f);
	Verify(startNewState >= playOldStateTill);  // these are only allowed to *touch* not overlap

	Unregister_Pointer(copy_name);
	delete[] copy_name;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimationState::LoadScriptEntry(Page *page, int &current_holder, int &current_curve, bool run_minimal)
{
	Check_Object(page);
	Check_Object(animStateEngine);

	
	
	// load all my children
	Page::NoteIterator *notes = page->MakeNoteIterator();
	Check_Object(notes);


	firstAnim = current_holder;
	animCount = 0;

	NameList *anim_param_list = NULL;

	if (!page->GetEntry("CarryOver", &carryOver))
	{
		carryOver = true;
	}

	Note *note;
	while ((note = notes->ReadAndNext()) != NULL)
	{
		Check_Object(note);
		const char* anim_type_name = note->GetName();
		Check_Pointer(anim_type_name);
		const char* note_data;
		note->GetEntry(&note_data);

		if (!_stricmp(anim_type_name, "AnimType"))
		{

			// load based off of list
			if (anim_param_list != NULL)
			{
				
				LoadAnimHolder(anim_param_list, firstAnim + animCount, current_curve, run_minimal);
				Check_Object(anim_param_list);
				delete anim_param_list;
				anim_param_list = NULL;
				++animCount;
			}

			
			Verify(anim_param_list == NULL);
			anim_param_list = new NameList();
			Check_Object(anim_param_list);
			
			anim_param_list->AddEntry(note->GetName(), (void*)note_data);

		}
		else
		{
			// in effect this spins till it gets the first animtype
			if (anim_param_list != NULL)
			{
				anim_param_list->AddEntry(note->GetName(), (void*)note_data);
			}
		}
	}

	if (anim_param_list != NULL)
	{
		LoadAnimHolder(anim_param_list, firstAnim + animCount, current_curve, run_minimal);
		Check_Object(anim_param_list);
		delete anim_param_list;
		anim_param_list = NULL;
		++animCount;
	}

	Verify(animCount > 0);

	//SPEW(("jerryeds", "LOAD %s : %d", page->GetName(), animCount));


	Check_Object(notes);
	delete notes;

	current_holder += animCount;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	

void TransitionState::LoadScriptEntry(Page *page, int &current_holder, int &current_curve, bool run_minimal)
{
	Check_Object(page);
	Check_Object(animStateEngine);

	AnimationState::LoadScriptEntry(page, current_holder, current_curve, run_minimal);

	//now load my curves...
	const char *curve_param;

	page->GetEntry("StartOverlapCurve", &curve_param, true);
	startOverlapCurve = current_curve;
	animStateEngine->animCurves[current_curve].LoadCurve(curve_param);
	current_curve++;
	Verify(current_curve <= animStateEngine->animCurvesCount);

	page->GetEntry("EndOverlapCurve", &curve_param, true);
	endOverlapCurve = current_curve;
	animStateEngine->animCurves[current_curve].LoadCurve(curve_param);
	current_curve++;
	Verify(current_curve <= animStateEngine->animCurvesCount);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	

void AnimationState::LoadAnimHolder(NameList *param_list, int anim_holder_count, int &current_curve, bool run_minimal)
{
	// This name list has only the animation parameters and nothing else.
	// Now we can use the search interface to make it work pretty
	

	// figure out the type

	NameList::Entry *page = param_list->FindEntry("AnimType");
	Check_Object(page);

	int type = animStateEngine->GetAnimHolderType(page->GetChar());
	
	// load generic data
	Verify(anim_holder_count < animStateEngine->animHoldersCount);
	
	AnimHolder *anim_holder = animStateEngine->MakeAnimHolder(type);
	animStateEngine->animHolderArray[anim_holder_count] = anim_holder;
	Check_Pointer(animStateEngine->animHolderArray[anim_holder_count]);
	Register_Pointer(animStateEngine->animHolderArray[anim_holder_count]);

	
	anim_holder->animType = type;
	
	page = param_list->FindEntry("Start");
	Check_Object(page);
	const char *contents = page->GetChar();

	if (!_strnicmp(contents, "after", 5))
	{
		anim_holder->startDelay = 0.0f;
		sscanf(contents, "after %d", &anim_holder->waitForAnim);
	}
	else
	{
		anim_holder->waitForAnim = AnimHolder::NoStartAfterToken;
		anim_holder->startDelay = page->GetAtof();
	}
	
	
	animStateEngine->animHolderArray[anim_holder_count]->Load(animStateEngine, param_list, run_minimal);



	anim_holder->firstCurve = current_curve;
	anim_holder->curveCount = 0;

	//load the curves
	page = param_list->GetFirstEntry();

	while(page != NULL)
	{
		if (!_stricmp(page->GetName(), "Curve"))
		{
			//load er up
			animStateEngine->animCurves[anim_holder->firstCurve+anim_holder->curveCount].LoadCurve(page->GetChar());

			anim_holder->curveCount++;
		}
		else if(!_stricmp(page->GetName(), "CurveMask"))
		{
			Verify(anim_holder->curveCount != 0);
			// verify we already have a curve...

			animStateEngine->animCurves[anim_holder->firstCurve+anim_holder->curveCount].LoadMasks(page->GetChar());

		}
		page = page->GetNextEntry();
	}
	

	current_curve += anim_holder->curveCount;
	Verify(current_curve <= animStateEngine->animCurvesCount);

	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	

size_t AnimationState::GetAnimHolderCount(Page *page)
{
	Check_Object(page);

		// spin thru and load appropriate type of anim holder
	ChainOf<Note*> *type_chain = page->MakeNoteChain("AnimType");
	Check_Object(type_chain);
	Page::NoteIterator pages(type_chain);

	int anim_holder_count = pages.GetSize();
	
	Check_Object(type_chain);
	delete type_chain;

	return anim_holder_count;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

int AnimationState::GetCurveCount(Page *page)
{
	Check_Object(page);

			// spin thru and load appropriate type of anim holder
	ChainOf<Note*> *curve_chain = page->MakeNoteChain("Curve");
	Check_Object(curve_chain);
	Page::NoteIterator pages(curve_chain);

	int curve_count = pages.GetSize();
	
	Check_Object(curve_chain);
	delete curve_chain;


	return curve_count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

int TransitionState::GetCurveCount(Page *page)
{
	return AnimationState::GetCurveCount(page) + 2;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//


//#################################### BIG NOTE ######################################
//  This is kind of tricky.  
// The problem lies in the fact that these guys can dynamicly scale 
// themselevs.  Which makes calculating this part a wee bit hard.

// It seems as if the solution is to treat each
// animation or chain of animations independantly
// when calculating the state position we figure
// out how much time has gone by since the state
// started.  Based off of that we dead reckon each
// chain of animations from current position to finish.
// based off of that we will have the "longest" animation
// chain.  Then we simply see where it is percentage wise.

// in order to do this we must have each animation know if it in 
// a chain and if so how much percentage of it is used for the anim

// This is will be a slow function if there are a lot of 
// chained animations

void AnimationState::CalculateChainTimes()
{

	// we are measuring total time of chained animations
	Verify(animCount > 0);


	for (int i = 0; i < animCount; ++i)
	{
		AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim];
		Check_Pointer(anim_holder);

		if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken)
		{
			// we have a chain here...
			// recurse the chain and adjust...

			// this algorithim depends on order so...
			Verify(anim_holder->waitForAnim < i);
			Verify(anim_holder->waitForAnim >= 0);
			Verify(anim_holder->waitForAnim < animCount);
			
			Stuff::Scalar total_time = 0.0f;
			RecurseAndCalculateChainTime(total_time, i);

		}
		else
		{
			anim_holder->startPercentage = 0.0f;
			anim_holder->percentageOfChain = 1.0f;
		}
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimationState::RecurseAndCalculateChainTime(Stuff::Scalar &total_time, int current_anim)
{
	AnimHolder *anim_holder = animStateEngine->animHolderArray[current_anim+firstAnim];
	Check_Pointer(anim_holder);

	total_time += anim_holder->GetTimeTotal();


	if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken)
	{
		Verify(anim_holder->waitForAnim < current_anim);
		Verify(anim_holder->waitForAnim >= 0);
		Verify(anim_holder->waitForAnim < animCount);
		RecurseAndCalculateChainTime(total_time, anim_holder->waitForAnim);
	}

	// depth first iteratation 
	// so we start at the bottom of the list


	// calculate our percentage of the total time..
	if (!Small_Enough(total_time))
	{
		if (!Small_Enough(anim_holder->GetTimeTotal()))
		{
			anim_holder->percentageOfChain = anim_holder->GetTimeTotal() / total_time;
		}
		else
		{
			anim_holder->percentageOfChain = 0;
		}
	}
	else
	{
		anim_holder->percentageOfChain = 0;
	}


	// calculate our start position
	if (anim_holder->waitForAnim == AnimHolder::NoStartAfterToken)
	{
		// first one starts at zero (internal delay is handled by the holder)
		anim_holder->startPercentage = 0.0f;
	}
	else
	{
		// our start is the waiting states start plus his percentage
		AnimHolder *wait_anim_holder = animStateEngine->animHolderArray[anim_holder->waitForAnim+firstAnim];
		Check_Pointer(wait_anim_holder);
		anim_holder->startPercentage = wait_anim_holder->startPercentage + wait_anim_holder->percentageOfChain;
	}

}




//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

AnimCurve::AnimCurve()
{
	curveType = NULLAnimType;
	
	curveMin = -1.0f; 
	curveMax = -1.0f;
	timeStart = -1.0f;
	timeEnd = -1.0f;

	invertTime = false;
	invertScale = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimCurve::LoadCurve(const char *curve_text)
{
	int invert_time_test = -1;
	int invert_scale_test = -1;

	sscanf(curve_text, "%d %f %f %f %f %d %d", &curveType, &curveMin, &curveMax, &timeStart, &timeEnd, &invert_time_test, &invert_scale_test);
//	sscanf(curve_text, "%f", &curveMin);
//	sscanf(curve_text, "%f", &curveMax);
//	sscanf(curve_text, "%f", &timeStart);
//	sscanf(curve_text, "%f", &timeEnd);
//	sscanf(curve_text, "%d", &invert_time_test);
//	sscanf(curve_text, "%d", &invert_scale_test);

	Verify(curveType >= 0 && curveType < CurveTypeCount);
	Verify(curveMin >= 0.0f && curveMin <= 1.0f);
	Verify(curveMax >= 0.0f && curveMax <= 1.0f);
	Verify(timeStart >= 0.0f && timeStart <= 1.0f);
	Verify(timeEnd >= 0.0f && timeEnd <= 1.0f);
	Verify(invert_time_test == 0 || invert_time_test == 1);
	Verify(invert_scale_test == 0 || invert_scale_test == 1);

	invertTime = (invert_time_test)?true:false;
	invertScale = (invert_scale_test)?true:false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void AnimCurve::LoadMasks(const char *mask_text)
{

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

