// CharacterStats.cpp : Implementation of cCharacterStats
#include "stdafx.h"
#include "DecalFilters.h"
#include "CharacterStats.h"

#include "AllegianceInfo.h"
#include "AttributeInfo.h"
#include "SkillInfo.h"
#include "Enchantment.h"
#include <time.h>

/////////////////////////////////////////////////////////////////////////////
// cCharacterStats

#include <iostream>
#include <fstream>
#include <set>

struct stSkillInfo
{
	LPTSTR Name;
	long AttribA, AttribB;
	long Divider;
	int  TrainStyle;
};

const DWORD PrimStatArray[] = { 0, 110,277,501,784,1125,1527,1988,2511,3097,3746,4459,5238,6084,6998,7982,9038,10167,11372,12654,14015,15459,16988,18604,20311,22113,24012,26014,28122,30341,32676,35132,37716,40434,43293,46301,49465,52795,56300,59991,63878,67975,72295,76851,81659,86737,92102,97775,103775,110128,116858,123991,131559,139591,148124,157194,166843,177113,188053,199715,212153,225429,239609,254762,270967,288306,306870,326756,348070,370928,395453,421779,450054,480434,513091,548210,585992,626654,670432,717582,768378,823122,882136,945773,1014414,1088469,1168386,1254649,1347781,1448351,1556972,1674311,1801089,1938088,2086155,2246205,2419233,2606314,2808613,3027394,3264023,3519983,3796877,4096444,4420567,4771285,5150808,5561528,6006039,6487148,7007896,7571580,8181768,8842327,9557443,10331656,11169877,12077431,13060084,14124082,15276190,16523738,17874666,19337572,20921773,22637359,24495261,26507320,28686361,31046278,33602120,36370190,39368147,42615120,46131828,49940719,54066105,58534323,63373901,68615745,74293328,80442912,87103777,94318471,102133083,110597540,119765922,129696811,140453665,152105222,164725942,178396483,193204214,209243776,226617688,245437001,265822007,287903011,311821164,337729361,365793227,396192167,429120520,464788799,503425038,545276249,590610001,639716134,692908610,750527522,812941268,880548904,953782704,1033110914,1119040753,1212121655,1312948783,1422166831,1540474151,1668627219,1807445467,1957816530,2120701915,2297143157,2488268472,2695299977,2919561502,3162487055,3425629996,3710672964,4019438644 };
const DWORD SecondaryStatArray[] = { 0, 73,183,331,517,743,1008,1312,1658,2044,2472,2943,3457,4015,4619,5268,5965,6711,7505,8352,9250,10203,11212,12279,13406,14595,15848,17169,18561,20025,21566,23187,24893,26687,28574,30559,32647,34845,37158,39594,42160,44864,47715,50722,53895,57247,60788,64531,68492,72685,77126,81834,86829,92130,97762,103748,110116,116895,124115,131812,140021,148784,158142,168143,178838,190282,202534,215659,229726,244812,260999,278375,297036,317087,338640,361819,386755,413592,442486,473604,507130,543260,582210,624211,669513,718390,771135,828069,889536,955912,1027602,1105046,1188719,1279139,1376862,1482495,1596694,1720167,1853685,1998080,2154256,2323189,2505939,2703654,2917575,3149049,3399533,3670609,3963986,4281518,4625212,4997243,5399967,5835936,6307913,6818893,7372119,7971105,8619656,9321894,10082286,10905668,11797280,12762798,13808370,14940657,16166873,17494831,18932998,20490543,22177399,24004326,25982977,28125979,30447007,32960875,35683629,38632653,41826775,45286392,49033597,53092322,57488493,62250191,67407835,72994377,79045509,85599896,92699419,100389447,108719122,117741679,127514781,138100892,149567674,161988421,175442525,190015988,205801968,222901379,241423530,261486830,283219543,306760608,332260525,359882324,389802601,422212649,457319683,495348165,536541237,581162277,629496585,681853203,738566897,800000293,866546197,938630108,1016712940,1101293965,1192914009,1292158910,1399663264,1516114484,1642257192,1778897985,1926910591,2087241457,2260915797,2449044157,2652829505,2873574933,3112691986,3371709687,3652284316,3956210003,4285430197 };

