//::///////////////////////////////////////////////
//:: Analyze Dweomer : On Enter
//:: jx_sp_anadweomer.nss
//:://////////////////////////////////////////////
/*
	The caster analyzes 1 item or creature per
	round. If the object analyzed is a magical
	item, it is identified. If it's a creature,
	all active spells are revealed except if the
	target creature succeeds a Will check.
*/
//:://////////////////////////////////////////////
//:: Created By: jallaix
//:: Created On: Aug 17, 2007
//:://////////////////////////////////////////////

#include "nw_i0_spells"
#include "ginc_companion"



void AnalyzeDweomerIdentify(object oCaster);
void AnalyzeDweomerByRound(int iRoundCount, object oCaster, location lCaster);


const string JX_ANALYZEDWEOMER_RESISTED = "JX_ANALYZEDWEOMER_RESISTED";

string MSG_ITEM_NOT_MAGICAL				= GetStringByStrRef(17079431);
string MSG_ITEM_ALREADY_IDENTIFIED		= GetStringByStrRef(17079432);
string MSG_ITEM_IDENTIFIED				= GetStringByStrRef(17079433);
string MSG_CREATURE_NOSPELL				= GetStringByStrRef(17079434);
string MSG_CREATURE_SPELL				= GetStringByStrRef(17079435);
string MSG_ITEM_RESISTED				= GetStringByStrRef(17079436);
string MSG_ITEM_ALREADY_RESISTED		= GetStringByStrRef(17079437);
string MSG_ITEM_NOT_MAGICAL_DEFAULT				= GetStringByStrRef(302215);
string MSG_ITEM_ALREADY_IDENTIFIED_DEFAULT		= GetStringByStrRef(302216);
string MSG_ITEM_IDENTIFIED_DEFAULT				= GetStringByStrRef(302217);
string MSG_CREATURE_NOSPELL_DEFAULT				= GetStringByStrRef(302218);
string MSG_CREATURE_SPELL_DEFAULT				= GetStringByStrRef(302219);
string MSG_ITEM_RESISTED_DEFAULT				= GetStringByStrRef(302220);
string MSG_ITEM_ALREADY_RESISTED_DEFAULT		= GetStringByStrRef(302221);

string sObjectsDetected;		// Detected creatures and items
string sItemsAnalyzed;			// Analyzed items
int iPosition;					// Current object to analyze
int iObjectsNumber;				// Number of objects to analyze



void MarkCurrentObjectAnalyzed()
{
	// Get the current objet identifier
	string sObject = JXStringSplit(sObjectsDetected, ";", iPosition);
	if (JXStringSplitCount(sObject, ",") == 1)
	{
		// Creatures are not marked as new spells could be found since the last analyze
		object oObject = IntToObject(StringToInt(JXStringSplit(sObject, ",", 0)));
		if (GetObjectType(oObject) == OBJECT_TYPE_CREATURE)
			return;
	}

	// Add the item to the list of analyzed objects
	if (sItemsAnalyzed != "") sItemsAnalyzed += ";";
	sItemsAnalyzed += sObject;
}

int GetIsCurrentObjectAnalyzed(object oCaster)
{
	// Get the current object to analyze
	string sObjectDetected = JXStringSplit(sObjectsDetected, ";", iPosition);

	// Loop all objects already analyzed
	int iItemsCount = JXStringSplitCount(sItemsAnalyzed, ";");
	int iLoop; string sItemAnalyzed;
	for (iLoop = 0; iLoop < iItemsCount; iLoop++)
	{
		// Get the analyzed item
		sItemAnalyzed = JXStringSplit(sItemsAnalyzed, ";", iLoop);
		// Return when the analyzed object is the same as the detected object
		if (sObjectDetected == sItemAnalyzed)
			return TRUE;
	}

	return FALSE;
}

