//::////////////////////////////////////////////// //:: ;-. ,-. ,-. ,-. //:: | ) | ) / ( ) //:: |-' |-< | ;-: //:: | | \ \ ( ) //:: ' ' ' `-' `-' //::////////////////////////////////////////////// //:: /* Library for json related functions. */ //:: //::////////////////////////////////////////////// //:: Script: prc_inc_json.nss //:: Author: Jaysyn //:: Created: 2025-08-14 12:52:32 //::////////////////////////////////////////////// #include "nw_inc_gff" #include "inc_debug" //::---------------------------------------------| //:: Helper functions | //::---------------------------------------------| //:: Function to calculate the maximum possible hitpoints for oCreature int GetMaxPossibleHP(object oCreature) { int nMaxHP = 0; // Stores the total maximum hitpoints int i = 1; // Initialize position for class index int nConb = GetAbilityModifier(ABILITY_CONSTITUTION, oCreature); // Loop through each class position the creature may have, checking each class in turn while (TRUE) { // Get the class ID at position i int nClassID = GetClassByPosition(i, oCreature); // If class is invalid (no more classes to check), break out of loop if (nClassID == CLASS_TYPE_INVALID) break; // Get the number of levels in this class int nClassLevels = GetLevelByClass(nClassID, oCreature); // Get the row index of the class in classes.2da by using class ID as the row index int nHitDie = StringToInt(Get2DAString("classes", "HitDie", nClassID)); // Add maximum HP for this class (Hit Die * number of levels in this class) nMaxHP += nClassLevels * nHitDie; // Move to the next class position i++; } nMaxHP += nConb * GetHitDice(oCreature); return nMaxHP; } // Returns how many feats a creature should gain when its HD increases int CalculateFeatsFromHD(int nOriginalHD, int nNewHD) { // HD increase int nHDIncrease = nNewHD - nOriginalHD; if (nHDIncrease <= 0) return 0; // No new feats if HD did not increase // D&D 3E: 1 feat per 3 HD int nBonusFeats = nHDIncrease / 3; return nBonusFeats; } // Returns how many stat boosts a creature needs based on its HD int GetStatBoostsFromHD(int nCreatureHD, int nModiferCap) { // Make sure we don't get negative boosts int nBoosts = (40 - nCreatureHD) / 4; if (nBoosts < 0) { nBoosts = 0; } return nBoosts; } // Struct to hold size modifiers struct SizeModifiers { int strMod; int dexMod; int conMod; int naturalAC; int attackBonus; int dexSkillMod; }; //::---------------------------------------------| //:: JSON functions | //::---------------------------------------------| //:: Returns the integer value of a VarTable entry named sVarName, or 0 if not found. int json_GetLocalIntFromVarTable(json jCreature, string sVarName) { json jVarTable = GffGetList(jCreature, "VarTable"); if (jVarTable == JsonNull()) return 0; int nCount = JsonGetLength(jVarTable); int i; for (i = 0; i < nCount; i++) { json jEntry = JsonArrayGet(jVarTable, i); if (jEntry == JsonNull()) continue; // Get the Name field using GFF functions json jName = GffGetString(jEntry, "Name"); if (jName == JsonNull()) continue; string sName = JsonGetString(jName); if (sName == sVarName) { // Get the Type field to verify it's an integer json jType = GffGetDword(jEntry, "Type"); if (jType != JsonNull()) { int nType = JsonGetInt(jType); if (nType == 1) // Type 1 = integer { // Get the Value field using GFF functions json jValue = GffGetInt(jEntry, "Value"); if (jValue == JsonNull()) return 0; return JsonGetInt(jValue); } } } } return 0; } //:: Returns the total Hit Dice from a JSON creature GFF. int json_GetCreatureHD(json jGff) { int nHD = 0; json jClasses = GffGetList(jGff, "ClassList"); if (jClasses == JsonNull()) return 0; int nCount = JsonGetLength(jClasses); int i; for (i = 0; i < nCount; i = i + 1) { json jClass = JsonArrayGet(jClasses, i); if (jClass == JsonNull()) continue; json jLevel = GffGetShort(jClass, "ClassLevel"); // Use GffGetShort, not GffGetField if (jLevel != JsonNull()) { int nLevel = JsonGetInt(jLevel); nHD += nLevel; } } if (nHD <= 0) nHD = 1; return nHD; } //:: Reads ABILITY_TO_INCREASE from creature's VarTable and applies stat boosts based on increased HD json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifierCap) { if (jCreature == JsonNull()) return jCreature; // Get the ability to increase from VarTable int nAbilityToIncrease = json_GetLocalIntFromVarTable(jCreature, "ABILITY_TO_INCREASE"); if (nAbilityToIncrease < 0 || nAbilityToIncrease > 5) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Invalid ABILITY_TO_INCREASE value: " + IntToString(nAbilityToIncrease)); return jCreature; // Invalid ability index } // Calculate total current HD from ClassList json jClassList = GffGetList(jCreature, "ClassList"); if (jClassList == JsonNull()) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Failed to get ClassList"); return jCreature; } int nCurrentTotalHD = 0; int nClassCount = JsonGetLength(jClassList); int i; for (i = 0; i < nClassCount; i++) { json jClass = JsonArrayGet(jClassList, i); if (jClass != JsonNull()) { json jClassLevel = GffGetShort(jClass, "ClassLevel"); if (jClassLevel != JsonNull()) { nCurrentTotalHD += JsonGetInt(jClassLevel); } } } if (nCurrentTotalHD <= 0) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: No valid Hit Dice found"); return jCreature; } // Calculate stat boosts based on crossing level thresholds // Characters get stat boosts at levels 4, 8, 12, 16, 20, etc. int nOriginalBoosts = nOriginalHD / 4; // How many boosts they already had int nCurrentBoosts = nCurrentTotalHD / 4; // How many they should have now int nBoosts = nCurrentBoosts - nOriginalBoosts; // Additional boosts to apply if (nBoosts <= 0) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: No boosts needed (Original boosts: " + IntToString(nOriginalBoosts) + ", Current boosts: " + IntToString(nCurrentBoosts) + ")"); return jCreature; } if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Applying " + IntToString(nBoosts) + " boosts to ability " + IntToString(nAbilityToIncrease) + " for HD increase from " + IntToString(nOriginalHD) + " to " + IntToString(nCurrentTotalHD)); // Determine which ability to boost and apply the increases string sAbilityField; switch (nAbilityToIncrease) { case 0: sAbilityField = "Str"; break; case 1: sAbilityField = "Dex"; break; case 2: sAbilityField = "Con"; break; case 3: sAbilityField = "Int"; break; case 4: sAbilityField = "Wis"; break; case 5: sAbilityField = "Cha"; break; default: if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Unknown ability index: " + IntToString(nAbilityToIncrease)); return jCreature; } // Get current ability score json jCurrentAbility = GffGetByte(jCreature, sAbilityField); if (jCurrentAbility == JsonNull()) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Failed to get " + sAbilityField + " score"); return jCreature; } int nCurrentScore = JsonGetInt(jCurrentAbility); int nNewScore = nCurrentScore + nBoosts; // Clamp to valid byte range if (nNewScore < 1) nNewScore = 1; if (nNewScore > 255) nNewScore = 255; if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Increasing " + sAbilityField + " from " + IntToString(nCurrentScore) + " to " + IntToString(nNewScore)); // Apply the ability score increase jCreature = GffReplaceByte(jCreature, sAbilityField, nNewScore); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Failed to update " + sAbilityField); return JsonNull(); } if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Successfully applied ability boosts"); return jCreature; } //:: Adjust a skill by its ID (more efficient than name lookup) json json_AdjustCreatureSkillByID(json jCreature, int nSkillID, int nMod) { // Get the SkillList json jSkillList = GffGetList(jCreature, "SkillList"); if (jSkillList == JsonNull()) { if(DEBUG) DoDebug("json_AdjustCreatureSkillByID: Failed to get SkillList"); return jCreature; } // Check if we have enough skills in the list int nSkillCount = JsonGetLength(jSkillList); if (nSkillID >= nSkillCount) { if(DEBUG) DoDebug("json_AdjustCreatureSkillByID: Skill ID " + IntToString(nSkillID) + " exceeds skill list length " + IntToString(nSkillCount)); return jCreature; } // Get the skill struct at the correct index json jSkill = JsonArrayGet(jSkillList, nSkillID); if (jSkill == JsonNull()) { if(DEBUG) DoDebug("json_AdjustCreatureSkillByID: Failed to get skill at index " + IntToString(nSkillID)); return jCreature; } // Get current rank json jRank = GffGetByte(jSkill, "Rank"); if (jRank == JsonNull()) { if(DEBUG) DoDebug("json_AdjustCreatureSkillByID: Failed to get Rank for skill ID " + IntToString(nSkillID)); return jCreature; } int nCurrentRank = JsonGetInt(jRank); int nNewRank = nCurrentRank + nMod; // Clamp to valid range if (nNewRank < 0) nNewRank = 0; if (nNewRank > 255) nNewRank = 255; // Update the rank in the skill struct jSkill = GffReplaceByte(jSkill, "Rank", nNewRank); if (jSkill == JsonNull()) { if(DEBUG) DoDebug("json_AdjustCreatureSkillByID: Failed to replace Rank for skill ID " + IntToString(nSkillID)); return JsonNull(); } // Replace the skill in the array jSkillList = JsonArraySet(jSkillList, nSkillID, jSkill); // Replace the SkillList in the creature jCreature = GffReplaceList(jCreature, "SkillList", jSkillList); return jCreature; } //:: Reads FutureFeat1..FutureFeatN from the template's VarTable and appends them to FeatList if missing. json json_AddFeatsFromCreatureVars(json jCreature, int nOriginalHD) { if (jCreature == JsonNull()) return jCreature; // Calculate current total HD int nCurrentHD = json_GetCreatureHD(jCreature); int nAddedHD = nCurrentHD - nOriginalHD; if (nAddedHD <= 0) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: No additional HD to process (Current: " + IntToString(nCurrentHD) + ", Original: " + IntToString(nOriginalHD) + ")"); return jCreature; } // Calculate how many feats the creature should get based on added HD // Characters get a feat at levels 1, 3, 6, 9, 12, 15, 18, etc. // For added levels, we need to check what feat levels they cross int nOriginalFeats = (nOriginalHD + 2) / 3; // Feats from original HD int nCurrentFeats = (nCurrentHD + 2) / 3; // Feats from current HD int nNumFeats = nCurrentFeats - nOriginalFeats; // Additional feats earned if (nNumFeats <= 0) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: No additional feats earned from " + IntToString(nAddedHD) + " added HD"); return jCreature; } if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Processing " + IntToString(nNumFeats) + " feats for " + IntToString(nAddedHD) + " added HD (Original: " + IntToString(nOriginalHD) + ", Current: " + IntToString(nCurrentHD) + ")"); // Get or create FeatList json jFeatArray = GffGetList(jCreature, "FeatList"); if (jFeatArray == JsonNull()) jFeatArray = JsonArray(); int nOriginalFeatCount = JsonGetLength(jFeatArray); if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Original feat count: " + IntToString(nOriginalFeatCount)); int nAdded = 0; int i = 1; int nMaxIterations = 100; // Safety valve int nIterations = 0; while (nAdded < nNumFeats && nIterations < nMaxIterations) { nIterations++; string sVarName = "FutureFeat" + IntToString(i); if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Checking " + sVarName); int nFeat = json_GetLocalIntFromVarTable(jCreature, sVarName); if (nFeat <= 0) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: " + sVarName + " not found or invalid"); i++; continue; } if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Found " + sVarName + " = " + IntToString(nFeat)); // Check if feat already exists int bHasFeat = FALSE; int nFeatCount = JsonGetLength(jFeatArray); int j; for (j = 0; j < nFeatCount; j++) { json jFeatStruct = JsonArrayGet(jFeatArray, j); if (jFeatStruct != JsonNull()) { json jFeatValue = GffGetWord(jFeatStruct, "Feat"); if (jFeatValue != JsonNull() && JsonGetInt(jFeatValue) == nFeat) { bHasFeat = TRUE; if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Feat " + IntToString(nFeat) + " already exists"); break; } } } // Insert if missing if (!bHasFeat) { json jNewFeat = JsonObject(); jNewFeat = JsonObjectSet(jNewFeat, "__struct_id", JsonInt(1)); jNewFeat = GffAddWord(jNewFeat, "Feat", nFeat); if (jNewFeat == JsonNull()) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Failed to create feat struct for feat " + IntToString(nFeat)); break; } jFeatArray = JsonArrayInsert(jFeatArray, jNewFeat); nAdded++; if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Added feat " + IntToString(nFeat) + " (" + IntToString(nAdded) + "/" + IntToString(nNumFeats) + ")"); } i++; // Safety break if we've checked too many variables if (i > 100) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Safety break - checked too many FutureFeat variables"); break; } } if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Completed. Added " + IntToString(nAdded) + " feats in " + IntToString(nIterations) + " iterations"); // Save back the modified FeatList only if we added something if (nAdded > 0) { jCreature = GffReplaceList(jCreature, "FeatList", jFeatArray); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Failed to replace FeatList"); return JsonNull(); } } return jCreature; } //:: Get the size of a JSON array int json_GetArraySize(json jArray) { int iSize = 0; while (JsonArrayGet(jArray, iSize) != JsonNull()) { iSize++; } return iSize; } //:: Directly modifies oCreature's Base Natural AC if iNewAC is higher. //:: json json_UpdateBaseAC(json jCreature, int iNewAC) { //json jBaseAC = GffGetByte(jCreature, "Creature/value/NaturalAC/value"); json jBaseAC = GffGetByte(jCreature, "NaturalAC"); if (jBaseAC == JsonNull()) { return JsonNull(); } else if (JsonGetInt(jBaseAC) > iNewAC) { return jCreature; } else { jCreature = GffReplaceByte(jCreature, "NaturalAC", iNewAC); return jCreature; } } //:: Directly modifies jCreature's Challenge Rating. //:: This is useful for most XP calculations. json json_UpdateCR(json jCreature, int nBaseCR, int nCRMod) { int nNewCR; //:: Add CRMod to current CR nNewCR = nBaseCR + nCRMod; //:: Modify Challenge Rating jCreature = GffReplaceFloat(jCreature, "ChallengeRating", IntToFloat(nNewCR)); return jCreature; } //:: Directly modifies ability scores in a creature's JSON GFF. //:: json json_UpdateTemplateStats(json jCreature, int iModStr = 0, int iModDex = 0, int iModCon = 0, int iModInt = 0, int iModWis = 0, int iModCha = 0) { int iCurrent; // STR if (!GffGetFieldExists(jCreature, "Str", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Str", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Str")); jCreature = GffReplaceByte(jCreature, "Str", iCurrent + iModStr); // DEX if (!GffGetFieldExists(jCreature, "Dex", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Dex", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Dex")); jCreature = GffReplaceByte(jCreature, "Dex", iCurrent + iModDex); // CON if (!GffGetFieldExists(jCreature, "Con", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Con", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Con")); jCreature = GffReplaceByte(jCreature, "Con", iCurrent + iModCon); // INT if (!GffGetFieldExists(jCreature, "Int", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Int", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Int")); jCreature = GffReplaceByte(jCreature, "Int", iCurrent + iModInt); // WIS if (!GffGetFieldExists(jCreature, "Wis", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Wis", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Wis")); jCreature = GffReplaceByte(jCreature, "Wis", iCurrent + iModWis); // CHA if (!GffGetFieldExists(jCreature, "Cha", GFF_FIELD_TYPE_BYTE)) jCreature = GffAddByte(jCreature, "Cha", 10); iCurrent = JsonGetInt(GffGetByte(jCreature, "Cha")); jCreature = GffReplaceByte(jCreature, "Cha", iCurrent + iModCha); return jCreature; } //:: Directly modifies oCreature's ability scores. //:: json json_UpdateCreatureStats(json jCreature, object oBaseCreature, int iModStr = 0, int iModDex = 0, int iModCon = 0, int iModInt = 0, int iModWis = 0, int iModCha = 0) { //:: Retrieve and modify ability scores int iCurrentStr = GetAbilityScore(oBaseCreature, ABILITY_STRENGTH); int iCurrentDex = GetAbilityScore(oBaseCreature, ABILITY_DEXTERITY); int iCurrentCon = GetAbilityScore(oBaseCreature, ABILITY_CONSTITUTION); int iCurrentInt = GetAbilityScore(oBaseCreature, ABILITY_INTELLIGENCE); int iCurrentWis = GetAbilityScore(oBaseCreature, ABILITY_WISDOM); int iCurrentCha = GetAbilityScore(oBaseCreature, ABILITY_CHARISMA); jCreature = GffReplaceByte(jCreature, "Str", iCurrentStr + iModStr); jCreature = GffReplaceByte(jCreature, "Dex", iCurrentDex + iModDex); jCreature = GffReplaceByte(jCreature, "Con", iCurrentCon + iModCon); jCreature = GffReplaceByte(jCreature, "Int", iCurrentInt + iModInt); jCreature = GffReplaceByte(jCreature, "Wis", iCurrentWis + iModWis); jCreature = GffReplaceByte(jCreature, "Cha", iCurrentCha + iModCha); return jCreature; } //:: Increases a creature's Hit Dice in its JSON GFF data by nAmount json json_AddHitDice(json jCreature, int nAmount) { if (jCreature == JsonNull() || nAmount <= 0) return jCreature; // Get the ClassList json jClasses = GffGetList(jCreature, "ClassList"); if (jClasses == JsonNull() || JsonGetLength(jClasses) == 0) return jCreature; // Grab the first class entry json jFirstClass = JsonArrayGet(jClasses, 0); // Only touch ClassLevel; do NOT modify Class type json jCurrentLevel = GffGetShort(jFirstClass, "ClassLevel"); int nCurrentLevel = JsonGetInt(jCurrentLevel); int nNewLevel = nCurrentLevel + nAmount; // Replace ClassLevel only jFirstClass = GffReplaceShort(jFirstClass, "ClassLevel", nNewLevel); // Put modified class back into the array jClasses = JsonArraySet(jClasses, 0, jFirstClass); // Replace ClassList in the creature JSON jCreature = GffReplaceList(jCreature, "ClassList", jClasses); return jCreature; } //:: Adjusts a creature's size by nSizeChange (-4 to +4) and updates ability scores accordingly. json json_AdjustCreatureSize(json jCreature, int nSizeDelta) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Entering function. nSizeDelta=" + IntToString(nSizeDelta)); if (jCreature == JsonNull() || nSizeDelta == 0) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Exiting: jCreature is null or nSizeDelta is 0"); return jCreature; } // Get Appearance_Type using GFF functions json jAppearanceType = GffGetWord(jCreature, "Appearance_Type"); if (jAppearanceType == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed to get Appearance_Type"); return jCreature; } int nAppearance = JsonGetInt(jAppearanceType); int nCurrentSize = StringToInt(Get2DAString("appearances", "Size", nAppearance)); // Default to Medium (4) if invalid if (nCurrentSize < 0 || nCurrentSize > 8) nCurrentSize = 4; if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Appearance_Type =" + IntToString(nAppearance) + ", Size =" + IntToString(nCurrentSize)); int nSteps = nSizeDelta; // Calculate modifiers based on size change int strMod = nSteps * 4; int dexMod = nSteps * -1; int conMod = nSteps * 2; int naturalAC = nSteps * 1; int dexSkillMod = nSteps * -2; if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Applying stat modifiers: STR=" + IntToString(strMod) + " DEX=" + IntToString(dexMod) + " CON=" + IntToString(conMod)); // Update ability scores using GFF functions with error checking json jStr = GffGetByte(jCreature, "Str"); if (jStr != JsonNull()) { int nNewStr = JsonGetInt(jStr) + strMod; if (nNewStr < 1) nNewStr = 1; if (nNewStr > 255) nNewStr = 255; jCreature = GffReplaceByte(jCreature, "Str", nNewStr); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed to update Str"); return JsonNull(); } } json jDex = GffGetByte(jCreature, "Dex"); if (jDex != JsonNull()) { int nNewDex = JsonGetInt(jDex) + dexMod; if (nNewDex < 1) nNewDex = 1; if (nNewDex > 255) nNewDex = 255; jCreature = GffReplaceByte(jCreature, "Dex", nNewDex); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed to update Dex"); return JsonNull(); } } json jCon = GffGetByte(jCreature, "Con"); if (jCon != JsonNull()) { int nNewCon = JsonGetInt(jCon) + conMod; if (nNewCon < 1) nNewCon = 1; if (nNewCon > 255) nNewCon = 255; jCreature = GffReplaceByte(jCreature, "Con", nNewCon); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed to update Con"); return JsonNull(); } } // Update Natural AC json jNaturalAC = GffGetByte(jCreature, "NaturalAC"); if (jNaturalAC != JsonNull()) { int nCurrentNA = JsonGetInt(jNaturalAC); if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Current NaturalAC: " + IntToString(nCurrentNA)); int nNewNA = nCurrentNA + naturalAC; if (nNewNA < 0) nNewNA = 0; if (nNewNA > 255) nNewNA = 255; jCreature = GffReplaceByte(jCreature, "NaturalAC", nNewNA); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed to update NaturalAC"); return JsonNull(); } } // Adjust all Dexterity-based skills by finding them in skills.2da if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Adjusting DEX-based skills"); int nSkillID = 0; while (TRUE) { string sKeyAbility = Get2DAString("skills", "KeyAbility", nSkillID); // Break when we've reached the end of skills if (sKeyAbility == "") break; // If this skill uses Dexterity, adjust it if (sKeyAbility == "DEX") { string sSkillLabel = Get2DAString("skills", "Label", nSkillID); if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Adjusting DEX skill: " + sSkillLabel + " (ID: " + IntToString(nSkillID) + ")"); jCreature = json_AdjustCreatureSkillByID(jCreature, nSkillID, dexSkillMod); if (jCreature == JsonNull()) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Failed adjusting skill ID " + IntToString(nSkillID)); return JsonNull(); } } nSkillID++; } if(DEBUG) DoDebug("json_AdjustCreatureSize completed successfully"); return jCreature; } //:: Test void //:: void main (){}