static stSkillInfo SkillInfo[] = {
	{ _T( "Unknown" ), eAttrNULL, eAttrNULL, 1, eTrainUnusable },
    { _T( "Axe" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Bow" ), eAttrCoordination, eAttrNULL, 2, eTrainUnusable },
    { _T( "Crossbow" ), eAttrCoordination, eAttrNULL, 2, eTrainUnusable },
    { _T( "Dagger" ), eAttrQuickness, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Mace" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Melee Defense" ), eAttrQuickness, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Missile Defense" ), eAttrQuickness, eAttrCoordination, 5, eTrainUnusable },
    { _T( "Unknown" ), eAttrNULL, eAttrNULL, 1, eTrainUnusable },
    { _T( "Spear" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Staff" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Sword" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Thrown Weapons" ), eAttrCoordination, eAttrNULL, 2, eTrainUnusable },
    { _T( "Unarmed Combat" ), eAttrStrength, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Arcane Lore" ), eAttrFocus, eAttrNULL, 3, eTrainUnusable },
    { _T( "Magic Defense" ), eAttrFocus, eAttrSelf, 7, eTrainUnusable },
    { _T( "Mana Conversion" ), eAttrFocus, eAttrSelf, 6, eTrainUnusable },
    { _T( "Unknown" ), eAttrNULL, eAttrNULL, 1, eTrainUnusable },
    { _T( "Item Tinkering" ), eAttrFocus, eAttrCoordination, 2, eTrainUnusable },
    { _T( "Assess Person" ), eAttrFocus, eAttrSelf, 2, eTrainUnusable },
    { _T( "Deception" ), eAttrFocus, eAttrSelf, 4, eTrainUnusable },
    { _T( "Healing" ), eAttrFocus, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Jump" ), eAttrStrength, eAttrCoordination, 2, eTrainUnusable },
    { _T( "Lockpick" ), eAttrFocus, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Run" ), eAttrQuickness, eAttrNULL, 1, eTrainUnusable },
    { _T( "Unknown" ), eAttrNULL, eAttrNULL, 1, eTrainUnusable },
    { _T( "Unknown" ), eAttrNULL, eAttrNULL, 1, eTrainUnusable },
    { _T( "Assess Creature" ), eAttrFocus, eAttrSelf, 2, eTrainUnusable },
    { _T( "Weapon Tinkering" ), eAttrFocus, eAttrStrength, 2, eTrainUnusable },
    { _T( "Armor Tinkering" ), eAttrFocus, eAttrEndurance, 2, eTrainUnusable },
    { _T( "Magic Item Tinkering" ), eAttrFocus, eAttrNULL, 1, eTrainUnusable },
    { _T( "Creature Enchantment" ), eAttrFocus, eAttrSelf, 4, eTrainUnusable },
    { _T( "Item Enchantment" ), eAttrFocus, eAttrSelf, 4, eTrainUnusable },
    { _T( "Life Magic" ), eAttrFocus, eAttrSelf, 4, eTrainUnusable },
    { _T( "War Magic" ), eAttrFocus, eAttrSelf, 4, eTrainUnusable },
    { _T( "Leadership" ), eAttrSelf, eAttrNULL, 4, eTrainUnusable },
    { _T( "Loyalty" ), eAttrSelf, eAttrNULL, 4, eTrainUnusable },
    { _T( "Fletching" ), eAttrFocus, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Alchemy" ), eAttrFocus, eAttrCoordination, 3, eTrainUnusable },
    { _T( "Cooking" ), eAttrFocus, eAttrCoordination, 3, eTrainUnusable }
};

STDMETHODIMP cCharacterStats::Initialize(INetService *pService)
{
	m_pService = pService;
	m_pService->get_Decal( &m_pDecal );

	SpellsLearned.clear();
	Enchantments.clear();

	GotLogin = false;
	GotAlleg = false;
	memset( Name, 0, sizeof( Name ) );
	memset( Gender, 0, sizeof( Gender ) );
	memset( Race, 0, sizeof( Race ) );
	memset( Class, 0, sizeof( Class ) );
	memset( Server, 0, sizeof( Server ) );
	memset( PrimStat, 0, sizeof( PrimStat ) );
	memset( PrimStatInitial, 0, sizeof( PrimStatInitial ) );
	memset( SecStatInc, 0, sizeof( SecStatInc ) );
	memset( SkillInc, 0, sizeof( SkillInc ) );
	memset( SkillTrain, 0, sizeof( SkillTrain ) );
	memset( SkillXP, 0, sizeof( SkillXP ) );
	memset( SkillBonus, 0, sizeof( SkillBonus ) );
	memset( &MyAlleg, 0, sizeof( MyAlleg ) );
	memset( &Monarch, 0, sizeof( Monarch ) );
	memset( &Patron, 0, sizeof( Patron ) );
	memset( Vassals, 0, sizeof( Vassals ) );
	TotalBurden = 0;
	TotalPyreal = 0;
	TotalXP = 0;
	UnassignedXP = 0;
	SkillPoints = 0;
	Level = 0;
	Rank = 0;
	Deaths = 0;
	Birth = 0;
	Age = 0;
	Vitae = 1.0f;
	DrainRange = 0.0f;
	HarmRange = 0.0f;
	GUID = 0;
	VassalCount = 0;
	Followers = 0;
	MonarchFollowers = 0;
	return S_OK;
}

STDMETHODIMP cCharacterStats::Terminate()
{
	m_pService.Release();
	m_pDecal.Release();
	
	return S_OK;
}

STDMETHODIMP cCharacterStats::DispatchClient(IMessage2 *pMsg)
{
	return S_OK;
}

STDMETHODIMP cCharacterStats::DispatchServer(IMessage2 *pMessage)
{
	USES_CONVERSION;

	long dwType;	pMessage->get_Type( &dwType );
	switch( dwType )
	{
	case 0xF746: // Character Entering World
		{
			_variant_t vObjectID;

			pMessage->get_Member (_variant_t ("character"), &vObjectID);
			GUID = vObjectID.lVal;

			break;
		}

	case 0xF659: // Login Failure - Char in World
		{
			GotLogin = false;
			GotAlleg = false;
			memset( Name, 0, sizeof( Name ) );
			memset( Gender, 0, sizeof( Gender ) );
			memset( Race, 0, sizeof( Race ) );
			memset( Class, 0, sizeof( Class ) );
			memset( PrimStat, 0, sizeof( PrimStat ) );
			memset( PrimStatInitial, 0, sizeof( PrimStatInitial ) );
			memset( SecStatInc, 0, sizeof( SecStatInc ) );
			memset( SkillInc, 0, sizeof( SkillInc ) );
			memset( SkillTrain, 0, sizeof( SkillTrain ) );
			memset( SkillXP, 0, sizeof( SkillXP ) );
			memset( SkillBonus, 0, sizeof( SkillBonus ) );
			memset( &MyAlleg, 0, sizeof( MyAlleg ) );
			memset( &Monarch, 0, sizeof( Monarch ) );
			memset( &Patron, 0, sizeof( Patron ) );
			memset( Vassals, 0, sizeof( Vassals ) );
			TotalBurden = 0;
			TotalPyreal = 0;
			TotalXP = 0;
			UnassignedXP = 0;
			SkillPoints = 0;
			Level = 0;
			Rank = 0;
			Deaths = 0;
			Birth = 0;
			Age = 0;
			Vitae = 1.0f;
			DrainRange = 0.0f;
			HarmRange = 0.0f;
			GUID = 0;
			VassalCount = 0;
			Followers = 0;
			MonarchFollowers = 0;
			SpellsLearned.clear();
			Enchantments.clear();

			break;
		}
	case 0xF653: // End 3d Mode
		{
			GotLogin = false;
			GotAlleg = false;
			memset( Name, 0, sizeof( Name ) );
			memset( Gender, 0, sizeof( Gender ) );
			memset( Race, 0, sizeof( Race ) );
			memset( Class, 0, sizeof( Class ) );
			memset( PrimStat, 0, sizeof( PrimStat ) );
			memset( PrimStatInitial, 0, sizeof( PrimStatInitial ) );
			memset( SecStatInc, 0, sizeof( SecStatInc ) );
			memset( SkillInc, 0, sizeof( SkillInc ) );
			memset( SkillTrain, 0, sizeof( SkillTrain ) );
			memset( SkillXP, 0, sizeof( SkillXP ) );
			memset( SkillBonus, 0, sizeof( SkillBonus ) );
			memset( &MyAlleg, 0, sizeof( MyAlleg ) );
			memset( &Monarch, 0, sizeof( Monarch ) );
			memset( &Patron, 0, sizeof( Patron ) );
			memset( Vassals, 0, sizeof( Vassals ) );
			TotalBurden = 0;
			TotalPyreal = 0;
			TotalXP = 0;
			UnassignedXP = 0;
			SkillPoints = 0;
			Level = 0;
			Rank = 0;
			Deaths = 0;
			Birth = 0;
			Age = 0;
			Vitae = 1.0f;
			DrainRange = 0.0f;
			HarmRange = 0.0f;
			GUID = 0;
			VassalCount = 0;
			Followers = 0;
			MonarchFollowers = 0;
			SpellsLearned.clear();
			Enchantments.clear();

			break;
		}
	
	case 0xF65A: // MOTD ?
		{
			_variant_t vMess;
			pMessage->get_Member( _variant_t ("message"), &vMess);

			memcpy(Server, OLE2T( vMess.bstrVal ) + 24, sizeof( Server ) );

			//char *szPeriod = strstr( Server, "." );
			// para - fix for the ever changing dt motd
			char * szLf = strstr( Server, "\n" );
			
			//if( szPeriod )
			//	*szPeriod = 0;
			if ( szLf )
			{
				*(--szLf) = 0; // seems ac uses /r/n, but strstr for /r didn't work in testing
				if (*(--szLf) == '.')
					*szLf = 0;

				//check for spaces (eff u dt!!!)
				szLf = strstr(Server, " ");
				if (szLf)
					*szLf = 0;
			}
			else //something went wrong, put "Unknown"
				strncpy(Server, "Unknown\0", 8);

			break;
		}

	case 0x0244:	// Update Vital Statistic
		{
			_variant_t vStat;
			_variant_t vValue;

			pMessage->get_Member (_variant_t ("vital"), &vStat);
			_ASSERTE(vStat.vt == VT_I4);

			pMessage->get_Member (_variant_t ("value"), &vValue);
			_ASSERTE(vValue.vt == VT_I4);

			switch (vStat.lVal)
			{
			case 2:	//Health
				Health = vValue.lVal;
				break;
			case 4:	//Stam
				Stamina = vValue.lVal;
				break;
			case 6:	//Mana
				Mana = vValue.lVal;
				break;
			}

			break;
		}
	case 0xF748: // Set Position and Motion
		{
			_variant_t vGUID;

			pMessage->get_Member(_variant_t ("object"), &vGUID);

			if (vGUID.lVal == GUID)
			{
				_variant_t vLandblock, vX, vY, vZ, vW;
				_variant_t vLoc;
				pMessage->get_Member (_variant_t ("position"), &vLoc);
				IMessageMember *pLoc = (IMessageMember *) vLoc.pdispVal;

				pLoc->get_Member(_variant_t ("landblock"), &vLandblock);
				pLoc->get_Member(_variant_t ("xOffset"), &vX);
				pLoc->get_Member(_variant_t ("yOffset"), &vY);
				pLoc->get_Member(_variant_t ("zOffset"), &vZ);
				pLoc->get_Member(_variant_t ("xHeading"), &vW);
				
				Location.landblock = vLandblock.lVal;
				Location.x = vX.fltVal;
				Location.y = vY.fltVal;
				Location.z = vZ.fltVal;
				Location.w = vW.fltVal;
			}
			break;
		}
	
	case 0x0237:
		{
			//Update Statistic

			_variant_t vType, vVal, vSeq;
			pMessage->get_Member(_variant_t("statistic"), &vType);
			pMessage->get_Member(_variant_t("value"), &vVal);

			if (vType.lVal == 0x05)
				TotalBurden = vVal.lVal;
			else if (vType.lVal == 0x14)
				TotalPyreal = vVal.lVal;
			else if (vType.lVal == 0x15)
				TotalXP = vVal.lVal;
			else if (vType.lVal == 0x16)
				UnassignedXP = vVal.lVal;
			else if (vType.lVal == 0x18)
				SkillPoints = vVal.lVal;
			else if (vType.lVal == 0x19)
				Level = vVal.lVal;
			else if (vType.lVal == 0x1E)
				Rank = vVal.lVal;
			else if (vType.lVal == 0x2B)
			{
				Deaths = vVal.lVal;

				// Remove all but item based enchantments
				for (std::list<cEnchantment *>::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
				{
					time_t CurrentTime = time(NULL);
					long SecondsLeft = (*tpi)->ExpireTime - CurrentTime;
	
					if ( SecondsLeft >= 0 )
					{
						delete *tpi;
						Enchantments.erase(tpi--);
					}
				}

				RecalcSecStats();
				RecalcSkills();
			}

			else if (vType.lVal == 0x62)
				Birth = vVal.lVal;
			else if (vType.lVal == 0x7D)
				Age = vVal.lVal;

			break;
		}

	case 0x023E:
		{
			//Update Skill

			_variant_t vSkill, vInc, vTrain, vXP;
			pMessage->get_Member(_variant_t("skill"), &vSkill);
			pMessage->get_Member(_variant_t("skillOffset"), &vInc);
			pMessage->get_Member(_variant_t("skillTrained"), &vTrain);
			pMessage->get_Member(_variant_t("appliedXP"), &vXP);

			SkillInc[vSkill.iVal] = vInc.iVal;
			SkillTrain[vSkill.iVal] = vTrain.lVal;
			SkillXP[vSkill.iVal] = vXP.lVal;

			RecalcSkill(vSkill.iVal);

			break;
		}

	case 0x0241:
		{
			//Update Attribute

			_variant_t vAttrib, vNew, vStartingValue;
			pMessage->get_Member(_variant_t("Attribute"), &vAttrib);
			pMessage->get_Member(_variant_t("NewIncrement"), &vNew);
			pMessage->get_Member(_variant_t("StartingValue"), &vStartingValue);

			PrimStat[vAttrib.iVal - 1] = vNew.lVal + vStartingValue.lVal;
            
			RecalcStat(vAttrib.iVal - 1);
			RecalcSkills();
			RecalcSecStats();

			break;
		}

	case 0x0243:
		{
			//Update Secondary Attribute

			_variant_t vAttrib, vNew;
			pMessage->get_Member(_variant_t("Attribute"), &vAttrib);
			pMessage->get_Member(_variant_t("PointsAdded"), &vNew);

			SecStatInc[(vAttrib.iVal - 1) / 2] = vNew.lVal;

			RecalcSecStats();

			break;
		}
	case 0xF7B0: // Game Event
		{
			_variant_t vEvent;

			pMessage->get_Member (_variant_t ("event"), &vEvent);
			_ASSERTE(vEvent.vt == VT_I4);

			switch (vEvent.intVal)
			{
			case (0x0013):	// Character Login
				{
					GotLogin = true;

					_variant_t vObjectID;
					_variant_t vStrings, vSpells;
					_variant_t vStringCount, vSpellCount;
					_variant_t vName;

					pMessage->get_Member (_variant_t ("character"), &vObjectID);
					GUID = vObjectID.lVal;

					pMessage->get_Member (_variant_t ("countStrings"), &vStringCount);
					pMessage->get_Member (_variant_t ("strings"), &vStrings);
					for(int i = 0; i < (vStringCount.iVal); i++)
					{
						IMessageMember* pMember = (IMessageMember*) vStrings.pdispVal;
						_variant_t vString1;

						pMember->get_Member (_variant_t (long(i)), &vString1);

						IMessageMember* pString  = (IMessageMember*) vString1.pdispVal;
						_variant_t vKey;

						pString->get_Member (_variant_t ("key"), &vKey);
						_variant_t vVal;
						pString->get_Member(_variant_t ("string"), &vVal);

						if (vKey.lVal == 0x01)
							strncpy( Name, OLE2T( vVal.bstrVal ),sizeof( Name ) );
						else if (vKey.lVal == 0x03)
							strncpy( Gender, OLE2T( vVal.bstrVal ),sizeof( Gender ) );
						else if (vKey.lVal == 0x04)
							strncpy( Race, OLE2T( vVal.bstrVal ),sizeof( Race ) );
						else if (vKey.lVal == 0x05)
							strncpy( Class, OLE2T( vVal.bstrVal ),sizeof( Class ) );
					}

					pMessage->get_Member (_variant_t ("countStats"), &vStringCount);
					pMessage->get_Member (_variant_t ("stats"), &vStrings);
					for(i = 0; i < (vStringCount.iVal); i++)
					{
						IMessageMember* pMember = (IMessageMember*) vStrings.pdispVal;
						_variant_t vString1;

						pMember->get_Member (_variant_t (long(i)), &vString1);

						IMessageMember* pString  = (IMessageMember*) vString1.pdispVal;
						
						_variant_t vType, vVal;
						pString->get_Member (_variant_t ("key"), &vType);
						pString->get_Member (_variant_t ("value"), &vVal);

						if (vType.lVal == 0x05)
							TotalBurden = vVal.lVal;
						else if (vType.lVal == 0x14)
							TotalPyreal = vVal.lVal;
						else if (vType.lVal == 0x15)
							TotalXP = vVal.lVal;
						else if (vType.lVal == 0x16)
							UnassignedXP = vVal.lVal;
						else if (vType.lVal == 0x18)
							SkillPoints = vVal.lVal;
						else if (vType.lVal == 0x19)
							Level = vVal.lVal;
						else if (vType.lVal == 0x1E)
							Rank = vVal.lVal;
						else if (vType.lVal == 0x2B)
							Deaths = vVal.lVal;
						else if (vType.lVal == 0x62)
							Birth = vVal.lVal;
						else if (vType.lVal == 0x7D)
							Age = vVal.lVal;
					}
					
					//New CharInfo Stuff
					_variant_t G;
					pMessage->get_Member(_variant_t ("initialStrength"), &G);		PrimStatInitial[0] = G.lVal;
					pMessage->get_Member(_variant_t ("initialEndurance"), &G);		PrimStatInitial[1] = G.lVal;
					pMessage->get_Member(_variant_t ("initialQuickness"), &G);		PrimStatInitial[2] = G.lVal;
					pMessage->get_Member(_variant_t ("initialCoordination"), &G);	PrimStatInitial[3] = G.lVal;
					pMessage->get_Member(_variant_t ("initialFocus"), &G);			PrimStatInitial[4] = G.lVal;
					pMessage->get_Member(_variant_t ("initialSelf"), &G);			PrimStatInitial[5] = G.lVal;

					pMessage->get_Member(_variant_t ("incStrength"), &G);			PrimStat[0] = G.lVal + PrimStatInitial[0]; CurStat[0] = PrimStat[0];
					pMessage->get_Member(_variant_t ("incEndurance"), &G);			PrimStat[1] = G.lVal + PrimStatInitial[1]; CurStat[1] = PrimStat[1];
					pMessage->get_Member(_variant_t ("incQuickness"), &G);			PrimStat[2] = G.lVal + PrimStatInitial[2]; CurStat[2] = PrimStat[2];
					pMessage->get_Member(_variant_t ("incCoordination"), &G);		PrimStat[3] = G.lVal + PrimStatInitial[3]; CurStat[3] = PrimStat[3];
					pMessage->get_Member(_variant_t ("incFocus"), &G);				PrimStat[4] = G.lVal + PrimStatInitial[4]; CurStat[4] = PrimStat[4];
					pMessage->get_Member(_variant_t ("incSelf"), &G);				PrimStat[5] = G.lVal + PrimStatInitial[5]; CurStat[5] = PrimStat[5];

					pMessage->get_Member(_variant_t ("incHealth"), &G);				SecStatInc[0] = G.lVal;
					pMessage->get_Member(_variant_t ("incStamina"), &G);			SecStatInc[1] = G.lVal;
					pMessage->get_Member(_variant_t ("incMana"), &G);				SecStatInc[2] = G.lVal;
					
					pMessage->get_Member(_variant_t ("currentHealth"), &G);			Health = G.lVal;
					pMessage->get_Member(_variant_t ("currentStamina"), &G);		Stamina = G.lVal;
					pMessage->get_Member(_variant_t ("currentMana"), &G);			Mana = G.lVal;

					_variant_t vRecordCount, vSkills;
					pMessage->get_Member (_variant_t ("skillCount"), &vRecordCount);
					pMessage->get_Member (_variant_t ("skills"), &vSkills);

					ZeroMemory(SkillTrain, sizeof(SkillTrain));

					for(i = 0; i < (vRecordCount.iVal); i++)
					{
						IMessageMember* pMember  = (IMessageMember*) vSkills.pdispVal;
						_variant_t vMember1;

						pMember->get_Member (_variant_t (long(i)), &vMember1);
						IMessageMember* pRecord = (IMessageMember*) vMember1.pdispVal;

						_variant_t vSkillN, vSkillI, vSkillT, vSkillX, vSkillF;
						pRecord->get_Member(_variant_t ("skill"), &vSkillN);
						pRecord->get_Member(_variant_t ("increment"), &vSkillI);
						pRecord->get_Member(_variant_t ("trained"), &vSkillT);
						pRecord->get_Member(_variant_t ("exp"), &vSkillX);
						pRecord->get_Member(_variant_t ("bonusPoints"), &vSkillF);

						SkillBonus[vSkillN.iVal] = vSkillF.lVal;
						SkillXP[vSkillN.iVal] = vSkillX.lVal;
						SkillTrain[vSkillN.iVal] = vSkillT.lVal;
						SkillInc[vSkillN.iVal] = vSkillI.iVal;

						float TPS = 0;

						if (SkillInfo[vSkillN.iVal].AttribA != eAttrNULL)
							TPS += PrimStat[SkillInfo[vSkillN.iVal].AttribA];
						if (SkillInfo[vSkillN.iVal].AttribB != eAttrNULL)
							TPS += PrimStat[SkillInfo[vSkillN.iVal].AttribB];

						TPS /= SkillInfo[vSkillN.iVal].Divider;

						TPS += vSkillI.lVal + 0.5f;

						CurSkill[vSkillN.iVal] = static_cast <DWORD> (TPS);
					}

					//Get Spellbook
					CComPtr<IMessageIterator> pBegin;
					CComPtr<IMessageIterator> pSpell;
					CComPtr<IMessageIterator> pSpellbook;

					pMessage->get_Begin( &pBegin );
					pMessage->get_Member( _variant_t( "spellbookCount" ), &vSpellCount ); 

					short sSpellbookCount = vSpellCount.iVal;
					HRESULT HRes = pBegin->get_NextObject( _bstr_t( "spellbook" ), &pSpellbook );
					if( HRes == S_OK )
					{
						for( int i = 0; i < sSpellbookCount; i++ )
						{
							pSpellbook->get_NextObjectIndex( &pSpell );

							long lSpellID;
							pSpell->get_NextInt( _bstr_t( "spell" ), &lSpellID );

							SpellsLearned.insert( lSpellID );

							pSpell.Release();
						}
					}

					//Get Enchantments
					_variant_t vSpell1;
					_variant_t vEnchantment, vSpellID, vIndex, vSourceID, vDifficulty, vEffectMask, vAffected, vLevelMask, vFamily, vDuration, vTimeElapsed, vStartTime, vAdjustment;
					
					// Get Life Enchantments
					pMessage->get_Member (_variant_t ("lifeSpellCount"), &vSpellCount);
					_ASSERTE(vSpellCount.vt == VT_I4);

					if(vSpellCount.vt == VT_I4)
					{
						pMessage->get_Member (_variant_t ("lifeSpells"), &vSpells);
						_ASSERTE (vSpells.vt == VT_DISPATCH);

						if(vSpells.vt == VT_DISPATCH)
						{
							for(i = 0; i < (vSpellCount.lVal); i++)
							{
								IMessageMember* pMember = (IMessageMember*) vSpells.pdispVal;

								pMember->get_Member (_variant_t (long(i)), &vSpell1);
								_ASSERTE(vSpell1.vt == VT_DISPATCH);

								if (vSpell1.vt == VT_DISPATCH)
								{
									IMessageMember* pRecord  = (IMessageMember*) vSpell1.pdispVal;

									pRecord->get_Member (_variant_t ("enchantment"), &vEnchantment);
									_ASSERTE (vEnchantment.vt == VT_DISPATCH);

									if(vEnchantment.vt != VT_DISPATCH)
										break;

									IMessageMember* pEnchantment = (IMessageMember*) vEnchantment.pdispVal;

									pEnchantment->get_Member (_variant_t ("spell"), &vSpellID);
									_ASSERTE(vSpellID.vt == VT_I2);
									pEnchantment->get_Member (_variant_t ("source"), &vSourceID);
									_ASSERTE(vSourceID.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("layers"), &vIndex);
									_ASSERTE(vIndex.vt == VT_I2);
									pEnchantment->get_Member (_variant_t ("difficulty"), &vDifficulty);
									_ASSERTE(vDifficulty.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("family"), &vFamily);
									_ASSERTE(vFamily.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("duration"), &vDuration);
									_ASSERTE(vDuration.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("timeElapsed"), &vTimeElapsed);
									_ASSERTE(vTimeElapsed.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("startTime"), &vStartTime);
									_ASSERTE(vStartTime.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("flags"), &vEffectMask);
									_ASSERTE(vEffectMask.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("affected"), &vAffected);
									_ASSERTE(vAffected.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("adjustment"), &vAdjustment);
									_ASSERTE(vAdjustment.vt == VT_R4);

									AddEnchant(vSpellID.iVal, vIndex.iVal, vDuration.dblVal, vFamily.lVal, vTimeElapsed.dblVal, vEffectMask.lVal, vAffected.lVal, vAdjustment.fltVal);
								}
							}
						}
					} // vSpellCount.vt == VT_I4

					// Get Creature Enchantments
					pMessage->get_Member (_variant_t ("creatureSpellCount"), &vSpellCount);
					_ASSERTE(vSpellCount.vt == VT_I4);

					if(vSpellCount.vt == VT_I4)
					{
						pMessage->get_Member (_variant_t ("creatureSpells"), &vSpells);
						_ASSERTE (vSpells.vt == VT_DISPATCH);

						if(vSpells.vt == VT_DISPATCH)
						{
							for(i = 0; i < (vSpellCount.lVal); i++)
							{
								IMessageMember* pMember = (IMessageMember*) vSpells.pdispVal;

								pMember->get_Member (_variant_t (long(i)), &vSpell1);
								_ASSERTE(vSpell1.vt == VT_DISPATCH);

								if (vSpell1.vt == VT_DISPATCH)
								{
									IMessageMember* pRecord  = (IMessageMember*) vSpell1.pdispVal;

									pRecord->get_Member (_variant_t ("enchantment"), &vEnchantment);
									_ASSERTE (vEnchantment.vt == VT_DISPATCH);

									if(vEnchantment.vt != VT_DISPATCH)
										break;

									IMessageMember* pEnchantment = (IMessageMember*) vEnchantment.pdispVal;

									pEnchantment->get_Member (_variant_t ("spell"), &vSpellID);
									_ASSERTE(vSpellID.vt == VT_I2);
									pEnchantment->get_Member (_variant_t ("source"), &vSourceID);
									_ASSERTE(vSourceID.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("layers"), &vIndex);
									_ASSERTE(vIndex.vt == VT_I2);
									pEnchantment->get_Member (_variant_t ("difficulty"), &vDifficulty);
									_ASSERTE(vDifficulty.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("family"), &vFamily);
									_ASSERTE(vFamily.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("duration"), &vDuration);
									_ASSERTE(vDuration.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("timeElapsed"), &vTimeElapsed);
									_ASSERTE(vTimeElapsed.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("startTime"), &vStartTime);
									_ASSERTE(vStartTime.vt == VT_R8);
									pEnchantment->get_Member (_variant_t ("flags"), &vEffectMask);
									_ASSERTE(vEffectMask.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("affected"), &vAffected);
									_ASSERTE(vAffected.vt == VT_I4);
									pEnchantment->get_Member (_variant_t ("adjustment"), &vAdjustment);
									_ASSERTE(vAdjustment.vt == VT_R4);

									AddEnchant(vSpellID.iVal, vIndex.iVal, vDuration.dblVal, vFamily.lVal, vTimeElapsed.dblVal, vEffectMask.lVal, vAffected.lVal, vAdjustment.fltVal);
								}
							}
						}
					} // vSpellCount.vt == VT_I4

					_variant_t vVitae;
					if( SUCCEEDED(pMessage->get_Member(_variant_t ("vitae"), &vVitae)))
					{
						if( vVitae.vt != VT_EMPTY )
						{
							IMessageMember* pVitae = (IMessageMember*) vVitae.pdispVal;
							pVitae->get_Member( _variant_t ("adjustment"), &vVitae);
							Vitae = vVitae.fltVal + 0.0000001f; // for float inaccuracies like 0.9499999881
						}

						else
							Vitae = 1.0f;
					}

					else
						Vitae = 1.0f;

					// Added to allow for varying enchantmentMask section since the above section doesn't
					RecalcSecStats();
					RecalcSkills();

					Fire_Login( GUID );
				}

				break; // Login

			case 0x0020: // Allegiance Info
				{
					GotAlleg = true;
					Patron.GUID = 0;
					Monarch.GUID = 0;
					MyAlleg.GUID = 0;
					VassalCount = 0;

					_variant_t vRecordCount, vRecords, vFol, vMonFol;

					pMessage->get_Member (_variant_t ("followers"), &vFol);
					pMessage->get_Member (_variant_t ("allegianceSize"), &vMonFol);

					Followers = vFol.lVal;
					MonarchFollowers = vMonFol.lVal;

					pMessage->get_Member (_variant_t ("recordCount"), &vRecordCount);
					_ASSERTE(vRecordCount.vt == VT_I2);

					pMessage->get_Member (_variant_t ("records"), &vRecords);
					_ASSERTE (vRecords.vt == VT_DISPATCH);

					//std::map< DWORD, cAllegianceInfo * >	AllegMap;
					std::vector<cAllegianceInfo *> AllegInfo;

					for(int i = 0; i < (vRecordCount.bVal); i++)
					{
						IMessageMember* pMember = (IMessageMember*) vRecords.pdispVal;
						_variant_t vMember1;

						pMember->get_Member (_variant_t (long(i)), &vMember1);

						IMessageMember* pRecord = (IMessageMember*) vMember1.pdispVal;
						_variant_t vInfoRec;
						pRecord->get_Member (_variant_t ("info"), &vInfoRec);
						IMessageMember* pInfo  = (IMessageMember*) vInfoRec.pdispVal;

						_variant_t vTreeParent, vGUID, vType, vXP, vGender, vRace, vRank, vLoyalty, vLeader, vName, vUnknown;
						
						pRecord->get_Member (_variant_t ("treeParent"), &vTreeParent);

						pInfo->get_Member (_variant_t ("character"), &vGUID);
						pInfo->get_Member (_variant_t ("type"), &vType);
						pInfo->get_Member (_variant_t ("exp"), &vXP);
						pInfo->get_Member (_variant_t ("gender"), &vGender);
						pInfo->get_Member (_variant_t ("race"), &vRace);
						pInfo->get_Member (_variant_t ("rank"), &vRank);
						pInfo->get_Member (_variant_t ("loyalty"), &vLoyalty);
						pInfo->get_Member (_variant_t ("leadership"), &vLeader);
						pInfo->get_Member (_variant_t ("unknown"), &vUnknown);
						pInfo->get_Member (_variant_t ("name"), &vName);

						cAllegianceInfo * tpai = new cAllegianceInfo;
						tpai->TreeParent = vTreeParent.lVal;
						tpai->GUID = vGUID.lVal;
						tpai->Type = vType.lVal;
						tpai->XP = vXP.lVal;
						tpai->Gender = vGender.bVal;
						tpai->Race = vRace.bVal;
						tpai->Rank = vRank.bVal;
						tpai->Loyalty = vLoyalty.iVal;
						tpai->Leadership = vLeader.iVal;
						tpai->Unknown = vUnknown.dblVal;
						strncpy( tpai->Name, OLE2A( vName.bstrVal ), sizeof( tpai->Name ) );
						
						//AllegMap.insert( std::make_pair( vGUID.lVal, tpai ) );
						AllegInfo.push_back(tpai);
					}

					//std::map< DWORD, cAllegianceInfo * >::iterator i = AllegMap.find( GUID );

					// Solo player, get outta here
					//if( i == AllegMap.end() )
					if ( AllegInfo.size() == 0 )
						break;

					//we now 'know' that the monarch is the first entry, always
					//std::map< DWORD, cAllegianceInfo * >::iterator i2 = AllegMap.begin();
					std::vector<cAllegianceInfo *>::iterator j = AllegInfo.begin();

					if ( (*j)->GUID != GUID ) //i'm not the monarch, so find out who is
					{
						if ( (*(j + 1))->GUID != GUID ) //i'm not a direct either
						{
							memcpy(&Monarch, *j, sizeof(cAllegianceInfo));
							//OutputDebugString(Monarch.Name);
							delete *j;
							j = AllegInfo.erase( j ); // this is my patron now
						}
						memcpy( &Patron, *j, sizeof( cAllegianceInfo ) );
						//OutputDebugString(Patron.Name);
						delete *j;
						j = AllegInfo.erase( j );
					}
					// we've taken care of any possible patron & monarch,
					// so i'm all that's left
					memcpy(&MyAlleg, *j, sizeof(cAllegianceInfo));
					//OutputDebugString(MyAlleg.Name);
					delete *j;
					j = AllegInfo.erase( j );
					
					//check for patron
					/*
					i = AllegMap.find( MyAlleg.TreeParent );
					if ( i != AllegMap.end() )
					{
						memcpy( &Patron, i->second, sizeof( cAllegianceInfo ) );
						delete i->second;
						AllegMap.erase( i );
					}

					//check for a monarch
					if ( Patron.GUID != 0 )
					{
						i = AllegMap.find( Patron.TreeParent );
						if ( i != AllegMap.end() )
						{
							memcpy( &Monarch, i->second, sizeof( cAllegianceInfo ) );
							delete i->second;
							AllegMap.erase( i );
						}
					}
					*/

					//The remainder *should* all be vassals
					while( AllegInfo.size() > 0 )
					{
						//for( i = AllegInfo.begin(); i != AllegInfo.end(); ++i )
						//{
							// Patron
							/*
							if( i->second->GUID == MyAlleg.TreeParent ) 
							{
								memcpy( &Patron, i->second, sizeof( cAllegianceInfo ) );
								OutputDebugString("Got Patron\n");

								delete i->second;
								i = AllegMap.erase( i );

								continue;
							}
							*/

							//Vassal
							//just leaving the sanity check in place here
							//if ((*i)->TreeParent == GUID)
							//{
								memcpy( &Vassals[VassalCount], *j, sizeof( cAllegianceInfo ) );
								//OutputDebugString(Vassals[VassalCount].Name);
								VassalCount++;

								delete *j;
								j = AllegInfo.erase( j );

								//continue;
							//}
						//}
					}					
					
					// only remnant should be monarch
					//p = AllegMap.begin()->second;
					//memcpy( &Monarch, p, sizeof( cAllegianceInfo ) );
					//delete p;

					break;
				}

			case 0x004E: // Add Enchantment
				{
					_variant_t vEnchantment, vSpellID, vIndex, vSourceID, vDifficulty, vEffectMask, vAffected, vLevelMask, vFamily, vDuration, vTimeElapsed, vStartTime, vAdjustment;
					
					pMessage->get_Member (_variant_t ("enchantment"), &vEnchantment);
					IMessageMember* pEnchantment = (IMessageMember*) vEnchantment.pdispVal;
					pEnchantment->get_Member (_variant_t ("spell"), &vSpellID);
					_ASSERTE(vSpellID.vt == VT_I2);
					pEnchantment->get_Member (_variant_t ("source"), &vSourceID);
					_ASSERTE(vSourceID.vt == VT_I4);
					pEnchantment->get_Member (_variant_t ("layers"), &vIndex);
					_ASSERTE(vIndex.vt == VT_I2);
					pEnchantment->get_Member (_variant_t ("difficulty"), &vDifficulty);
					_ASSERTE(vDifficulty.vt == VT_I4);
					pEnchantment->get_Member (_variant_t ("family"), &vFamily);
					_ASSERTE(vFamily.vt == VT_I4);
					pEnchantment->get_Member (_variant_t ("duration"), &vDuration);
					_ASSERTE(vDuration.vt == VT_R8);
					pEnchantment->get_Member (_variant_t ("timeElapsed"), &vTimeElapsed);
					_ASSERTE(vTimeElapsed.vt == VT_R8);
					pEnchantment->get_Member (_variant_t ("startTime"), &vStartTime);
					_ASSERTE(vStartTime.vt == VT_R8);
					pEnchantment->get_Member (_variant_t ("flags"), &vEffectMask);
					_ASSERTE(vEffectMask.vt == VT_I4);
					pEnchantment->get_Member (_variant_t ("affected"), &vAffected);
					_ASSERTE(vAffected.vt == VT_I4);
					pEnchantment->get_Member (_variant_t ("adjustment"), &vAdjustment);
					_ASSERTE(vAdjustment.vt == VT_R4);
					
					if( 666 == vSpellID.iVal ) // Vitae isn't a normal enchantment
					{
						Vitae = vAdjustment.fltVal + 0.0000001f; // for float inaccuracies like 0.9499999881
						RecalcSecStats();
						RecalcSkills();
					}
					else
						AddEnchant(vSpellID.iVal, vIndex.iVal, vDuration.dblVal, vFamily.lVal, vTimeElapsed.dblVal, vEffectMask.lVal, vAffected.lVal, vAdjustment.fltVal);

					break;
				}

			case 0x01AE: // Remove Multiple Enchantments 
				{
					_variant_t vNumSpells, vSpells, vSpell1;
					pMessage->get_Member(_variant_t ("count"), &vNumSpells);
					pMessage->get_Member(_variant_t ("enchantments"), &vSpells);

					IMessageMember* pMember = (IMessageMember*) vSpells.pdispVal;

					for (int i=0;i<vNumSpells.lVal;i++)
					{
						pMember->get_Member (_variant_t (long(i)), &vSpell1);
						IMessageMember* pRecord = (IMessageMember*) vSpell1.pdispVal;

						_variant_t vSpellID, vLayer;
						
						pRecord->get_Member (_variant_t ("spell"), &vSpellID);
						pRecord->get_Member (_variant_t ("layer"), &vLayer);

						if( 666 == vSpellID.iVal ) // Vitae isn't a normal enchantment
							Vitae = 1.0f;
						else
							RemoveEnchant(vSpellID.iVal, vLayer.iVal);
					}

					break;
				}


			case 0x004F: // Remove Enchantment 
				{
					_variant_t vSpellID, vLayer;
					
					pMessage->get_Member (_variant_t ("spell"), &vSpellID);
					pMessage->get_Member (_variant_t ("layer"), &vLayer);

					RemoveEnchant(vSpellID.iVal, vLayer.iVal);

					break;
				}

			case 0x01A4: // Remove Enchantment (Silent) 
				{
					_variant_t vSpellID, vLayer;
					
					pMessage->get_Member (_variant_t ("spell"), &vSpellID);
					pMessage->get_Member (_variant_t ("layers"), &vLayer);

					RemoveEnchant(vSpellID.iVal, vLayer.iVal);

					break;
				}

			case 0x01A6: // Remove Multiple Enchantments 
				{
					_variant_t vNumSpells, vSpells, vSpell1;
					pMessage->get_Member(_variant_t ("count"), &vNumSpells);
					pMessage->get_Member(_variant_t ("enchantments"), &vSpells);

					IMessageMember* pMember = (IMessageMember*) vSpells.pdispVal;

					for (int i=0;i<vNumSpells.lVal;i++)
					{
						pMember->get_Member (_variant_t (long(i)), &vSpell1);
						IMessageMember* pRecord = (IMessageMember*) vSpell1.pdispVal;

						_variant_t vSpellID, vLayer;
						
						pRecord->get_Member (_variant_t ("spell"), &vSpellID);
						pRecord->get_Member (_variant_t ("layer"), &vLayer);

						RemoveEnchant(vSpellID.iVal, vLayer.iVal);
					}

					break;
				}
			case 0x004C:	// Spellbook Add
				{
					_variant_t vSpellID;
					pMessage->get_Member( _variant_t( "spell" ), &vSpellID );
					DWORD nSpellID = vSpellID.lVal;

					std::pair< std::set< DWORD >::iterator, bool > pSuccess;
					
					pSuccess = SpellsLearned.insert( nSpellID );
					if( pSuccess.second )
						Fire_Spellbook_Add( nSpellID );

					break;
				}

			case 0x004D:	// Spellbook Delete
				{
					_variant_t vSpellID;
					pMessage->get_Member( _variant_t( "spell" ), &vSpellID );
					DWORD nSpellID = vSpellID.lVal;
					if( SpellsLearned.erase( nSpellID ) )
						Fire_Spellbook_Delete( nSpellID );

					break;
				}

			} // End - F7B0
		}
	}

	return S_OK;
}

void cCharacterStats::AddEnchant(int SpellID, int Layer, double Duration, DWORD Family, double Elapsed, DWORD AffectMask, DWORD Affected, float Adjustment)
{
	time_t TimeExpires = time(NULL);
	// duration of the spell plus the (negative) number the server sends us indicating how many seconds have elapsed
	TimeExpires += (long(Duration + 0.5f) + long(Elapsed + 0.5f));

	cEnchantment *tpench = new cEnchantment;
	tpench->iSpellID = SpellID;
	tpench->iLayer = Layer;
	tpench->dwFamily = Family;
	tpench->ExpireTime = TimeExpires;
	tpench->dwAffected = Affected;
	tpench->dwAffectMask = AffectMask;
	tpench->fAdjustment = Adjustment;

	Enchantments.push_back(tpench);

	std::map<DWORD, float> FamilyMap;

	switch (tpench->dwAffectMask & 0x13)
	{
	case 0x01:	//Primary Stat
		{
			tpench->dwAffected--;

			for (std::list<cEnchantment *>::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
			{
				if ((((*tpi)->dwAffectMask & 0x13) == 0x01) && ((*tpi)->dwAffected == tpench->dwAffected))
				{
					if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
						FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
				}
			}

			float tpsf = PrimStat[tpench->dwAffected];

			for (std::map<DWORD, float>::iterator tpf = FamilyMap.begin(); tpf != FamilyMap.end(); tpf++)
				tpsf += (*tpf).second;

			CurStat[tpench->dwAffected] = static_cast <DWORD> (tpsf + 0.5f);

			RecalcSecStats();
			RecalcSkills();
		}
		break;
	case 0x02:	//Secondary Stat
		{
			tpench->dwAffected--;
			tpench->dwAffected /= 2;

			for (std::list<cEnchantment *>::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
			{
				if ((((*tpi)->dwAffectMask & 0x13) == 0x02) && ((*tpi)->dwAffected == tpench->dwAffected))
				{
					if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
						FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
				}
			}

			float tpsf = SecStatInc[tpench->dwAffected];

			if (tpench->dwAffected == 0) tpsf += CurStat[1] / 2.0f + 0.5f;
			else if (tpench->dwAffected == 1) tpsf += CurStat[1];
			else if (tpench->dwAffected == 2) tpsf += CurStat[5];

			for (std::map<DWORD, float>::iterator tpf = FamilyMap.begin(); tpf != FamilyMap.end(); tpf++)
				tpsf += (*tpf).second;

			CurSecStat[tpench->dwAffected] = static_cast <DWORD> (tpsf);
		}
		break;
	case 0x10:	//Skill
		{
			for (std::list<cEnchantment *>::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
			{
				if ((((*tpi)->dwAffectMask & 0x13) == 0x10) && ((*tpi)->dwAffected == tpench->dwAffected))
				{
					if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
						FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
				}
			}

			RecalcSkill(tpench->dwAffected);

		}
		break;
	}
}

void cCharacterStats::RemoveEnchant(int SpellID, int Layer)
{
	for (std::list< cEnchantment * >::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
	{
		if( ( (*tpi)->iSpellID == SpellID) && (  (*tpi)->iLayer == Layer) )
		{
            cEnchantment tpench;
			memcpy(&tpench, *tpi, sizeof(tpench));
			delete *tpi;
			Enchantments.erase(tpi);
			if ((tpench.dwAffectMask & 0x013) == 0x01)
			{
				RecalcStat(tpench.dwAffected);
				RecalcSecStats();
				RecalcSkills();
			}
			else if ((tpench.dwAffectMask & 0x013) == 0x02)
			{
				RecalcSecStats();
			}
			else if ((tpench.dwAffectMask & 0x013) == 0x10)
			{
				RecalcSkill(tpench.dwAffected);
			}
			tpi = Enchantments.begin();
		}
	}
}

void cCharacterStats::RecalcStat(int Stat)
{
	std::map<DWORD, float> FamilyMap;

	for (std::list< cEnchantment * >::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
	{
		if ((((*tpi)->dwAffectMask & 0x13) == 0x01) && ((*tpi)->dwAffected == Stat))
		{
			if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
				FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
		}
	}

	float tpsf = (float) PrimStat[Stat];

	for (std::map<DWORD, float>::iterator tpf = FamilyMap.begin(); tpf != FamilyMap.end(); tpf++)
		tpsf += (*tpf).second;

	CurStat[Stat] = static_cast <DWORD> (tpsf);
}

void cCharacterStats::RecalcSecStats()
{
	for (int i=0;i<3;i++)
		RecalcSecStat(i);
}

void cCharacterStats::RecalcSecStat(int SecStat)
{
	std::map<DWORD, float> FamilyMap;

	for (std::list< cEnchantment * >::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
	{
		if( (((*tpi)->dwAffectMask & 0x13) == 0x02) && ((*tpi)->dwAffected == SecStat) )
		{
			if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
				FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
		}
	}

	float tpsf = SecStatInc[SecStat];
	if (SecStat == 0) tpsf += CurStat[1] / 2.0f + 0.5f;
	else if (SecStat == 1) tpsf += CurStat[1];
	else if (SecStat == 2) tpsf += CurStat[5];

	for (std::map<DWORD, float>::iterator tpf = FamilyMap.begin(); tpf != FamilyMap.end(); tpf++)
		tpsf += (*tpf).second;

	CurSecStat[SecStat] = static_cast <DWORD> (tpsf);
}
void cCharacterStats::RecalcSkills()
{
	for (int i=0;i<40;i++)
		RecalcSkill(i);
}
void cCharacterStats::RecalcSkill(int Skill)
{
	std::map<DWORD, float> FamilyMap;

	for (std::list<cEnchantment *>::iterator tpi = Enchantments.begin(); tpi != Enchantments.end(); tpi++)
	{
		if ((((*tpi)->dwAffectMask & 0x13) == 0x10) && ((*tpi)->dwAffected == Skill))
		{
			if (abs(FamilyMap[(*tpi)->dwFamily]) <= abs((*tpi)->fAdjustment))
				FamilyMap[(*tpi)->dwFamily] = (*tpi)->fAdjustment;
		}
	}

	float TPS = 0;

	if (SkillInfo[Skill].AttribA != eAttrNULL)
		TPS += CurStat[SkillInfo[Skill].AttribA - 1];

	if (SkillInfo[Skill].AttribB != eAttrNULL)
		TPS += CurStat[SkillInfo[Skill].AttribB - 1];

	TPS /= SkillInfo[Skill].Divider;

	TPS += SkillInc[Skill] + SkillBonus[Skill] + 0.5f;

	for (std::map<DWORD, float>::iterator tpf = FamilyMap.begin(); tpf != FamilyMap.end(); tpf++)
		TPS += (*tpf).second;

	CurSkill[Skill] = static_cast <DWORD> (TPS);
}

HRESULT cCharacterStats::get_TotalSpells(long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = SpellsLearned.size();
	return S_OK;
}

HRESULT cCharacterStats::get_SpellLearned(long SpellID, long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if( SpellID < 0 )
	{
		*pVal = -1;
		return E_FAIL;
	}

	std::set< DWORD >::iterator a = SpellsLearned.find( SpellID );
	if( a == SpellsLearned.end() )
	{
		*pVal = 0;
		return S_OK;
	}

	*pVal = 1;
	return S_OK;
}

HRESULT cCharacterStats::get_ClassTemplate(BSTR *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = T2BSTR(Class);
	return S_OK;
}

HRESULT cCharacterStats::get_Gender(BSTR *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = T2BSTR(Gender);
	return S_OK;
}

HRESULT cCharacterStats::get_Race(BSTR *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = T2BSTR(Race);
	return S_OK;
}

HRESULT cCharacterStats::get_Name(BSTR *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = T2BSTR(Name);
	return S_OK;
}

HRESULT cCharacterStats::get_Server(BSTR *pVal)
{
	if( !(*Server) )
		return E_FAIL;

	/*
	//DIE!!
	if( strncmp( Server, "DARKTIDE", 8 ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "FROSTFELL" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "HARVESTGAIN" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "LEAFCULL" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "MORNINGTHAW" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "SOLCLAIM" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "THISTLEDOWN" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "WINTERSEBB" ) == 0 )
		*pVal = T2BSTR( Server );
	else if( stricmp( Server, "VERDANTINE" ) == 0 )
		*pVal = T2BSTR( Server );
	else
		*pVal = T2BSTR("Unknown");
	*/
	*pVal = T2BSTR(Server);
	return S_OK;
}

HRESULT cCharacterStats::get_SkillPoints(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = SkillPoints;
	return S_OK;
}

HRESULT cCharacterStats::get_UnassignedExp(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = UnassignedXP;
	return S_OK;
}

HRESULT cCharacterStats::get_TotalExp(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = TotalXP;
	return S_OK;
}

HRESULT cCharacterStats::get_Rank(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Rank;
	return S_OK;
}

HRESULT cCharacterStats::get_Level(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Level;
	return S_OK;
}

HRESULT cCharacterStats::get_Character(/*[out, retval]*/ long *pVal)
{
	*pVal = GUID;
	return S_OK;
}

HRESULT cCharacterStats::get_VitalCount(/*[out, retval]*/ long *pVal)
{
	*pVal = 3;
	return S_OK;
}

HRESULT cCharacterStats::get_SkillCount(/*[out, retval]*/ long *pVal)
{
	*pVal = 40;
	return S_OK;
}

HRESULT cCharacterStats::get_AttributeCount(/*[out, retval]*/ long *pVal)
{
	*pVal = 6;
	return S_OK;
}

HRESULT cCharacterStats::get_Vital(enum eVitalID Index, /*[out, retval]*/ ISkillInfo ** pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 3))
		return E_FAIL;

	CComObject< ::cSkillInfo > *pSkill;
	CComObject< ::cSkillInfo >::CreateInstance( &pSkill );

	pSkill->m_pStats = this;

	if (Index == 1)
	{
		pSkill->m_pSkill->m_szName = "Health";
		pSkill->m_pSkill->m_nAttribute1 = eAttrEndurance;
		pSkill->m_pSkill->m_nAttribute2 = eAttrNULL;
		pSkill->m_pSkill->m_nDenominator = 2;
	}
	else if (Index == 2)
	{
		pSkill->m_pSkill->m_szName = "Stamina";
		pSkill->m_pSkill->m_nAttribute1 = eAttrEndurance;
		pSkill->m_pSkill->m_nAttribute2 = eAttrNULL;
		pSkill->m_pSkill->m_nDenominator = 1;
	}
	else if (Index == 3)
	{
		pSkill->m_pSkill->m_szName = "Mana";
		pSkill->m_pSkill->m_nAttribute1 = eAttrSelf;
		pSkill->m_pSkill->m_nAttribute2 = eAttrNULL;
		pSkill->m_pSkill->m_nDenominator = 1;
	}

	pSkill->m_pSkill->m_nExp = SecondaryStatArray[SecStatInc[Index - 1]];
	pSkill->m_pSkill->m_nBonus = 0;
	pSkill->m_pSkill->m_nOffset = SecStatInc[Index - 1];
	pSkill->m_pSkill->m_trained = eTrainTrained;

	return pSkill->QueryInterface( IID_ISkillInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_Skill(enum eSkillID Index, ISkillInfo ** pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 39))
		return E_FAIL;


	CComObject< ::cSkillInfo > *pSkill;
	CComObject< ::cSkillInfo >::CreateInstance( &pSkill );

	pSkill->m_pStats = this;

	pSkill->m_pSkill->m_nExp = SkillXP[Index];
	pSkill->m_pSkill->m_nBonus = SkillBonus[Index];
	pSkill->m_pSkill->m_nOffset = SkillInc[Index];
	
	pSkill->m_pSkill->m_nAttribute1 = SkillInfo[Index].AttribA;
	pSkill->m_pSkill->m_nAttribute2 = SkillInfo[Index].AttribB;
	pSkill->m_pSkill->m_nDenominator = SkillInfo[Index].Divider;
	pSkill->m_pSkill->m_szName = SkillInfo[Index].Name;
	pSkill->m_pSkill->m_trained = (enum eTrainingType) SkillTrain[Index];

	return pSkill->QueryInterface( IID_ISkillInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_Attribute(enum eAttributeID Index, /*[out, retval]*/ IAttributeInfo ** pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 6))
		return E_FAIL;

	CComObject< ::cAttributeInfo > *pAttrib;
	CComObject< ::cAttributeInfo >::CreateInstance( &pAttrib );
	
	pAttrib->m_pInfo->m_nBase = PrimStatInitial[Index - 1];
	pAttrib->m_pInfo->m_nCurrent = PrimStat[Index - 1];
	pAttrib->m_pInfo->m_nExp = PrimStatArray[PrimStat[Index - 1] - PrimStatInitial[Index - 1]];

	if (Index == 1)
		pAttrib->m_pInfo->m_szName = "Strength";
	else if (Index == 2)
		pAttrib->m_pInfo->m_szName = "Endurance";
	else if (Index == 3)
		pAttrib->m_pInfo->m_szName = "Quickness";
	else if (Index == 4)
		pAttrib->m_pInfo->m_szName = "Coordination";
	else if (Index == 5)
		pAttrib->m_pInfo->m_szName = "Focus";
	else if (Index == 6)
		pAttrib->m_pInfo->m_szName = "Self";

	return pAttrib->QueryInterface( IID_IAttributeInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_Birth(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Birth;
	return S_OK;
}

HRESULT cCharacterStats::get_Age(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Age;
	return S_OK;
}

HRESULT cCharacterStats::get_Deaths(/*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Deaths;
	return S_OK;
}

HRESULT cCharacterStats::get_Monarch(IAllegianceInfo ** pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	if (!Monarch.GUID)
		return E_FAIL;

	CComObject< ::cAllegianceInfo > *pAlleg;
	CComObject< ::cAllegianceInfo >::CreateInstance( &pAlleg );

	strncpy( pAlleg->m_pAllegiance->Name, Monarch.Name, sizeof( pAlleg->m_pAllegiance->Name ) );
	pAlleg->m_pAllegiance->GUID = Monarch.GUID;
	pAlleg->m_pAllegiance->TreeParent = Monarch.TreeParent;
	pAlleg->m_pAllegiance->Leadership = Monarch.Leadership;
	pAlleg->m_pAllegiance->Loyalty = Monarch.Loyalty;
	pAlleg->m_pAllegiance->XP = Monarch.XP;
	pAlleg->m_pAllegiance->Race = Monarch.Race;
	pAlleg->m_pAllegiance->Rank = Monarch.Rank;
	pAlleg->m_pAllegiance->Gender = Monarch.Gender;
	pAlleg->m_pAllegiance->Type = Monarch.Type;
	pAlleg->m_pAllegiance->Unknown = Monarch.Unknown;
	
	return pAlleg->QueryInterface( IID_IAllegianceInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_Patron(IAllegianceInfo ** pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	if (!Patron.GUID)
		return E_FAIL;

	CComObject< ::cAllegianceInfo > *pAlleg;
	CComObject< ::cAllegianceInfo >::CreateInstance( &pAlleg );

	strncpy( pAlleg->m_pAllegiance->Name, Patron.Name, sizeof( pAlleg->m_pAllegiance->Name ) );
	pAlleg->m_pAllegiance->GUID = Patron.GUID;
	pAlleg->m_pAllegiance->TreeParent = Patron.TreeParent;
	pAlleg->m_pAllegiance->Leadership = Patron.Leadership;
	pAlleg->m_pAllegiance->Loyalty = Patron.Loyalty;
	pAlleg->m_pAllegiance->XP = Patron.XP;
	pAlleg->m_pAllegiance->Race = Patron.Race;
	pAlleg->m_pAllegiance->Rank = Patron.Rank;
	pAlleg->m_pAllegiance->Gender = Patron.Gender;
	pAlleg->m_pAllegiance->Type = Patron.Type;
	pAlleg->m_pAllegiance->Unknown = Patron.Unknown;
	
	return pAlleg->QueryInterface( IID_IAllegianceInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_MyAllegiance(IAllegianceInfo ** pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	if (!MyAlleg.GUID)
		return E_FAIL;

	CComObject< ::cAllegianceInfo > *pAlleg;
	CComObject< ::cAllegianceInfo >::CreateInstance( &pAlleg );

	strncpy( pAlleg->m_pAllegiance->Name, MyAlleg.Name, sizeof( pAlleg->m_pAllegiance->Name ) );
	pAlleg->m_pAllegiance->GUID = MyAlleg.GUID;
	pAlleg->m_pAllegiance->TreeParent = MyAlleg.TreeParent;
	pAlleg->m_pAllegiance->Leadership = MyAlleg.Leadership;
	pAlleg->m_pAllegiance->Loyalty = MyAlleg.Loyalty;
	pAlleg->m_pAllegiance->XP = MyAlleg.XP;
	pAlleg->m_pAllegiance->Race = MyAlleg.Race;
	pAlleg->m_pAllegiance->Rank = MyAlleg.Rank;
	pAlleg->m_pAllegiance->Gender = MyAlleg.Gender;
	pAlleg->m_pAllegiance->Type = MyAlleg.Type;
	pAlleg->m_pAllegiance->Unknown = MyAlleg.Unknown;
	
	return pAlleg->QueryInterface( IID_IAllegianceInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_Vassal(long VassalNum, IAllegianceInfo ** pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	if ((VassalNum < 0) || (VassalNum >= VassalCount))
		return E_FAIL;

	if (!Vassals[ VassalNum ].GUID)
		return E_FAIL;

	CComObject< ::cAllegianceInfo > *pAlleg;
	CComObject< ::cAllegianceInfo >::CreateInstance( &pAlleg );

	strncpy( pAlleg->m_pAllegiance->Name, Vassals[ VassalNum ].Name, sizeof( pAlleg->m_pAllegiance->Name ) );
	pAlleg->m_pAllegiance->GUID = Vassals[ VassalNum ].GUID;
	pAlleg->m_pAllegiance->TreeParent = Vassals[ VassalNum ].TreeParent;
	pAlleg->m_pAllegiance->Leadership = Vassals[ VassalNum ].Leadership;
	pAlleg->m_pAllegiance->Loyalty = Vassals[ VassalNum ].Loyalty;
	pAlleg->m_pAllegiance->XP = Vassals[ VassalNum ].XP;
	pAlleg->m_pAllegiance->Race = Vassals[ VassalNum ].Race;
	pAlleg->m_pAllegiance->Rank = Vassals[ VassalNum ].Rank;
	pAlleg->m_pAllegiance->Gender = Vassals[ VassalNum ].Gender;
	pAlleg->m_pAllegiance->Type = Vassals[ VassalNum ].Type;
	pAlleg->m_pAllegiance->Unknown = Vassals[ VassalNum ].Unknown;
	
	return pAlleg->QueryInterface( IID_IAllegianceInfo, reinterpret_cast< void ** >( pVal ) );
}

HRESULT cCharacterStats::get_VassalCount(long *pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	*pVal = VassalCount;
	return S_OK;
}

HRESULT cCharacterStats::get_Followers(long *pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	*pVal = Followers;
	return S_OK;
}

HRESULT cCharacterStats::get_MonarchFollowers(long *pVal)
{
	if (!GotAlleg)
		return E_FAIL;

	*pVal = MonarchFollowers;
	return S_OK;
}

HRESULT cCharacterStats::get_EffectiveVital(enum eVitalID Index, /*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 3))
		return E_FAIL;

	float fSkill = SecStatInc[Index - 1];
	if (Index == 1) fSkill += CurStat[1] / 2.0f + 0.5f;
	else if (Index == 2) fSkill += CurStat[1];
	else if (Index == 3) fSkill += CurStat[5];

	long nBuffs = CurSecStat[Index - 1] - static_cast< long >(fSkill); //calculates net buffs

	*pVal = long(long(fSkill) * Vitae + 0.5f + nBuffs);

	return S_OK;
}

HRESULT cCharacterStats::get_EffectiveSkill(enum eSkillID Index, /*[out, retval]*/ long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 39))
		return E_FAIL;

	// get current skill
	float fSkill = 0.0;
	if( SkillInfo[Index].AttribA != eAttrNULL )
		fSkill += CurStat[ SkillInfo[Index].AttribA - 1];
	if( SkillInfo[Index].AttribB != eAttrNULL )
		fSkill += CurStat[ SkillInfo[Index].AttribB - 1];
	fSkill /= static_cast< float >( SkillInfo[Index].Divider );
	fSkill += SkillInc[Index] + 0.5f;
	fSkill += SkillBonus[Index];

	// get our net buffs
	long nBuffs = CurSkill[Index] - long(fSkill);

	// AC calculates the vitae penalty to a skill before buffs, so we do too
	*pVal = long(long(fSkill) * Vitae + 0.5f + nBuffs);

	return S_OK;
}

HRESULT cCharacterStats::get_EffectiveAttribute(enum eAttributeID Index, /*[out, retval]*/ long * pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if ((Index < 1) || (Index > 6))
		return E_FAIL;

	*pVal = CurStat[Index - 1];

	return S_OK;
}

HRESULT cCharacterStats::get_Health(long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Health;
	return S_OK;
}

HRESULT cCharacterStats::get_Stamina(long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Stamina;
	return S_OK;
}

HRESULT cCharacterStats::get_Mana(long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Mana;
	return S_OK;
}

HRESULT cCharacterStats::get_EnchantmentCount(long *pVal)
{
	if (!GotLogin)
		return E_FAIL;

	*pVal = Enchantments.size();
	return S_OK;
}

HRESULT cCharacterStats::get_Enchantment(long EnchantNum, IEnchantment **pVal)
{
	if (!GotLogin)
		return E_FAIL;

	if (EnchantNum < 0)
		return E_FAIL;

	std::list<cEnchantment *>::iterator tpi = Enchantments.begin();

	for( int i = 1; i < EnchantNum; i++ )
		tpi++;

	time_t CurrentTime = time(NULL);
	long SecondsLeft = (*tpi)->ExpireTime - CurrentTime;
	// Item based spells have no expiration.
	// Sometimes nearly expired spells may also return -1 due to internet latency.
	if( SecondsLeft < 0 )
		SecondsLeft = -1;

	CComObject< ::Enchantment > *pEnchant;
	CComObject< ::Enchantment >::CreateInstance( &pEnchant );

	pEnchant->put_SpellID( (*tpi)->iSpellID );
	pEnchant->put_Layer( (*tpi)->iLayer );
	pEnchant->put_TimeRemaining( SecondsLeft );
	pEnchant->put_Affected( (*tpi)->dwAffected );
	pEnchant->put_AffectedMask( (*tpi)->dwAffectMask );
	pEnchant->put_Family( (*tpi)->dwFamily );
	pEnchant->put_Adjustment( (*tpi)->fAdjustment );

	pEnchant->QueryInterface(pVal);
	return S_OK;
}

HRESULT cCharacterStats::get_Vitae(long *pVal)
{
	if(!GotLogin)
		return E_FAIL;

	*pVal = long((1.00 - Vitae) * 100 + 0.5f);
	return S_OK;
}

HRESULT cCharacterStats::get_BurdenUnits(long *pVal)
{
	if(!GotLogin)
		return E_FAIL;

	*pVal = long(TotalBurden);
	return S_OK;
}

HRESULT cCharacterStats::get_Burden(long *pVal)
{
	if(!GotLogin)
		return E_FAIL;

	long nLegalBurden;
	get_EffectiveAttribute(eAttrStrength, &nLegalBurden);
	nLegalBurden *= 150;

	*pVal = long( ( float(TotalBurden) / nLegalBurden ) * 100 );
	return S_OK;
}