//-----------------------------------------
// Analyze Dweomer : Perform identification
//-----------------------------------------
void AnalyzeDweomerIdentify(object oCaster)
{
	string sObject;
	object oDetected;
	string sMessage;

	// Get the object previously saved
	sObject = JXStringSplit(sObjectsDetected, ";", iPosition);
	oDetected = IntToObject(StringToInt(JXStringSplit(sObject, ",", 0)));

	if (GetObjectType(oDetected) == OBJECT_TYPE_ITEM)
	{
		// Identify the item
		if (JXGetIsItemMagical(oDetected))
		{
			if (GetIdentified(oDetected))
				sMessage = JXStringReplaceToken((MSG_ITEM_ALREADY_IDENTIFIED == "" ? MSG_ITEM_ALREADY_IDENTIFIED_DEFAULT : MSG_ITEM_ALREADY_IDENTIFIED), 850, GetName(oDetected));
			else
			{
				SetIdentified(oDetected, TRUE);
				sMessage = JXStringReplaceToken((MSG_ITEM_IDENTIFIED == "" ? MSG_ITEM_IDENTIFIED_DEFAULT : MSG_ITEM_IDENTIFIED), 850, GetName(oDetected));
			}
		}
		else
			sMessage = JXStringReplaceToken((MSG_ITEM_NOT_MAGICAL == "" ? MSG_ITEM_NOT_MAGICAL_DEFAULT : MSG_ITEM_NOT_MAGICAL), 850, GetName(oDetected));

		AssignCommand(oDetected, SpeakString(sMessage));
	}
	else if (GetObjectType(oDetected) == OBJECT_TYPE_CREATURE)
	{
		// Identify an item hold by a creature
		if (JXStringSplitCount(sObject, ",") == 2)
		{
			object oItemHeld = IntToObject(StringToInt(JXStringSplit(sObject, ",", 1)));

			// The item is not magical
			if (!JXGetIsItemMagical(oItemHeld))
			{
				sMessage = JXStringReplaceToken((MSG_ITEM_NOT_MAGICAL == "" ? MSG_ITEM_NOT_MAGICAL_DEFAULT : MSG_ITEM_NOT_MAGICAL), 850, GetName(oItemHeld));
				AssignCommand(oDetected, SpeakString(sMessage));
			}
			// The item already resisted the spell
			else if (GetLocalInt(oItemHeld, JX_ANALYZEDWEOMER_RESISTED) == 1)
			{
				sMessage = JXStringReplaceToken((MSG_ITEM_ALREADY_RESISTED == "" ? MSG_ITEM_ALREADY_RESISTED_DEFAULT : MSG_ITEM_ALREADY_RESISTED), 850, JXGetBaseItemName(oItemHeld));
				AssignCommand(oDetected, SpeakString(sMessage));
			}
			// The item is already identified
			else if (GetLocalInt(oItemHeld, JX_ITEM_IDENTIFIED))
			{
				sMessage = JXStringReplaceToken((MSG_ITEM_ALREADY_IDENTIFIED == "" ? MSG_ITEM_ALREADY_IDENTIFIED_DEFAULT : MSG_ITEM_ALREADY_IDENTIFIED), 850, GetName(oItemHeld));
				AssignCommand(oDetected, SpeakString(sMessage));
			}
			// Try to identify the item
			else
			{
				// The item makes a Will Save vs. Spells to avoid to be identified
				if (!JXMyItemSavingThrow(SAVING_THROW_WILL,
										 oItemHeld,
									     JXGetSpellSaveDC(),
									     SAVING_THROW_TYPE_SPELL,
									     oCaster))
				{
					// Mark the item as identified
					SetLocalInt(oItemHeld, JX_ITEM_IDENTIFIED, 1);
					sMessage = JXStringReplaceToken((MSG_ITEM_IDENTIFIED == "" ? MSG_ITEM_IDENTIFIED_DEFAULT : MSG_ITEM_IDENTIFIED), 850, GetName(oItemHeld));
					AssignCommand(oDetected, SpeakString(sMessage));
				}
				// The item resisted the identification attempt
				else
				{
					// Mark the item as resisted : no other attempt possible for 24 hours
					SetLocalInt(oItemHeld, JX_ANALYZEDWEOMER_RESISTED, 1);
					AssignCommand(oItemHeld, DelayCommand(HoursToSeconds(24), DeleteLocalInt(oItemHeld, JX_ANALYZEDWEOMER_RESISTED)));
					sMessage = JXStringReplaceToken((MSG_ITEM_RESISTED == "" ? MSG_ITEM_RESISTED_DEFAULT : MSG_ITEM_RESISTED), 850, JXGetBaseItemName(oItemHeld));
					AssignCommand(oDetected, SpeakString(sMessage));
				}
			}
		}
		// Identify active spells on the creature
		else
		{
			// Get the active spells
			string sActiveSpells = JXGetActiveSpells(oDetected);
			int bSpellDetected = FALSE;

			int iSpellId;
			int iSpellCasterLevel;

			// Loop all active spells
			int iCount = JXStringSplitCount(sActiveSpells, ";");
			int iLoop;
			for (iLoop = 0; iLoop < iCount; iLoop++)
			{
				// Fire cast spell at event for the target
				SignalEvent(oDetected, EventSpellCastAt(oCaster, JX_SPELL_ANALYZEDWEOMER, FALSE));

				// Find a spell informations
				iSpellId = StringToInt(JXStringSplit(sActiveSpells, ";", iLoop));
				iSpellCasterLevel = JXGetSpellCasterLevel(oDetected, iSpellId);
				if ((iSpellCasterLevel > 0) && (JXGetIsSpellMagical(iSpellId)))
				{
					// Print a message : Spell name and caster level
					sMessage = JXStringReplaceToken((MSG_CREATURE_SPELL == "" ? MSG_CREATURE_SPELL_DEFAULT : MSG_CREATURE_SPELL), 850, JXGetSpellName(iSpellId));
					sMessage = JXStringReplaceToken(sMessage, 851, IntToString(iSpellCasterLevel));
					AssignCommand(oDetected, SpeakString(sMessage));

					bSpellDetected = TRUE;
				}
			}

			// Print a message : No spell detected
			if (!bSpellDetected)
				AssignCommand(oDetected, SpeakString((MSG_CREATURE_NOSPELL == "" ? MSG_CREATURE_NOSPELL_DEFAULT : MSG_CREATURE_NOSPELL)));
		}
	}
}


