Skip to content

Commit

Permalink
Creature: Add {Get|Set}MulticlassLimit (#1788)
Browse files Browse the repository at this point in the history
Co-authored-by: Daz <[email protected]>
  • Loading branch information
hendrikgit and Daztek authored Oct 22, 2024
1 parent 8c56c4e commit 57a3800
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ https://github.com/nwnxee/unified/compare/build8193.36.12...HEAD
- Player: ReloadColorPalettes()
- Race: SuppressCreatureRaceEffects()
- Race: ReactivateCreatureRaceEffects()
- Creature: {Get|Set}MulticlassLimit()

### Changed
- Player: added bChatWindow parameter to FloatingTextStringOnCreature()
Expand Down
64 changes: 64 additions & 0 deletions Plugins/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3584,3 +3584,67 @@ NWNX_EXPORT ArgumentStack GetMaxAttackRange(ArgumentStack&& args)

return 0.0f;
}

NWNX_EXPORT ArgumentStack GetMulticlassLimit(ArgumentStack&& args)
{
if (auto *pCreature = Utils::PopCreature(args))
{
return pCreature->nwnxGet<int32_t>("MULTICLASS_LIMIT").value_or(0);
}
return 0;
}

NWNX_EXPORT ArgumentStack SetMulticlassLimit(ArgumentStack&& args)
{
static Hooks::Hook s_GetIsClassAvailableHook = Hooks::HookFunction(&CNWSCreatureStats::GetIsClassAvailable,
+[](CNWSCreatureStats *pThis, uint8_t nClass) -> BOOL
{
if (pThis->m_bIsPC)
{
const auto limit = pThis->m_pBaseCreature->nwnxGet<int32_t>("MULTICLASS_LIMIT");
if (limit.has_value() && pThis->m_nNumMultiClasses >= limit.value())
{
for (int i = 0; i < pThis->m_nNumMultiClasses; ++i)
{
if (pThis->m_ClassInfo[i].m_nClass == nClass)
return s_GetIsClassAvailableHook->CallOriginal<BOOL>(pThis, nClass);
}

return 0;
}
}

return s_GetIsClassAvailableHook->CallOriginal<BOOL>(pThis, nClass);
}, Hooks::Order::Late);

if (auto *pCreature = Utils::PopCreature(args))
{
const auto limit = args.extract<int32_t>();
ASSERT_OR_THROW(limit >= 0);

const bool persist = !!args.extract<int32_t>();

if (!pCreature->m_bPlayerCharacter)
{
LOG_WARNING("SetMulticlassLimit: Only works on PCs");
return {};
}

if (limit >= Globals::Rules()->GetRulesetIntEntry(CRULES_HASHEDSTR("MULTICLASS_LIMIT"), 3))
{
LOG_WARNING("SetMulticlassLimit: Limit has to be lower than the server limit");
return {};
}

if (limit == 0)
{
pCreature->nwnxRemove("MULTICLASS_LIMIT");
}
else
{
pCreature->nwnxSet("MULTICLASS_LIMIT", limit, persist);
}
}

return {};
}
38 changes: 38 additions & 0 deletions Plugins/Creature/NWScript/nwnx_creature.nss
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,23 @@ void NWNX_Creature_SetAbilityIncreaseByLevel(object oCreature, int nLevel, int n
/// @return The maximum attack range for oCreature to oTarget
float NWNX_Creature_GetMaxAttackRange(object oCreature, object oTarget);

/// @brief Returns the creature's multiclass limit.
/// @note Only works on player characters.
/// @param oCreature The creature object. Has to be a player character.
/// @return The PCs multiclass limit. Returns 0 if no limit is set.
int NWNX_Creature_GetMulticlassLimit(object oCreature);

/// @brief Sets the creature's multiclass limit.
/// @note Only works on player characters and only for future level ups.
/// Classes already taken will continue to be available on level up.
/// The limit must be lower than the server limit set in ruleset.2da MULTICLASS_LIMIT.
/// Setting a value of 0 will remove the limit.
/// @param oCreature The creature object. Has to be a player character.
/// @param nLimit The multiclass limit.
/// @param bPersist Whether the limit should persist to the .bic file.
/// @note Persistence is enabled after a server reset by the first use of this function.
void NWNX_Creature_SetMulticlassLimit(object oCreature, int nLimit, int bPersist = FALSE);

/// @}

void NWNX_Creature_AddFeat(object creature, int feat)
Expand Down Expand Up @@ -2611,3 +2628,24 @@ float NWNX_Creature_GetMaxAttackRange(object oCreature, object oTarget)

return NWNX_GetReturnValueFloat();
}

int NWNX_Creature_GetMulticlassLimit(object oCreature)
{
string sFunc = "GetMulticlassLimit";

NWNX_PushArgumentObject(oCreature);
NWNX_CallFunction(NWNX_Creature, sFunc);

return NWNX_GetReturnValueInt();
}


void NWNX_Creature_SetMulticlassLimit(object oCreature, int nLimit, int bPersist = FALSE)
{
string sFunc = "SetMulticlassLimit";

NWNX_PushArgumentInt(bPersist);
NWNX_PushArgumentInt(nLimit);
NWNX_PushArgumentObject(oCreature);
NWNX_CallFunction(NWNX_Creature, sFunc);
}
6 changes: 5 additions & 1 deletion Plugins/Creature/NWScript/nwnx_creature_t.nss
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ void main()
NWNX_Creature_SetDomain(oCreature2, CLASS_TYPE_CLERIC, 2, (nDomain2+1)%5);
NWNX_Tests_Report("NWNX_Creature", "{S,G}etDomain", GetDomain(oCreature2, 1, CLASS_TYPE_CLERIC) == (nDomain2+1)%5);

//Test Armor class versus function, elf mage vs bandit
//Test Armor class versus function, elf mage vs bandit
NWNX_Tests_Report("NWNX_Creature", "Without a Versus GetArmorClassVersus", NWNX_Creature_GetArmorClassVersus(oCreature, OBJECT_INVALID) > 0);
int nOldAC = NWNX_Creature_GetArmorClassVersus(oCreature, oCreature2);
int nOldTouchAC = NWNX_Creature_GetArmorClassVersus(oCreature, oCreature2, TRUE);
Expand All @@ -275,5 +275,9 @@ void main()
NWNX_Creature_SetInitiativeModifier(oCreature, nInitiativeMod + 10);
NWNX_Tests_Report("NWNX_Creature", "{S,G}etInitiativeModifer", NWNX_Creature_GetInitiativeModifier(oCreature) == nInitiativeMod+10);

NWNX_Creature_SetMulticlassLimit(oCreature, 2);
NWNX_Tests_Report("NWNX_Creature", "{Set/Get}MulticlassLimit", NWNX_Creature_GetMulticlassLimit(oCreature) == 2);
NWNX_Creature_SetMulticlassLimit(oCreature, 0);

WriteTimestampedLogEntry("NWNX_Creature unit test end.");
}

0 comments on commit 57a3800

Please sign in to comment.