openDecal/Native/DecalFilters/CharacterStats.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:27:56 +01:00

1849 lines
No EOL
56 KiB
C++

// 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;
}