//---------------------------------------------
// Analyze Dweomer : Search items and creatures
//---------------------------------------------
void AnalyzeDweomerByRound(int iRoundCount, object oCaster, location lCaster)
{
	// If the caster is not concentrating or if his position has changed, wait for the next round
	if ((GetCurrentAction(oCaster) != ACTION_INVALID) || (GetLocation(oCaster) != lCaster))
	{
		DelayCommand(6.0f, AnalyzeDweomerByRound(1, oCaster, GetLocation(oCaster)));
		return;
	}

	// Do nothing
	if (iRoundCount == 0)
		DelayCommand(6.0f, AnalyzeDweomerByRound(0, oCaster, lCaster));

	// Locate objects to analyze
	else if (iRoundCount == 1)
	{
		// Initialize the variable that will contain detected object
		sObjectsDetected = "";
		iPosition = 0;

		// Compute the maximum distance of items to identify
		int iCasterLevel = JXGetCasterLevel();
		float iMaxDistance = 7.6 + 1.5 * (iCasterLevel / 2);

		// Loop items in the sphere area
		object oDetected = GetFirstObjectInShape(SHAPE_SPHERE, iMaxDistance, GetLocation(oCaster), TRUE, OBJECT_TYPE_ITEM);
		while (GetIsObjectValid(oDetected))
		{
			// Add the item to the detected objects list
			if (sObjectsDetected != "") sObjectsDetected += ";";
			sObjectsDetected += IntToString(ObjectToInt(oDetected));

			oDetected = GetNextObjectInShape(SHAPE_SPHERE, iMaxDistance, GetLocation(oCaster), TRUE, OBJECT_TYPE_ITEM);
		}

		// Loop creatures in the sphere area
		oDetected = GetFirstObjectInShape(SHAPE_SPHERE, iMaxDistance, GetLocation(oCaster), TRUE, OBJECT_TYPE_CREATURE);
		while (GetIsObjectValid(oDetected))
		{
			if (!GetIsObjectInParty(oDetected))
			{
				// Add the creature to the detected objects list
				if (sObjectsDetected != "") sObjectsDetected += ";";
				sObjectsDetected += IntToString(ObjectToInt(oDetected));

				// Loop all items the creature holds
				object oItemHeld; int iLoop;
				for (iLoop = 0; iLoop < 14; iLoop++)
				{
					oItemHeld = GetItemInSlot(iLoop, oDetected);
					if (GetIsObjectValid(oItemHeld))
					{
						// Add the item to the detected objects list
						if (sObjectsDetected != "") sObjectsDetected += ";";
						sObjectsDetected += IntToString(ObjectToInt(oDetected)) + "," + IntToString(ObjectToInt(oItemHeld));
					}
				}
			}

			oDetected = GetNextObjectInShape(SHAPE_SPHERE, iMaxDistance, GetLocation(oCaster), TRUE, OBJECT_TYPE_CREATURE);
		}

		// Write down the number of objects to analyze
		iObjectsNumber = JXStringSplitCount(sObjectsDetected, ";");

		AnalyzeDweomerByRound(2, oCaster, lCaster);
	}

	// Analyze an object
	else if (iRoundCount == 2)
	{
		// Don't try to identify an item that has already been analyzed
		while (GetIsCurrentObjectAnalyzed(oCaster) && (iPosition < iObjectsNumber))
			iPosition++;
		if (iPosition == iObjectsNumber)
		{
			DelayCommand(6.0, AnalyzeDweomerByRound(0, oCaster, lCaster));
			return;
		}
		// Perform the identification of objects
		AnalyzeDweomerIdentify(oCaster);
		// Whatever the result is, mark the object analyzed
		MarkCurrentObjectAnalyzed();
		// Define the next object to analyze
		if (++iPosition < iObjectsNumber)
			DelayCommand(6.0, AnalyzeDweomerByRound(2, oCaster, lCaster));
		else
			DelayCommand(6.0, AnalyzeDweomerByRound(0, oCaster, lCaster));
	}
}


//--------------------------------------------------
// Analyze Dweomer (main function) : Start detection
//--------------------------------------------------
void main()
{
	object oCaster = GetEnteringObject();

	// Test if the entering object is the caster
	object oAOECreator = JXGetAOERealCreator();
	if (oAOECreator != oCaster) return;

	// Look for items and creatures
	AnalyzeDweomerByRound(1, oCaster, GetLocation(oCaster));
}