2039 lines
60 KiB
Plaintext
2039 lines
60 KiB
Plaintext
//
|
|
// NWSubraces
|
|
//
|
|
// Basic subrace functionality - private stuff
|
|
//
|
|
// (c) Shir'le E. Illios, 2002 (shirle@drowwanderer.com)
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
// **************************************************************
|
|
// ** Constants
|
|
// **********************
|
|
|
|
// Constant to note if we are currently debugging. Should be FALSE in release version.
|
|
int SUBRACE_DEBUG = FALSE;
|
|
|
|
// Constant to note if we're using the subrace hide or the subrace effects.
|
|
int USE_SUBRACE_HIDE = FALSE;
|
|
|
|
// Name of the subrace field on the characters.
|
|
string SUBRACE_FIELD = "SUBRACE_CHARACTER";
|
|
|
|
// Base field name used to save subrace information.
|
|
string SUBRACE_STRUCT_TAG = "SUBRACE";
|
|
|
|
// Field name for the number of stored subraces.
|
|
string SUBRACE_COUNT = "SUBRACE_COUNT";
|
|
|
|
// Base field name for the mapping of subrace ID to index.
|
|
string SUBRACE_IDMAP = "SUBRACE_IDMAP";
|
|
|
|
// Base field name for the mapping of subrace ID to description.
|
|
string SUBRACE_DESCMAP = "SUBRACE_DESCMAP";
|
|
|
|
// The field name for the area settings.
|
|
string AREA_SETTING = "AREA_SETTING";
|
|
|
|
// The Field name for the light setting on the character.
|
|
string LIGHT_SETTING = "LIGHT_SETTING";
|
|
|
|
// The Field name for the ground setting on the character.
|
|
string GROUND_SETTING = "GROUND_SETTING";
|
|
|
|
|
|
// **************************************************************
|
|
// ** Forward declarations
|
|
// **********************
|
|
|
|
// Private function for the subraces script. Do not use.
|
|
effect SEI_ParseTrait( object a_oCharacter, string a_sTrait );
|
|
|
|
// Private function for the subraces script. Do not use.
|
|
void SEI_DefineSubraces();
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Feedback functions
|
|
// **********************
|
|
|
|
// Write an entry for subraces in the log.
|
|
//
|
|
void SEI_WriteSubraceLogEntry( string a_sLogEntry, int a_bIsDebug = FALSE )
|
|
{
|
|
// If this is a normal log entry or if we are debugging.
|
|
if( !a_bIsDebug || SUBRACE_DEBUG )
|
|
{
|
|
// Write entry to log file.
|
|
WriteTimestampedLogEntry( "SEI Subraces: " + a_sLogEntry );
|
|
}
|
|
}
|
|
|
|
|
|
// Send a subraces message to a PC.
|
|
//
|
|
void SEI_SendSubraceMessageToPC( object a_oPC, string a_sMessage )
|
|
{
|
|
// First make sure we actually have a valid PC
|
|
if( GetIsPC( a_oPC ) )
|
|
{
|
|
SEI_WriteSubraceLogEntry( a_sMessage, TRUE );
|
|
SendMessageToPC( a_oPC, "[Subraces] " + a_sMessage );
|
|
}
|
|
}
|
|
|
|
|
|
// Translates the subrace enum to a readable string.
|
|
//
|
|
string SEI_SubraceEnumToString( int a_nSubrace )
|
|
{
|
|
|
|
if( a_nSubrace == SUBRACE_NONE )
|
|
{
|
|
return "subrace not initialized";
|
|
}
|
|
else if( a_nSubrace == SUBRACE_MONSTER )
|
|
{
|
|
return "Monster";
|
|
}
|
|
else
|
|
{
|
|
return GetLocalString( GetModule(), SUBRACE_DESCMAP + IntToString( a_nSubrace ) );
|
|
}
|
|
|
|
} // End SEI_SubraceEnumToString
|
|
|
|
|
|
// Send a message to the character that the subrace has been initialized.
|
|
//
|
|
void SEI_SendSubraceInitMsg( object a_oCharacter, int a_nSubrace )
|
|
{
|
|
|
|
// Send a message to the PC that the subrace was successfully initialized.
|
|
if( GetIsPC( a_oCharacter ) )
|
|
{
|
|
|
|
// Get the textual description of the subrace.
|
|
string sSubraceDesc = SEI_SubraceEnumToString( a_nSubrace );
|
|
|
|
// Write a debug message to the log.
|
|
SEI_WriteSubraceLogEntry(
|
|
"Subrace field of character '" + GetName( a_oCharacter ) +
|
|
"' (played by '" + GetPCPlayerName( a_oCharacter ) +
|
|
"') is '" + GetSubRace( a_oCharacter ) +
|
|
"', which has been interpreted as [" + IntToString( a_nSubrace ) +
|
|
"] '" + sSubraceDesc + "' and set accordingly." );
|
|
|
|
// Tell the player that the subrace was initialized.
|
|
SEI_SendSubraceMessageToPC( a_oCharacter,
|
|
"Your subrace field has been interpreted as '" + sSubraceDesc +
|
|
"' and your abilities have been set accordingly." );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_SendSubraceInitMsg
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace data functions
|
|
// **********************
|
|
|
|
// Returns a new subrace structure and saves it to the global object.
|
|
//
|
|
struct Subrace SEI_CreateSubrace( int a_nSubrace, int a_nBaseRace, string a_sDescription )
|
|
{
|
|
|
|
// The struct we're creating.
|
|
struct Subrace stSubrace;
|
|
|
|
// Global object to store the values on.
|
|
object oModule = GetModule();
|
|
|
|
// Number of subraces already added (highest index is nSubraceCount-1)
|
|
int nSubraceCount = GetLocalInt( oModule, SUBRACE_COUNT );
|
|
|
|
// Store the values in the new struct.
|
|
stSubrace.m_nID = a_nSubrace;
|
|
stSubrace.m_nBaseRace = a_nBaseRace;
|
|
stSubrace.m_nNumFieldValues = 0;
|
|
stSubrace.m_nNumTraits = 0;
|
|
stSubrace.m_bSpellResistance = 0;
|
|
stSubrace.m_nLightSensitivity = 0;
|
|
stSubrace.m_fLightBlindness = 0.0;
|
|
stSubrace.m_nStonecunning = 0;
|
|
stSubrace.m_nSpellLikeAbility = 0;
|
|
stSubrace.m_nECLAdd = 0;
|
|
stSubrace.m_nFavoredClassF = -1;
|
|
stSubrace.m_nFavoredClassM = -1;
|
|
stSubrace.m_bIsDefault = FALSE;
|
|
|
|
// Save the mapping of subrace ID to index.
|
|
SetLocalInt( oModule, SUBRACE_IDMAP + IntToString(a_nSubrace), nSubraceCount );
|
|
|
|
// Save the textual description of this subrace
|
|
// SEI_NOTE: Storing strings in structs doesn't seem to work. Hence done this way.
|
|
SetLocalString( GetModule(), SUBRACE_DESCMAP + IntToString( a_nSubrace ), a_sDescription );
|
|
|
|
// Increase the number of added subraces.
|
|
SetLocalInt( oModule, SUBRACE_COUNT, ++nSubraceCount );
|
|
|
|
return stSubrace;
|
|
|
|
} // End SEI_CreateSubrace
|
|
|
|
|
|
// Adds a new field text for the subrace.
|
|
// These field texts translate to matches in the subrace field on the character
|
|
// sheet; if the field contains one of these texts the subrace will match.
|
|
//
|
|
struct Subrace SEI_AddFieldText( struct Subrace a_stSubrace, string a_sText )
|
|
{
|
|
|
|
// Global object to store the values on.
|
|
object oModule = GetModule();
|
|
|
|
// The field name to store the new text under.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_stSubrace.m_nID) +
|
|
"_FIELD" + IntToString(a_stSubrace.m_nNumFieldValues);
|
|
|
|
// Store the text.
|
|
SetLocalString( oModule, sTag, a_sText );
|
|
|
|
// Increase the number of stored texts.
|
|
++(a_stSubrace.m_nNumFieldValues);
|
|
|
|
return a_stSubrace;
|
|
|
|
} // End SEI_AddFieldText
|
|
|
|
|
|
// Adds a trait for the subrace.
|
|
// These trait strings are parsed into the effects applied to the character.
|
|
//
|
|
struct Subrace SEI_AddTrait( struct Subrace a_stSubrace, string a_sTrait )
|
|
{
|
|
|
|
// Global object to store the values on.
|
|
object oModule = GetModule();
|
|
|
|
// The field name to store the new trait under.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_stSubrace.m_nID) +
|
|
"_TRAIT" + IntToString(a_stSubrace.m_nNumTraits);
|
|
|
|
// Store the text.
|
|
SetLocalString( oModule, sTag, a_sTrait );
|
|
|
|
// Increase the number of stored traits.
|
|
++(a_stSubrace.m_nNumTraits);
|
|
|
|
return a_stSubrace;
|
|
|
|
} // End SEI_AddTrait
|
|
|
|
|
|
// Returns the mapping of the subrace to the index where the subrace is stored.
|
|
//
|
|
int SEI_SubraceIDToIdx( int a_nSubrace )
|
|
{
|
|
return GetLocalInt( GetModule(), SUBRACE_IDMAP + IntToString(a_nSubrace) );
|
|
}
|
|
|
|
|
|
// Saves the subrace struct to the module for later retrieval to a specific index.
|
|
//
|
|
void SEI_SaveSubraceIdx( struct Subrace a_stSubrace, int a_nSubraceIdx )
|
|
{
|
|
|
|
// Global object to store the values on.
|
|
object oModule = GetModule();
|
|
|
|
// The base field name to store the values under.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_nSubraceIdx) + "_";
|
|
|
|
// Save the values from the struct.
|
|
SetLocalInt( oModule, sTag + "ID", a_stSubrace.m_nID );
|
|
SetLocalInt( oModule, sTag + "RACE", a_stSubrace.m_nBaseRace );
|
|
SetLocalInt( oModule, sTag + "NFLD", a_stSubrace.m_nNumFieldValues );
|
|
SetLocalInt( oModule, sTag + "NTRT", a_stSubrace.m_nNumTraits );
|
|
SetLocalInt( oModule, sTag + "SRES", a_stSubrace.m_bSpellResistance );
|
|
SetLocalInt( oModule, sTag + "LSEN", a_stSubrace.m_nLightSensitivity );
|
|
SetLocalFloat( oModule, sTag + "LBLI", a_stSubrace.m_fLightBlindness );
|
|
SetLocalInt( oModule, sTag + "STON", a_stSubrace.m_nStonecunning );
|
|
SetLocalInt( oModule, sTag + "SLA", a_stSubrace.m_nSpellLikeAbility );
|
|
SetLocalInt( oModule, sTag + "ECL", a_stSubrace.m_nECLAdd );
|
|
SetLocalInt( oModule, sTag + "FAVF", a_stSubrace.m_nFavoredClassF );
|
|
SetLocalInt( oModule, sTag + "FAVM", a_stSubrace.m_nFavoredClassM );
|
|
SetLocalInt( oModule, sTag + "DEF", a_stSubrace.m_bIsDefault );
|
|
|
|
} // End SEI_SaveSubraceIdx
|
|
|
|
|
|
// Loads the subrace struct from the module at a specific index.
|
|
//
|
|
struct Subrace SEI_LoadSubraceIdx( int a_nSubraceIdx )
|
|
{
|
|
|
|
// The structure that the data will be loaded into.
|
|
struct Subrace stSubrace;
|
|
|
|
// Global object where the values are stored.
|
|
object oModule = GetModule();
|
|
|
|
// The base field name the values are stored under.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_nSubraceIdx) + "_";
|
|
|
|
// Load the values into the struct.
|
|
stSubrace.m_nID = GetLocalInt( oModule, sTag + "ID" );
|
|
stSubrace.m_nBaseRace = GetLocalInt( oModule, sTag + "RACE" );
|
|
stSubrace.m_nNumFieldValues = GetLocalInt( oModule, sTag + "NFLD" );
|
|
stSubrace.m_nNumTraits = GetLocalInt( oModule, sTag + "NTRT" );
|
|
stSubrace.m_bSpellResistance = GetLocalInt( oModule, sTag + "SRES" );
|
|
stSubrace.m_nLightSensitivity = GetLocalInt( oModule, sTag + "LSEN" );
|
|
stSubrace.m_fLightBlindness = GetLocalFloat( oModule, sTag + "LBLI" );
|
|
stSubrace.m_nStonecunning = GetLocalInt( oModule, sTag + "STON" );
|
|
stSubrace.m_nSpellLikeAbility = GetLocalInt( oModule, sTag + "SLA" );
|
|
stSubrace.m_nECLAdd = GetLocalInt( oModule, sTag + "ECL" );
|
|
stSubrace.m_nFavoredClassF = GetLocalInt( oModule, sTag + "FAVF" );
|
|
stSubrace.m_nFavoredClassM = GetLocalInt( oModule, sTag + "FAVM" );
|
|
stSubrace.m_bIsDefault = GetLocalInt( oModule, sTag + "DEF" );
|
|
|
|
return stSubrace;
|
|
|
|
} // End SEI_LoadSubraceIdx
|
|
|
|
|
|
// Saves the subrace struct to the module for later retrieval.
|
|
//
|
|
void SEI_SaveSubrace( struct Subrace a_stSubrace )
|
|
{
|
|
SEI_SaveSubraceIdx( a_stSubrace, SEI_SubraceIDToIdx( a_stSubrace.m_nID ) );
|
|
}
|
|
|
|
|
|
// Loads the subrace struct from the module.
|
|
//
|
|
struct Subrace SEI_LoadSubrace( int a_nSubrace )
|
|
{
|
|
return SEI_LoadSubraceIdx( SEI_SubraceIDToIdx( a_nSubrace ) );
|
|
}
|
|
|
|
|
|
// Returns the ECL modification for the character.
|
|
//
|
|
int SEI_GetECLAdd( object a_oCharacter )
|
|
{
|
|
|
|
int nECLAdd = 0;
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Make sure we actually have a subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Load the information on the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( nSubrace );
|
|
|
|
// Get the ECL modification from the subrace information.
|
|
nECLAdd = stSubrace.m_nECLAdd;
|
|
|
|
} // End if
|
|
|
|
return nECLAdd;
|
|
|
|
} // End SEI_GetECLAdd
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace trait parser functions
|
|
// **********************
|
|
|
|
// Parse the trait string to get the effect it describes.
|
|
// This function recognises traits with three arguments.
|
|
//
|
|
effect SEI_ParseTrait3( object a_oCharacter, string a_sTrait, string a_sArg1, string a_sArg2, string a_sArg3 )
|
|
{
|
|
|
|
effect eResult;
|
|
|
|
if( a_sTrait == "save_inc" )
|
|
{
|
|
eResult = EffectSavingThrowIncrease( StringToInt(a_sArg1), StringToInt(a_sArg2), StringToInt(a_sArg3) );
|
|
}
|
|
else if( a_sTrait == "save_dec" )
|
|
{
|
|
eResult = EffectSavingThrowDecrease( StringToInt(a_sArg1), StringToInt(a_sArg2), StringToInt(a_sArg3) );
|
|
}
|
|
else
|
|
{
|
|
SEI_WriteSubraceLogEntry( "Unable to parse trait: '" +
|
|
a_sTrait + " " + a_sArg1 + " " + a_sArg2 + " " + a_sArg3 + "'" );
|
|
}
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_ParseTrait3
|
|
|
|
|
|
// Parse the trait string to get the effect it describes.
|
|
// This function recognises traits with two arguments.
|
|
//
|
|
effect SEI_ParseTrait2( object a_oCharacter, string a_sTrait, string a_sArg1, string a_sArg2 )
|
|
{
|
|
|
|
effect eResult;
|
|
|
|
if( a_sTrait == "ability_inc" )
|
|
{
|
|
eResult = EffectAbilityIncrease( StringToInt(a_sArg1), StringToInt(a_sArg2) );
|
|
}
|
|
else if( a_sTrait == "ability_dec" )
|
|
{
|
|
eResult = EffectAbilityDecrease( StringToInt(a_sArg1), StringToInt(a_sArg2) );
|
|
}
|
|
else if( a_sTrait == "skill_inc" )
|
|
{
|
|
eResult = EffectSkillIncrease( StringToInt(a_sArg1), StringToInt(a_sArg2) );
|
|
}
|
|
else if( a_sTrait == "skill_dec" )
|
|
{
|
|
eResult = EffectSkillDecrease( StringToInt(a_sArg1), StringToInt(a_sArg2) );
|
|
}
|
|
else if( a_sTrait == "vs" )
|
|
{
|
|
effect eEffect = SEI_ParseTrait( a_oCharacter, a_sArg2 );
|
|
eResult = VersusRacialTypeEffect( eEffect, StringToInt( a_sArg1 ) );
|
|
}
|
|
else
|
|
{
|
|
|
|
int nSplit = FindSubString( a_sArg2, " " );
|
|
|
|
if( nSplit >= 0 )
|
|
{
|
|
|
|
string sHead = GetStringLeft( a_sArg2, nSplit );
|
|
string sTail = GetStringRight( a_sArg2, GetStringLength(a_sArg2) - ( nSplit + 1 ) );
|
|
|
|
eResult = SEI_ParseTrait3( a_oCharacter, a_sTrait, a_sArg1, sHead, sTail );
|
|
|
|
}
|
|
else
|
|
{
|
|
SEI_WriteSubraceLogEntry( "Unable to parse trait: '" +
|
|
a_sTrait + " " + a_sArg1 + " " + a_sArg2 + "'" );
|
|
}
|
|
|
|
} // End if-elseif-else
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_ParseTrait2
|
|
|
|
|
|
// Parse the trait string to get the effect it describes.
|
|
// This function recognises traits with one argument.
|
|
//
|
|
effect SEI_ParseTrait1( object a_oCharacter, string a_sTrait, string a_sArg1 )
|
|
{
|
|
|
|
effect eResult;
|
|
|
|
if( a_sTrait == "ac_inc" )
|
|
{
|
|
eResult = EffectACIncrease( StringToInt(a_sArg1) );
|
|
}
|
|
else if( a_sTrait == "ac_dec" )
|
|
{
|
|
eResult = EffectACDecrease( StringToInt(a_sArg1) );
|
|
}
|
|
else if( a_sTrait == "attack_inc" )
|
|
{
|
|
eResult = EffectAttackIncrease( StringToInt(a_sArg1) );
|
|
}
|
|
else if( a_sTrait == "attack_dec" )
|
|
{
|
|
eResult = EffectAttackDecrease( StringToInt(a_sArg1) );
|
|
}
|
|
else if( a_sTrait == "immune" )
|
|
{
|
|
eResult = EffectImmunity( StringToInt(a_sArg1) );
|
|
}
|
|
else if( a_sTrait == "ex" )
|
|
{
|
|
effect eEffect = SEI_ParseTrait( a_oCharacter, a_sArg1 );
|
|
eResult = ExtraordinaryEffect( eEffect );
|
|
}
|
|
else if( a_sTrait == "su" )
|
|
{
|
|
effect eEffect = SEI_ParseTrait( a_oCharacter, a_sArg1 );
|
|
eResult = SupernaturalEffect( eEffect );
|
|
}
|
|
else if( a_sTrait == "m" )
|
|
{
|
|
if( GetGender( a_oCharacter ) == GENDER_MALE )
|
|
{
|
|
eResult = SEI_ParseTrait( a_oCharacter, a_sArg1 );
|
|
}
|
|
}
|
|
else if( a_sTrait == "f" )
|
|
{
|
|
if( GetGender( a_oCharacter ) == GENDER_FEMALE )
|
|
{
|
|
eResult = SEI_ParseTrait( a_oCharacter, a_sArg1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
int nSplit = FindSubString( a_sArg1, " " );
|
|
|
|
if( nSplit >= 0 )
|
|
{
|
|
|
|
string sHead = GetStringLeft( a_sArg1, nSplit );
|
|
string sTail = GetStringRight( a_sArg1, GetStringLength(a_sArg1) - ( nSplit + 1 ) );
|
|
|
|
eResult = SEI_ParseTrait2( a_oCharacter, a_sTrait, sHead, sTail );
|
|
|
|
}
|
|
else
|
|
{
|
|
SEI_WriteSubraceLogEntry( "Unable to parse trait: '" +
|
|
a_sTrait + " " + a_sArg1 + "'" );
|
|
}
|
|
|
|
} // End if-elseif-else
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_ParseTrait1
|
|
|
|
|
|
// Parse the trait string to get the effect it describes.
|
|
// This function recognises traits without arguments.
|
|
//
|
|
effect SEI_ParseTrait( object a_oCharacter, string a_sTrait )
|
|
{
|
|
|
|
effect eResult;
|
|
|
|
if( a_sTrait == "darkvision" )
|
|
{
|
|
eResult = EffectVisualEffect( VFX_DUR_DARKVISION );
|
|
}
|
|
else
|
|
{
|
|
|
|
int nSplit = FindSubString( a_sTrait, " " );
|
|
|
|
if( nSplit >= 0 )
|
|
{
|
|
|
|
string sHead = GetStringLeft( a_sTrait, nSplit );
|
|
string sTail = GetStringRight( a_sTrait, GetStringLength(a_sTrait) - ( nSplit + 1 ) );
|
|
|
|
eResult = SEI_ParseTrait1( a_oCharacter, sHead, sTail );
|
|
|
|
}
|
|
else
|
|
{
|
|
SEI_WriteSubraceLogEntry( "Unable to parse trait: '" + a_sTrait + "'" );
|
|
}
|
|
|
|
} // End if-else
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_ParseTrait
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace initialization functions
|
|
// **********************
|
|
|
|
// Initializes the available subraces and everything that is needed to properly
|
|
// run this script.
|
|
//
|
|
void SEI_InitSubraces()
|
|
{
|
|
|
|
struct Subrace stSubrace;
|
|
|
|
// First define an invalid subrace.
|
|
stSubrace = SEI_CreateSubrace( SUBRACE_NONE, RACIAL_TYPE_INVALID, "Invalid subrace" );
|
|
stSubrace.m_bIsDefault = TRUE;
|
|
SEI_SaveSubrace( stSubrace );
|
|
|
|
// Then define all the other subraces.
|
|
SEI_DefineSubraces();
|
|
|
|
} // End SEI_InitSubraces
|
|
|
|
|
|
// Sets the subrace field on a_oCharacter to a_nSubrace.
|
|
//
|
|
void SEI_SetSubraceVar( object a_oCharacter, int a_nSubrace, int a_bPrintToLog = TRUE )
|
|
{
|
|
|
|
if( a_oCharacter != OBJECT_INVALID )
|
|
{
|
|
|
|
SEI_WriteSubraceLogEntry(
|
|
"Setting subrace variable of character '" + GetName(a_oCharacter) +
|
|
"' (played by '" + GetPCPlayerName(a_oCharacter) +
|
|
"') to [" + IntToString(a_nSubrace) +
|
|
"] '" + SEI_SubraceEnumToString(a_nSubrace) + "'.", !a_bPrintToLog );
|
|
|
|
// Store the subrace on the character for easy reference later.
|
|
SetLocalInt( a_oCharacter, SUBRACE_FIELD, a_nSubrace );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_SetSubraceVar
|
|
|
|
|
|
// Test to see if the subrace field (and base race) match to this subrace.
|
|
//
|
|
int SEI_TestSubraceField( struct Subrace a_stSubrace, int a_nRace, string a_sSubraceField )
|
|
{
|
|
|
|
int nSubrace = SUBRACE_NONE;
|
|
|
|
// If subrace's base race matches the to be tested race.
|
|
if( a_stSubrace.m_nBaseRace == a_nRace )
|
|
{
|
|
|
|
// Global object whre the values are stored.
|
|
object oModule = GetModule();
|
|
|
|
// Basic field name of the stored field texts.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_stSubrace.m_nID) + "_FIELD";
|
|
|
|
// If this subrace is a default and no subrace was specified then use this subrace.
|
|
if( a_stSubrace.m_bIsDefault && ( GetStringLength( a_sSubraceField ) == 0 ) )
|
|
{
|
|
nSubrace = a_stSubrace.m_nID;
|
|
}
|
|
else
|
|
{
|
|
|
|
// For each text stored (and so long as we haven't found a match).
|
|
int nField;
|
|
for( nField = 0 ;
|
|
( nField < a_stSubrace.m_nNumFieldValues ) && ( nSubrace == SUBRACE_NONE ) ;
|
|
++nField )
|
|
{
|
|
// Get the field text and test for a match.
|
|
string sFieldText = GetLocalString( oModule, sTag + IntToString(nField) );
|
|
if( FindSubString( a_sSubraceField, sFieldText ) != -1 )
|
|
{
|
|
nSubrace = a_stSubrace.m_nID;
|
|
}
|
|
}
|
|
|
|
} // End if-else
|
|
|
|
} // End if
|
|
|
|
return nSubrace;
|
|
|
|
} // End SEI_TestSubraceField
|
|
|
|
|
|
// Interpret the subrace string and return the subrace enum.
|
|
//
|
|
int SEI_ReadSubraceField( int a_nRace, string a_sSubraceField )
|
|
{
|
|
|
|
int nSubrace = SUBRACE_NONE;
|
|
|
|
// Get the total number of added subraces.
|
|
int nNrSubraces = GetLocalInt( GetModule(), SUBRACE_COUNT );
|
|
|
|
// For each subrace stored (and so long as we haven't found a match).
|
|
int nIdx;
|
|
for( nIdx = 0 ; ( nIdx < nNrSubraces ) && ( nSubrace == SUBRACE_NONE ) ; ++nIdx )
|
|
{
|
|
// Load the subrace information and check to see if there's a match.
|
|
struct Subrace stSubrace = SEI_LoadSubraceIdx( nIdx );
|
|
nSubrace = SEI_TestSubraceField( stSubrace, a_nRace, a_sSubraceField );
|
|
}
|
|
|
|
return nSubrace;
|
|
|
|
} // End SEI_ReadSubraceField
|
|
|
|
|
|
// Start the subrace dialog for character a_oCharacter.
|
|
//
|
|
void SEI_StartSubraceDialog( object a_oCharacter )
|
|
{
|
|
if( a_oCharacter != OBJECT_INVALID )
|
|
{
|
|
AssignCommand( a_oCharacter, SetCommandable( TRUE, a_oCharacter ) );
|
|
AssignCommand( a_oCharacter, ClearAllActions() );
|
|
AssignCommand( a_oCharacter, ActionStartConversation( a_oCharacter, "sei_subraces", TRUE ) );
|
|
AssignCommand( a_oCharacter, ActionDoCommand( SetCommandable( FALSE, a_oCharacter ) ) );
|
|
}
|
|
}
|
|
|
|
|
|
// Reads the character's subrace field and sets the corresponding variable
|
|
// on the character. To do this it tries to interpret the subrace field on
|
|
// the character sheet.
|
|
//
|
|
int SEI_InitSubraceVar( object a_oCharacter )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Only initialize the subrace variable if we haven't done so already.
|
|
if( nSubrace == SUBRACE_NONE )
|
|
{
|
|
|
|
string sSubraceField = GetStringLowerCase( GetSubRace( a_oCharacter ) );
|
|
|
|
SEI_WriteSubraceLogEntry( "Subrace field of " + GetName( a_oCharacter ) +
|
|
" is '" + sSubraceField + "'", TRUE );
|
|
|
|
nSubrace = SEI_ReadSubraceField( GetRacialType( a_oCharacter ), sSubraceField );
|
|
|
|
SEI_WriteSubraceLogEntry( "Subrace of player " + GetName( a_oCharacter ) +
|
|
" interpreted as " + IntToString( nSubrace ) +
|
|
" (" + SEI_SubraceEnumToString( nSubrace ) + ")", TRUE );
|
|
|
|
// See if we have recognized the subrace or not.
|
|
if( nSubrace == SUBRACE_NONE )
|
|
{
|
|
if( GetIsPC( a_oCharacter ) )
|
|
{
|
|
// Ask the player for a subrace we understand.
|
|
SEI_StartSubraceDialog( a_oCharacter );
|
|
}
|
|
else
|
|
{
|
|
// Assume the character is a monster.
|
|
nSubrace = SUBRACE_MONSTER;
|
|
}
|
|
}
|
|
|
|
// Store the subrace on the character for easy reference later.
|
|
SEI_SetSubraceVar( a_oCharacter, nSubrace, FALSE );
|
|
|
|
} // End if
|
|
|
|
return nSubrace;
|
|
|
|
} // End SEI_InitSubraceVar
|
|
|
|
|
|
// Remove the subrace hide item of the character.
|
|
//
|
|
void SEI_RemoveSubraceHide( object a_oCharacter )
|
|
{
|
|
|
|
// Get the item stored in the creature hide slot.
|
|
object oHide = GetItemInSlot( INVENTORY_SLOT_CARMOUR, a_oCharacter );
|
|
|
|
if( GetIsObjectValid( oHide ) &&
|
|
( GetStringLeft( GetTag( oHide ), 11 ) == "sei_subrace" ) )
|
|
{
|
|
DestroyObject( oHide );
|
|
}
|
|
|
|
} // End SEI_RemoveSubraceHide
|
|
|
|
|
|
// Create the subrace hide item on the character.
|
|
//
|
|
void SEI_CreateSubraceHide( object a_oCharacter, string a_sHide )
|
|
{
|
|
|
|
object oHide = CreateItemOnObject( a_sHide, a_oCharacter );
|
|
|
|
if( oHide != OBJECT_INVALID )
|
|
{
|
|
//AssignCommand( a_oCharacter, ActionEquipItem( oHide, INVENTORY_SLOT_CARMOUR ) );
|
|
ActionEquipItem( oHide, INVENTORY_SLOT_CARMOUR );
|
|
}
|
|
|
|
} // End SEI_CreateSubraceHide
|
|
|
|
|
|
// Set the subrace hide item of the character to a_sHide.
|
|
//
|
|
void SEI_GiveSubraceHide( object a_oCharacter, string a_sHide )
|
|
{
|
|
|
|
object oHide = GetItemInSlot( INVENTORY_SLOT_CARMOUR, a_oCharacter );
|
|
|
|
string sHideTag = GetTag( oHide );
|
|
|
|
// If this item isn't already equiped.
|
|
if( sHideTag != a_sHide )
|
|
{
|
|
|
|
// Let the character stop doing whatever it is doing.
|
|
AssignCommand( a_oCharacter, ClearAllActions() );
|
|
|
|
// First remove whatever hide item is already set on the character.
|
|
AssignCommand( a_oCharacter, ActionDoCommand( SEI_RemoveSubraceHide( a_oCharacter ) ) );
|
|
|
|
// Create the subrace hide and equip it.
|
|
// SEI_NOTE: Assigned as an action to correctly follow the removal of the item.
|
|
AssignCommand( a_oCharacter, ActionDoCommand( SEI_CreateSubraceHide( a_oCharacter, a_sHide ) ) );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_GiveSubraceHide
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace area functions
|
|
// **********************
|
|
|
|
// Read a specific setting of the settings.
|
|
//
|
|
int SEI_ReadAreaSetting( int a_nSettings, int a_nUnit )
|
|
{
|
|
if( a_nUnit == 0 )
|
|
{
|
|
return AREA_NONE;
|
|
}
|
|
else
|
|
{
|
|
// SEI_NOTE: Making use of the integer-division rounding here.
|
|
return ( ( ( a_nSettings % ( 10 * a_nUnit ) ) / a_nUnit ) * a_nUnit );
|
|
}
|
|
}
|
|
|
|
|
|
// Get an area setting.
|
|
//
|
|
int SEI_GetAreaSetting( object a_oArea, int a_nSettings, int a_nUnit )
|
|
{
|
|
|
|
// Read the passed settings to get the specific setting.
|
|
int nResult = SEI_ReadAreaSetting( a_nSettings, a_nUnit );
|
|
|
|
// If we want to use the default for the area...
|
|
if( nResult <= a_nUnit )
|
|
{
|
|
|
|
// Read the area settings from the area.
|
|
int nAreaSetting = GetLocalInt( a_oArea, AREA_SETTING );
|
|
|
|
// Get the area setting.
|
|
nResult = SEI_ReadAreaSetting( nAreaSetting, a_nUnit );
|
|
|
|
// If the area uses the module default...
|
|
if( nResult <= a_nUnit )
|
|
{
|
|
|
|
// Get the module.
|
|
object oModule = GetModule();
|
|
|
|
// Read the default settings from the module.
|
|
nAreaSetting = GetLocalInt( oModule, AREA_SETTING );
|
|
|
|
// Get the default setting.
|
|
nResult = SEI_ReadAreaSetting( nAreaSetting, a_nUnit );
|
|
|
|
// We're already at the default, so if it's default again then nothing is set.
|
|
if( nResult <= a_nUnit )
|
|
{
|
|
nResult = AREA_NONE;
|
|
}
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
return nResult;
|
|
|
|
} // End SEI_GetAreaSetting
|
|
|
|
|
|
// Sets the area settings for the area.
|
|
// SEI_NOTE: I'd much rather use the toolset area settings, but I can't
|
|
// access them from script.
|
|
//
|
|
void SEI_SetAreaSettings( object a_oArea, int a_nSettings )
|
|
{
|
|
|
|
// If we have a valid object.
|
|
if( GetIsObjectValid( a_oArea ) )
|
|
{
|
|
|
|
// Get the current default settings.
|
|
int nDefaultSettings = GetLocalInt( a_oArea, AREA_SETTING );
|
|
|
|
// Make sure we're not going to set the same settings again.
|
|
if( nDefaultSettings != a_nSettings )
|
|
{
|
|
|
|
// Get the current default light setting.
|
|
int nDefaultLight =( nDefaultSettings % 10 );
|
|
|
|
// Get the current default underground setting.
|
|
int nDefaultGround = ( ( nDefaultSettings % 100 ) / 10 ) * 10;
|
|
|
|
// Get the rest of the current default settings.
|
|
int nDefaultRest =( nDefaultSettings / 100 ) * 100;
|
|
|
|
// Get the light setting.
|
|
int nSettingLight = ( a_nSettings % 10 );
|
|
|
|
// Get the underground setting.
|
|
int nSettingGround = ( ( a_nSettings % 100 ) / 10 ) * 10;
|
|
|
|
// If we're trying to set the light setting, then change it.
|
|
if( nSettingLight > 0 )
|
|
{
|
|
nDefaultLight = nSettingLight;
|
|
}
|
|
|
|
// If we're trying to set the underground setting, then change it.
|
|
if( nSettingGround > 0 )
|
|
{
|
|
nDefaultGround = nSettingGround;
|
|
}
|
|
|
|
// Combine the new settings to get the new default setting.
|
|
int nNewDefaultSettings = nDefaultRest + nDefaultGround + nDefaultLight;
|
|
|
|
// Set the new settings if they have changed.
|
|
if( nNewDefaultSettings != nDefaultSettings )
|
|
{
|
|
SetLocalInt( a_oArea, AREA_SETTING, nNewDefaultSettings );
|
|
}
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_SetAreaSettings
|
|
|
|
|
|
// Sets the default area settings for the module.
|
|
//
|
|
void SEI_SetDefaultAreaSettings( int a_nSettings )
|
|
{
|
|
|
|
// Get the module to store the defaults on.
|
|
object oModule = GetModule();
|
|
|
|
// Set the default settings on the module object.
|
|
// SEI_NOTE: This is a bit of a sneaky way of doing this, but since the
|
|
// SEI_SetAreaSettings functions doesn't use the fact that it is
|
|
// an area being passed to it, it works.
|
|
SEI_SetAreaSettings( oModule, a_nSettings );
|
|
|
|
} // End SEI_SetDefaultAreaSettings
|
|
|
|
|
|
// Remove the effects from the character if it isn't a subrace effect.
|
|
//
|
|
void SEI_RemoveEffect( object a_oCharacter, effect a_eEffect )
|
|
{
|
|
|
|
// Only remove effects not applied by the module.
|
|
if( ( GetEffectCreator( a_eEffect ) != GetModule() ) &&
|
|
( GetEffectSubType( a_eEffect ) != SUBTYPE_SUPERNATURAL ) )
|
|
{
|
|
RemoveEffect( a_oCharacter, a_eEffect );
|
|
}
|
|
|
|
} // End SEI_RemoveEffect
|
|
|
|
|
|
// Remove the effects from the character if it isn't a subrace effect.
|
|
//
|
|
void SEI_RemoveEffects( object a_oCharacter )
|
|
{
|
|
|
|
// Go through all effects on the character.
|
|
effect eEffect = GetFirstEffect( a_oCharacter );
|
|
while( GetIsEffectValid( eEffect ) )
|
|
{
|
|
|
|
RemoveEffect( a_oCharacter, eEffect );
|
|
|
|
eEffect = GetNextEffect( a_oCharacter );
|
|
|
|
} // End while
|
|
|
|
} // End SEI_RemoveEffects
|
|
|
|
|
|
// Remove the subrace effects from the character.
|
|
//
|
|
void SEI_RemoveSubraceEffects( object a_oCharacter, int a_bIncludePermanent = TRUE,
|
|
int a_iEffectType = EFFECT_TYPE_INVALIDEFFECT )
|
|
{
|
|
|
|
// Go through all effects on the character.
|
|
effect eEffect = GetFirstEffect( a_oCharacter );
|
|
while( GetIsEffectValid( eEffect ) )
|
|
{
|
|
|
|
// Only remove subrace effects.
|
|
if( ( GetEffectSubType( eEffect ) == SUBTYPE_SUPERNATURAL ) &&
|
|
( ( a_iEffectType == EFFECT_TYPE_INVALIDEFFECT ) ||
|
|
( GetEffectType( eEffect ) == a_iEffectType ) ) &&
|
|
( ( a_bIncludePermanent ) ||
|
|
( GetEffectCreator( eEffect ) != GetModule() ) ) )
|
|
{
|
|
RemoveEffect( a_oCharacter, eEffect );
|
|
}
|
|
|
|
eEffect = GetNextEffect( a_oCharacter );
|
|
|
|
} // End while
|
|
|
|
} // End SEI_RemoveSubraceEffects
|
|
|
|
|
|
void SEI_ApplyEffectToObject( int a_nDurationType, effect a_eEffect, object a_oTarget, float a_fDuration = 0.0, int a_bPermanent = FALSE )
|
|
{
|
|
|
|
// Make the effect supernatural so that it persists through resting.
|
|
AssignCommand( ( a_bPermanent ? GetModule() : GetArea( a_oTarget ) ),
|
|
ApplyEffectToObject( a_nDurationType, SupernaturalEffect( a_eEffect ), a_oTarget, a_fDuration ) );
|
|
|
|
} // End SEI_ApplyEffectToObject
|
|
|
|
|
|
// Create the effect simulating stonecunning.
|
|
//
|
|
effect SEI_EffectStonecunning()
|
|
{
|
|
|
|
// Stonecunning gives +2 on search checks while underground.
|
|
effect eResult = EffectSkillIncrease( SKILL_SEARCH, 2 );
|
|
|
|
// +2 to Hide checks while underground.
|
|
// SEI_NOTE: Included in Stonecunning to safe from adding yet another attribute.
|
|
eResult = EffectLinkEffects( eResult,
|
|
EffectSkillIncrease( SKILL_HIDE, 2 ) );
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_EffectStonecunning
|
|
|
|
|
|
// Create the effect simulating light sensitivity.
|
|
//
|
|
effect SEI_EffectLightSensitivity( int a_nSeverity )
|
|
{
|
|
|
|
// Decrease attack rolls.
|
|
effect eResult = EffectAttackDecrease( a_nSeverity );
|
|
|
|
// Decrease all saving throws.
|
|
eResult = EffectLinkEffects( eResult,
|
|
EffectSavingThrowDecrease( SAVING_THROW_ALL, a_nSeverity, SAVING_THROW_TYPE_ALL ) );
|
|
|
|
// Decrease all skill checks.
|
|
eResult = EffectLinkEffects( eResult,
|
|
EffectSkillDecrease( SKILL_ALL_SKILLS, a_nSeverity ) );
|
|
|
|
return eResult;
|
|
|
|
} // End SEI_EffectLightSensitivity
|
|
|
|
|
|
// Apply the area settings to the character.
|
|
//
|
|
void SEI_ApplyAreaSettings( object a_oCharacter, int a_nSettings )
|
|
{
|
|
|
|
// If we have something to work with.
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Make sure we actually have a subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Load the information on the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( nSubrace );
|
|
|
|
// Get the area the character is in.
|
|
object oArea = GetArea( a_oCharacter );
|
|
|
|
// If this subrace has stonecunning...
|
|
if( stSubrace.m_nStonecunning )
|
|
{
|
|
|
|
// Get the ground setting (underground/above ground).
|
|
int nGroundSetting = SEI_GetAreaSetting( oArea, a_nSettings, 10 );
|
|
|
|
// Get if the character thinks it's above or underground.
|
|
int nCharGround = GetLocalInt( a_oCharacter, GROUND_SETTING );
|
|
|
|
// If this area is underground...
|
|
if( nGroundSetting == AREA_UNDERGROUND )
|
|
{
|
|
|
|
// If the character just went underground.
|
|
if( nCharGround != AREA_UNDERGROUND )
|
|
{
|
|
|
|
// Apply the stonecunning effect to the character.
|
|
SEI_ApplyEffectToObject(
|
|
DURATION_TYPE_PERMANENT,
|
|
SEI_EffectStonecunning(),
|
|
a_oCharacter );
|
|
|
|
// Remember that the character is underground.
|
|
SetLocalInt( a_oCharacter, GROUND_SETTING, AREA_UNDERGROUND );
|
|
|
|
} // End if
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// If the character just came above ground.
|
|
if( nCharGround != AREA_ABOVEGROUND )
|
|
{
|
|
|
|
// Remove the stonecunning effect.
|
|
SEI_RemoveSubraceEffects( a_oCharacter, FALSE, EFFECT_TYPE_SKILL_INCREASE );
|
|
|
|
// Remember that the character is above ground.
|
|
SetLocalInt( a_oCharacter, GROUND_SETTING, AREA_ABOVEGROUND );
|
|
|
|
} // End if
|
|
|
|
} // End if-else
|
|
|
|
} // End if (Stonecunning)
|
|
|
|
// If this subrace is sensitive to light...
|
|
if( stSubrace.m_nLightSensitivity > 0 )
|
|
{
|
|
|
|
// Get the light setting.
|
|
int nLightSetting = SEI_GetAreaSetting( oArea, a_nSettings, 1 );
|
|
|
|
// Get the amount of light the character is in.
|
|
int nCharLight = GetLocalInt( a_oCharacter, LIGHT_SETTING );
|
|
|
|
// If the character is in a light area...
|
|
if( ( nLightSetting == AREA_LIGHT ) ||
|
|
( ( nLightSetting == AREA_SUN ) && ( GetIsDay() || GetIsDawn() ) ) )
|
|
{
|
|
|
|
// If the character just entered a light area.
|
|
if( nCharLight != AREA_LIGHT )
|
|
{
|
|
|
|
// Apply the light sensitivity effect to the character.
|
|
SEI_ApplyEffectToObject(
|
|
DURATION_TYPE_PERMANENT,
|
|
SEI_EffectLightSensitivity( stSubrace.m_nLightSensitivity ),
|
|
a_oCharacter );
|
|
|
|
// If the character is blinded by bright light...
|
|
if( stSubrace.m_fLightBlindness > 0.0 )
|
|
{
|
|
|
|
// Blind the character for the time specified.
|
|
SEI_ApplyEffectToObject(
|
|
DURATION_TYPE_TEMPORARY,
|
|
EffectBlindness(),
|
|
a_oCharacter,
|
|
stSubrace.m_fLightBlindness );
|
|
|
|
} // End if
|
|
|
|
// Remember that the character is in a light area.
|
|
SetLocalInt( a_oCharacter, LIGHT_SETTING, AREA_LIGHT );
|
|
|
|
} // End if
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// If the character just entered a dark area.
|
|
if( nCharLight != AREA_DARK )
|
|
{
|
|
|
|
// Remove the light sensitivity effect.
|
|
SEI_RemoveSubraceEffects( a_oCharacter, FALSE, EFFECT_TYPE_SKILL_DECREASE );
|
|
|
|
// Remember that the character is in a dark area.
|
|
SetLocalInt( a_oCharacter, LIGHT_SETTING, AREA_DARK );
|
|
|
|
} // End if
|
|
|
|
} // End if-else
|
|
|
|
} // End if (Light sensitivity)
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_ApplyAreaSettings
|
|
|
|
|
|
// Set spacial traits to a character when the characters enters an area.
|
|
//
|
|
void SEI_EnterArea( object a_oCharacter, int a_nSettings )
|
|
{
|
|
|
|
// If we have something to work with.
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
|
|
// Get the area the character is in.
|
|
object oArea = GetArea( a_oCharacter );
|
|
|
|
// Save the settings as the area settings.
|
|
SEI_SetAreaSettings( oArea, a_nSettings );
|
|
|
|
// Apply the settings to the character.
|
|
SEI_ApplyAreaSettings( a_oCharacter, a_nSettings );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_EnterArea
|
|
|
|
|
|
void SEI_AreaHeartbeat( object a_oArea )
|
|
{
|
|
|
|
// Get the area's light setting setting.
|
|
int nAreaLightSetting = SEI_ReadAreaSetting( GetLocalInt( a_oArea, AREA_SETTING ), 1 );
|
|
|
|
if( nAreaLightSetting == AREA_SUN )
|
|
{
|
|
|
|
// Is it currently light or dark?
|
|
int bIsLight = ( GetIsDay() || GetIsDawn() );
|
|
|
|
// What does the area think it is?
|
|
int nAreaLight = GetLocalInt( a_oArea, LIGHT_SETTING );
|
|
|
|
// Variable of whether the characters in the area need updating.
|
|
int bMustUpdate = FALSE;
|
|
|
|
// If it is light, but the area still thinks it's dark...
|
|
if( bIsLight && ( nAreaLight != AREA_LIGHT ) )
|
|
{
|
|
|
|
// We must update the characters in the area.
|
|
bMustUpdate = TRUE;
|
|
|
|
// Update the light setting of the area.
|
|
SetLocalInt( a_oArea, LIGHT_SETTING, AREA_LIGHT );
|
|
|
|
}
|
|
// If it is dark, but the area still thinks it's light...
|
|
else if( !bIsLight && ( nAreaLight != AREA_DARK ) )
|
|
{
|
|
|
|
// We must update the characters in the area.
|
|
bMustUpdate = TRUE;
|
|
|
|
// Update the light setting of the area.
|
|
SetLocalInt( a_oArea, LIGHT_SETTING, AREA_DARK );
|
|
|
|
} // End if-elseif
|
|
|
|
// If the characters in the area must be updated.
|
|
if( bMustUpdate )
|
|
{
|
|
|
|
// Go through all the PCs.
|
|
object oPC = GetFirstPC();
|
|
while( GetIsObjectValid( oPC ) )
|
|
{
|
|
|
|
// If the PC is in this area then update the character.
|
|
if( GetArea( oPC ) == a_oArea )
|
|
{
|
|
SEI_ApplyAreaSettings( oPC, 0 );
|
|
}
|
|
|
|
oPC = GetNextPC();
|
|
|
|
} // End while
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_AreaHeartbeat
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace initialization functions (cont.)
|
|
// **********************
|
|
|
|
// Set the character's correct hide item based on the subrace.
|
|
//
|
|
void SEI_SetSubraceHide( struct Subrace a_stSubrace, object a_oCharacter )
|
|
{
|
|
|
|
// If we're using the subrace hide instead of the subrace effects....
|
|
if( USE_SUBRACE_HIDE )
|
|
{
|
|
|
|
// Default subraces don't have any traits (past the default).
|
|
if( a_stSubrace.m_bIsDefault )
|
|
{
|
|
// Give the subrace hide item without any attributes.
|
|
SEI_GiveSubraceHide( a_oCharacter, "sei_subrace0" );
|
|
}
|
|
else
|
|
{
|
|
// Give the subrace hide item with the traits.
|
|
SEI_GiveSubraceHide( a_oCharacter, "sei_subrace_" + IntToString( a_stSubrace.m_nID ) );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
SEI_RemoveSubraceHide( a_oCharacter );
|
|
|
|
} // End if-else
|
|
|
|
} // End SEI_SetSubraceHide
|
|
|
|
|
|
// Give the spell-like abilities to the character depending on the subrace.
|
|
//
|
|
void SEI_GiveSpellLikeAbilities( struct Subrace a_stSubrace, object a_oCharacter )
|
|
{
|
|
|
|
// If this subrace has a spell-like ability then give it to them.
|
|
if( a_stSubrace.m_nSpellLikeAbility > 0 )
|
|
{
|
|
|
|
// Create the tag for the item.
|
|
string sItemTag = "sei_sla_" + IntToString( a_stSubrace.m_nSpellLikeAbility );
|
|
|
|
// Get an item with the tag (if the character has one).
|
|
object oSpellItem = GetItemPossessedBy( a_oCharacter, sItemTag );
|
|
|
|
// If the character doesn't have the item yet...
|
|
if( ! GetIsObjectValid( oSpellItem ) )
|
|
{
|
|
// Create a new spell-like abilities item on the character.
|
|
CreateItemOnObject( sItemTag, a_oCharacter );
|
|
}
|
|
|
|
} // End if
|
|
|
|
} // End SEI_GiveSpellLikeAbilities
|
|
|
|
|
|
// Apply the subrace traits for this subrace to the character.
|
|
//
|
|
void SEI_ApplySubraceTraits( struct Subrace a_stSubrace, object a_oCharacter )
|
|
{
|
|
|
|
effect eSubraceEffect;
|
|
|
|
int bFirstValidInited = FALSE;
|
|
|
|
// Only do this if we're not using the subrace hide item for this.
|
|
if( !USE_SUBRACE_HIDE )
|
|
{
|
|
|
|
// Global object where the values are stored.
|
|
object oModule = GetModule();
|
|
|
|
// Basic field name of the stored traits.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString(a_stSubrace.m_nID) + "_TRAIT";
|
|
|
|
// For each trait stored...
|
|
int nTrait;
|
|
for( nTrait = 0 ; nTrait < a_stSubrace.m_nNumTraits ; ++nTrait )
|
|
{
|
|
|
|
// Get the string describing this trait.
|
|
string sTraitString = GetLocalString( oModule, sTag + IntToString( nTrait ) );
|
|
|
|
if( bFirstValidInited )
|
|
{
|
|
|
|
// Add the effect to the chain of effects we already have.
|
|
eSubraceEffect = EffectLinkEffects( eSubraceEffect,
|
|
SEI_ParseTrait( a_oCharacter, sTraitString ) );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// This may be the first trait, so just parse the trait string.
|
|
eSubraceEffect = SEI_ParseTrait( a_oCharacter, sTraitString );
|
|
|
|
// If this is the first valid effect then note that so.
|
|
if( GetEffectType( eSubraceEffect ) != EFFECT_TYPE_INVALIDEFFECT )
|
|
{
|
|
bFirstValidInited = TRUE;
|
|
}
|
|
|
|
} // End if-else
|
|
|
|
} // End for
|
|
|
|
} // End if
|
|
|
|
// If this subrace has spell resistance, apply it based on character level.
|
|
if( a_stSubrace.m_bSpellResistance )
|
|
{
|
|
|
|
// Create the spell resistance effect.
|
|
effect eSREffect = EffectSpellResistanceIncrease( 11 + GetHitDice( a_oCharacter ) );
|
|
|
|
// Link it if we haven't linked anything yet.
|
|
if( bFirstValidInited )
|
|
{
|
|
eSubraceEffect = EffectLinkEffects( eSubraceEffect, eSREffect );
|
|
}
|
|
else
|
|
{
|
|
// This is the first valid effect.
|
|
eSubraceEffect = eSREffect;
|
|
bFirstValidInited = TRUE;
|
|
}
|
|
|
|
} // End if
|
|
|
|
// If we have something to apply, then apply it to the character.
|
|
if( bFirstValidInited )
|
|
{
|
|
SEI_ApplyEffectToObject( DURATION_TYPE_PERMANENT,
|
|
eSubraceEffect, a_oCharacter, 0.0, TRUE );
|
|
}
|
|
|
|
} // End SEI_ApplySubraceTraits
|
|
|
|
|
|
// Reads the character's subrace field and sets the corresponding traits
|
|
// on the character.
|
|
//
|
|
void SEI_InitSubraceTraits( object a_oCharacter, int a_bIncludeItems = TRUE )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Make sure we actually have a subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Load the information on the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( nSubrace );
|
|
|
|
// If we also want to include the items
|
|
if( a_bIncludeItems )
|
|
{
|
|
|
|
// Set the subrace hide item to te character.
|
|
SEI_SetSubraceHide( stSubrace, a_oCharacter );
|
|
|
|
// Give the spell-like abilities item.
|
|
SEI_GiveSpellLikeAbilities( stSubrace, a_oCharacter );
|
|
|
|
} // End if
|
|
|
|
// Apply the subrace's racial traits to the character.
|
|
SEI_ApplySubraceTraits( stSubrace, a_oCharacter );
|
|
|
|
// Apply effects based on the area settings if needed.
|
|
SEI_ApplyAreaSettings( a_oCharacter, 0 );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_InitSubraceTraits
|
|
|
|
|
|
// Remove all possible subrace stuff from the character.
|
|
//
|
|
void SEI_RemoveSubrace( object a_oCharacter, int a_bRemoveItems = TRUE )
|
|
{
|
|
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
|
|
if( a_bRemoveItems )
|
|
{
|
|
|
|
// Remove the subrace hide.
|
|
SEI_RemoveSubraceHide( a_oCharacter );
|
|
|
|
int nSpellItem;
|
|
for( nSpellItem = 1 ; nSpellItem <= 3 ; ++nSpellItem )
|
|
{
|
|
|
|
// Get the spell-like ability item.
|
|
object oSpellItem = GetItemPossessedBy( a_oCharacter, "sei_sla_" + IntToString( nSpellItem ) );
|
|
|
|
// If the character has the object, then destroy it.
|
|
if( GetIsObjectValid( oSpellItem ) )
|
|
{
|
|
DestroyObject( oSpellItem );
|
|
}
|
|
|
|
} // End for
|
|
|
|
} // End if
|
|
|
|
SEI_RemoveSubraceEffects( a_oCharacter );
|
|
|
|
} // End if
|
|
|
|
} // End SEI_RemoveSubrace
|
|
|
|
|
|
// Initializes the subrace for character a_oCharacter.
|
|
//
|
|
void SEI_InitSubrace( object a_oCharacter )
|
|
{
|
|
|
|
// Sanity check, make sure we have something to work with.
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
|
|
// Get the character's subrace.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
SEI_RemoveSubrace( a_oCharacter, FALSE );
|
|
|
|
// See if this character's subrace has been initialized before.
|
|
if( nSubrace == SUBRACE_NONE )
|
|
{
|
|
|
|
// Initialize the subrace variable on the character.
|
|
nSubrace = SEI_InitSubraceVar( a_oCharacter );
|
|
|
|
// See if we successfully initialized the character's subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Initialize the subrace traits on the character.
|
|
SEI_InitSubraceTraits( a_oCharacter );
|
|
|
|
// Send a message that the subrace has been inited.
|
|
SEI_SendSubraceInitMsg( a_oCharacter, nSubrace );
|
|
|
|
} // End if
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// Initialize the subrace traits on the character.
|
|
SEI_InitSubraceTraits( a_oCharacter );
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_InitSubrace
|
|
|
|
|
|
// Finishes the subrace dialog for the character, initializing the subrace.
|
|
//
|
|
void SEI_FinishSubraceDialog( object a_oCharacter )
|
|
{
|
|
|
|
SetCommandable( TRUE, a_oCharacter );
|
|
|
|
// Sanity check, make sure we have something to work with.
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
// Initialize the subrace for the effects, etc.
|
|
SEI_InitSubraceTraits( a_oCharacter );
|
|
SEI_SendSubraceInitMsg( a_oCharacter, GetLocalInt( a_oCharacter, SUBRACE_FIELD ) );
|
|
}
|
|
|
|
} // End SEI_FinishSubraceDialog
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace active functions
|
|
// **********************
|
|
|
|
// Modifies the character's subrace attributes on a character's level up.
|
|
//
|
|
void SEI_LevelUpSubrace( object a_oCharacter )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Make sure that we actually have a subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Load the information on the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( nSubrace );
|
|
|
|
// If this subrace has spell resistance then increase it.
|
|
if( stSubrace.m_bSpellResistance )
|
|
{
|
|
|
|
// Create the spell resistance effect (for being higher it overrides the old values).
|
|
effect eSREffect = EffectSpellResistanceIncrease( 11 + GetHitDice( a_oCharacter ) );
|
|
|
|
// Apply the effect to the character.
|
|
SEI_ApplyEffectToObject( DURATION_TYPE_INSTANT, eSREffect, a_oCharacter, 0.0, TRUE );
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_LevelUpSubrace
|
|
|
|
|
|
// Returns the subrace (enum) for the target.
|
|
//
|
|
int SEI_GetCharacterSubrace( object a_oCharacter )
|
|
{
|
|
|
|
int nSubrace = SUBRACE_NONE;
|
|
|
|
// Sanity check, make sure we have something to work with.
|
|
if( a_oCharacter != OBJECT_INVALID )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Write the findings into the log.
|
|
SEI_WriteSubraceLogEntry( "Subrace of player " + GetName( a_oCharacter ) +
|
|
" is " + IntToString( nSubrace ) +
|
|
" (" + SEI_SubraceEnumToString( nSubrace ) + ")", TRUE );
|
|
|
|
// If subrace not initialized yet.
|
|
if( nSubrace == SUBRACE_NONE )
|
|
{
|
|
SEI_InitSubrace( a_oCharacter );
|
|
nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
// Return the character's subrace.
|
|
return nSubrace;
|
|
|
|
} // End SEI_GetCharacterSubrace
|
|
|
|
|
|
// Returns whether the character is of subrace a_nSubrace.
|
|
//
|
|
int SEI_IsCharacterOfSubrace( object a_oCharacter, int a_nSubrace )
|
|
{
|
|
return SEI_GetCharacterSubrace( a_oCharacter ) == a_nSubrace;
|
|
}
|
|
|
|
|
|
// Returns the effective character level of the character.
|
|
//
|
|
int SEI_GetEffectiveCharacterLevel( object a_oCharacter )
|
|
{
|
|
return GetHitDice( a_oCharacter ) + SEI_GetECLAdd( a_oCharacter );
|
|
}
|
|
|
|
|
|
// Make sure that the character has the subrace items they're supposed to have.
|
|
//
|
|
void SEI_KeepSubraceItem( object a_oCharacter )
|
|
{
|
|
|
|
// Have we been given a valid character?
|
|
if( GetIsObjectValid( a_oCharacter ) )
|
|
{
|
|
|
|
// Read the local variable off the character.
|
|
int nSubrace = GetLocalInt( a_oCharacter, SUBRACE_FIELD );
|
|
|
|
// Make sure we actually have a subrace.
|
|
if( nSubrace != SUBRACE_NONE )
|
|
{
|
|
|
|
// Load the information on the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( nSubrace );
|
|
|
|
// Give the spell-like abilities item.
|
|
SEI_GiveSpellLikeAbilities( stSubrace, a_oCharacter );
|
|
|
|
} // End if
|
|
|
|
} // End if
|
|
|
|
} // End SEI_KeepSubraceItem
|
|
|
|
|
|
// Make sure that the subrace item is removed when someone loses it.
|
|
//
|
|
int SEI_DropSubraceItem( object a_oItem )
|
|
{
|
|
|
|
// If this is a spell-like ability item...
|
|
if( GetStringLeft( GetTag( a_oItem ), 8 ) == "sei_sla_" )
|
|
{
|
|
|
|
// Destroy the item.
|
|
DestroyObject( a_oItem );
|
|
|
|
// Return the we've recognized the item.
|
|
return TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// This is not a spell-like ability item,
|
|
// so return we haven't recognized it.
|
|
return FALSE;
|
|
|
|
} // End if-else
|
|
|
|
} // End SEI_DropSubraceItem
|
|
|
|
|
|
|
|
|
|
// **************************************************************
|
|
// ** Subrace experience functions
|
|
// **********************
|
|
|
|
// Finds the default subrace for the race.
|
|
//
|
|
int SEI_FindDefaultSubrace( int a_nRace )
|
|
{
|
|
|
|
// Global object where the values are stored.
|
|
object oModule = GetModule();
|
|
|
|
// Get the total number of added subraces.
|
|
int nNrSubraces = GetLocalInt( oModule, SUBRACE_COUNT );
|
|
|
|
// For all stored subraces...
|
|
int nIdx;
|
|
for( nIdx = 0 ; nIdx < nNrSubraces ; ++nIdx )
|
|
{
|
|
|
|
// The base field name the values are stored under.
|
|
string sTag = SUBRACE_STRUCT_TAG + IntToString( nIdx ) + "_";
|
|
|
|
// If this is the default subrace for the race...
|
|
if( ( GetLocalInt( oModule, sTag + "RACE" ) == a_nRace ) &&
|
|
GetLocalInt( oModule, sTag + "DEF" ) )
|
|
{
|
|
// Return the ID of the subrace.
|
|
return GetLocalInt( oModule, sTag + "ID" );
|
|
}
|
|
|
|
} // End for
|
|
|
|
// There is no default race for this subrace.
|
|
return SUBRACE_NONE;
|
|
|
|
} // End SEI_FindDefaultSubrace
|
|
|
|
|
|
// Returns the favored class for the subrace.
|
|
//
|
|
int SEI_GetFavoredClass( object a_oCharacter, struct Subrace a_stSubrace, int n_aHighestClass )
|
|
{
|
|
|
|
int nResult = CLASS_TYPE_INVALID;
|
|
|
|
int nClass = -1;
|
|
|
|
// Get te favored class depending on the gender of the character.
|
|
if( GetGender( a_oCharacter ) == GENDER_FEMALE )
|
|
{
|
|
nClass = a_stSubrace.m_nFavoredClassF;
|
|
}
|
|
else if( GetGender( a_oCharacter ) == GENDER_MALE )
|
|
{
|
|
nClass = a_stSubrace.m_nFavoredClassM;
|
|
}
|
|
|
|
// If we have a favored class...
|
|
if( nClass >= 0 )
|
|
{
|
|
|
|
// If no specific class is specified then take the highest class.
|
|
if( nClass == CLASS_TYPE_INVALID )
|
|
{
|
|
nResult = n_aHighestClass;
|
|
}
|
|
else
|
|
{
|
|
nResult = nClass;
|
|
}
|
|
|
|
} // End if
|
|
|
|
return nResult;
|
|
|
|
} // End SEI_GetFavoredClass
|
|
|
|
|
|
// Find the favored class for the subrace.
|
|
//
|
|
int SEI_FindFavoredClass( object a_oCharacter, struct Subrace a_stSubrace, int n_aHighestClass )
|
|
{
|
|
|
|
// Get the favored class for the subrace.
|
|
int nResult = SEI_GetFavoredClass( a_oCharacter, a_stSubrace, n_aHighestClass );
|
|
|
|
// If no favored class was specified...
|
|
if( nResult == CLASS_TYPE_INVALID )
|
|
{
|
|
|
|
// Get the favored class for the subrace's base race.
|
|
nResult = SEI_GetFavoredClass(
|
|
a_oCharacter,
|
|
SEI_LoadSubrace( SEI_FindDefaultSubrace( a_stSubrace.m_nBaseRace ) ),
|
|
n_aHighestClass );
|
|
|
|
} // End if
|
|
|
|
return nResult;
|
|
|
|
} // End SEI_FindFavoredClass
|
|
|
|
|
|
// Calculate the amount of experience the character of would get with these two classes.
|
|
// It is assumed that a_nHigh is the highest level in a class.
|
|
// It is assumed that the favored class is already dropped.
|
|
//
|
|
float SEI_CalcXPPenaltyForClasses( int a_nHigh, int a_nLow )
|
|
{
|
|
|
|
// Start without a penalty.
|
|
float fPenalty = 0.0;
|
|
|
|
// If the difference between the highest class and the other class is too
|
|
// high then increase the penalty.
|
|
if( ( a_nLow != 0 ) && ( ( a_nHigh - a_nLow ) > 1 ) )
|
|
{
|
|
fPenalty += 0.2;
|
|
}
|
|
|
|
return fPenalty;
|
|
|
|
} // End SEI_CalcXPPenaltyForClasses
|
|
|
|
|
|
// Calculate the amount of experience penalty the character will get.
|
|
// Sorts level according to highest level and drops the favored class.
|
|
//
|
|
float SEI_DropForFavoredClass( object a_oCharacter, struct Subrace a_stSubrace, int a_nClass1, int a_nLevel1, int a_nClass2, int a_nLevel2, int a_nClass3, int a_nLevel3 )
|
|
{
|
|
|
|
// Get the class with the most levels.
|
|
int nHighestClass = CLASS_TYPE_INVALID;
|
|
if( a_nLevel2 > a_nLevel1 )
|
|
{
|
|
nHighestClass = ( ( a_nLevel3 > a_nLevel2 ) ? a_nClass3 : a_nClass2 );
|
|
}
|
|
else
|
|
{
|
|
nHighestClass = ( ( a_nLevel3 > a_nLevel1 ) ? a_nClass3 : a_nClass1 );
|
|
}
|
|
|
|
// Find the favored class for this subrace.
|
|
int nFavoredClass = SEI_FindFavoredClass( a_oCharacter, a_stSubrace, nHighestClass );
|
|
|
|
// Ignore the level in the favored class.
|
|
int nLevel1 = ( ( a_nClass1 == nFavoredClass ) ? 0 : a_nLevel1 );
|
|
int nLevel2 = ( ( a_nClass2 == nFavoredClass ) ? 0 : a_nLevel2 );
|
|
int nLevel3 = ( ( a_nClass3 == nFavoredClass ) ? 0 : a_nLevel3 );
|
|
|
|
// Sort the first and the second level to highest.
|
|
int nTempHigh = ( ( nLevel1 > nLevel2 ) ? nLevel1 : nLevel2 );
|
|
int nLow1 = ( ( nLevel1 > nLevel2 ) ? nLevel2 : nLevel1 );
|
|
|
|
// Sort the third level and the current highest.
|
|
int nHigh = ( ( nLevel3 > nTempHigh ) ? nLevel3 : nTempHigh );
|
|
int nLow2 = ( ( nLevel3 > nTempHigh ) ? nTempHigh : nLevel3 );
|
|
|
|
// Start without a penalty.
|
|
float fPenalty = 0.0;
|
|
|
|
// Calculate the penalty for the two lower classes in relation to the higher one.
|
|
fPenalty += SEI_CalcXPPenaltyForClasses( nHigh, nLow1 );
|
|
fPenalty += SEI_CalcXPPenaltyForClasses( nHigh, nLow2 );
|
|
|
|
// Return the calculated penalty.
|
|
return fPenalty;
|
|
|
|
} // End SEI_DropForFavoredClass
|
|
|
|
|
|
// Modify the amount of experience for the character's favored class.
|
|
//
|
|
|
|
int SEI_ModifyXPForFavoredClass( object a_oCharacter, int a_nXP )
|
|
{
|
|
int nXP = a_nXP;
|
|
|
|
// Get the character's classes and levels individually.
|
|
int nClass1 = GetClassByPosition(1, a_oCharacter);
|
|
int nClass2 = GetClassByPosition(2, a_oCharacter);
|
|
int nClass3 = GetClassByPosition(3, a_oCharacter);
|
|
int nClass4 = GetClassByPosition(4, a_oCharacter);
|
|
int nClass5 = GetClassByPosition(5, a_oCharacter);
|
|
int nClass6 = GetClassByPosition(6, a_oCharacter);
|
|
int nClass7 = GetClassByPosition(7, a_oCharacter);
|
|
int nClass8 = GetClassByPosition(8, a_oCharacter);
|
|
|
|
int nLevel1 = GetLevelByClass(nClass1, a_oCharacter);
|
|
int nLevel2 = (nClass2 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass2, a_oCharacter) : 0;
|
|
int nLevel3 = (nClass3 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass3, a_oCharacter) : 0;
|
|
int nLevel4 = (nClass4 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass4, a_oCharacter) : 0;
|
|
int nLevel5 = (nClass5 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass5, a_oCharacter) : 0;
|
|
int nLevel6 = (nClass6 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass6, a_oCharacter) : 0;
|
|
int nLevel7 = (nClass7 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass7, a_oCharacter) : 0;
|
|
int nLevel8 = (nClass8 != CLASS_TYPE_INVALID) ? GetLevelByClass(nClass8, a_oCharacter) : 0;
|
|
|
|
// If character only has one class then this isn't an issue.
|
|
if (nClass2 != CLASS_TYPE_INVALID) {
|
|
// Load the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace(SEI_GetCharacterSubrace(a_oCharacter));
|
|
|
|
// Get the normal XP modification.
|
|
float fNormalMod = 1.0 - SEI_DropForFavoredClass(
|
|
a_oCharacter, SEI_LoadSubrace(SEI_FindDefaultSubrace(stSubrace.m_nBaseRace)),
|
|
nClass1, nLevel1, nClass2, nLevel2, nClass3, nLevel3,
|
|
nClass4, nLevel4, nClass5, nLevel5, nClass6, nLevel6,
|
|
nClass7, nLevel7, nClass8, nLevel8
|
|
);
|
|
|
|
// Get the XP modification for subrace.
|
|
float fSubraceMod = 1.0 - SEI_DropForFavoredClass(
|
|
a_oCharacter, stSubrace,
|
|
nClass1, nLevel1, nClass2, nLevel2, nClass3, nLevel3,
|
|
nClass4, nLevel4, nClass5, nLevel5, nClass6, nLevel6,
|
|
nClass7, nLevel7, nClass8, nLevel8
|
|
);
|
|
|
|
// Undo the normal penalty and set the new one.
|
|
// SEI_NOTE: No danger of div-0 as the max penalty is 40%.
|
|
nXP = FloatToInt(IntToFloat(nXP) * (fSubraceMod / fNormalMod));
|
|
}
|
|
|
|
return nXP;
|
|
}
|
|
|
|
|
|
/* int SEI_ModifyXPForFavoredClass( object a_oCharacter, int a_nXP )
|
|
{
|
|
|
|
int nXP = a_nXP;
|
|
|
|
// Get the three classes the character has.
|
|
int nClass1 = GetClassByPosition( 1, a_oCharacter );
|
|
int nClass2 = GetClassByPosition( 2, a_oCharacter );
|
|
int nClass3 = GetClassByPosition( 3, a_oCharacter );
|
|
|
|
// If character only has one class then this isn't an issue.
|
|
if( nClass2 != CLASS_TYPE_INVALID )
|
|
{
|
|
|
|
// Get the number of levels in the character's classes.
|
|
int nLevel1 = GetLevelByClass( nClass1, a_oCharacter );
|
|
int nLevel2 = GetLevelByClass( nClass2, a_oCharacter );
|
|
int nLevel3 = 0;
|
|
|
|
// If this character has three classes get the level in the third class.
|
|
if( nClass3 != CLASS_TYPE_INVALID )
|
|
{
|
|
nLevel3 = GetLevelByClass( nClass3, a_oCharacter );
|
|
}
|
|
|
|
// Load the character's subrace.
|
|
struct Subrace stSubrace = SEI_LoadSubrace( SEI_GetCharacterSubrace( a_oCharacter ) );
|
|
|
|
// Get the normal XP modification.
|
|
float fNormalMod = 1.0 - SEI_DropForFavoredClass(
|
|
a_oCharacter, SEI_LoadSubrace( SEI_FindDefaultSubrace( stSubrace.m_nBaseRace ) ),
|
|
nClass1, nLevel1, nClass2, nLevel2, nClass3, nLevel3 );
|
|
|
|
// Get the XP modification for subrace.
|
|
float fSubraceMod = 1.0 - SEI_DropForFavoredClass(
|
|
a_oCharacter, stSubrace, nClass1, nLevel1, nClass2, nLevel2, nClass3, nLevel3 );
|
|
|
|
// Undo the normal penalty and set the new one.
|
|
// SEI_NOTE: No danger of div-0 as the max penalty is 40%.
|
|
nXP = FloatToInt( IntToFloat( nXP ) * ( fSubraceMod / fNormalMod ) );
|
|
|
|
} // End if
|
|
|
|
return nXP;
|
|
|
|
} // End SEI_ModifyXPForFavoredClass
|
|
*/
|
|
|
|
// Modify the experience to take into acount any ECL modification.
|
|
//
|
|
int SEI_ModifyXPForECL( object a_oCharacter, int a_nXP )
|
|
{
|
|
|
|
// Get the level without ECL modification.
|
|
float fBaseLevel = IntToFloat( GetHitDice( a_oCharacter ) );
|
|
|
|
// Get the amount of ECL modification.
|
|
int nECLAdd = SEI_GetECLAdd( a_oCharacter );
|
|
|
|
// Calculate how much to modify the experience.
|
|
float fModify = ( fBaseLevel / ( fBaseLevel + nECLAdd ) );
|
|
|
|
// Modify the experience and return.
|
|
return FloatToInt( fModify * a_nXP );
|
|
|
|
} // End SEI_ModifyXPForECL
|
|
|
|
|
|
// Modify the experience the character would get to correct for subrace.
|
|
//
|
|
int SEI_ModifyXPForSubrace( object a_oCharacter, int a_nXP )
|
|
{
|
|
|
|
int nXP = a_nXP;
|
|
|
|
nXP = SEI_ModifyXPForFavoredClass( a_oCharacter, nXP );
|
|
|
|
nXP = SEI_ModifyXPForECL( a_oCharacter, nXP );
|
|
|
|
return nXP;
|
|
|
|
} // End SEI_ModifyXPForSubrace
|
|
|