Hrm. Well, there's lots of ways to skin that cat.
For some stuff, we have paragon creatures - scaled up versions of the normal creatures, which depend on player power - the number of times they've completed one of the end runs on the server, the Asmodeus fight. They operate by altering variables, which are then acted on, all in onspawn, by our ApplyLocals function, which applies effects, sets ab, and more. I'll put up the two relevant scripts here for your perusal:
Paragon ScalingLocalsMy most recent project has been a scaling of summons. Unlike the paragons, we're not scaling weapon damages on them, but everything else is subject to scaling, from ab to ac. This is especially ambitious, given that most of the 40 and under creatures don't use ApplyLocals normally, and have an altogether different scaling. Here's that (incomplete) code, which gets even more nitty gritty, adjusting ab and ac to exact parameters, so that we can scale a level 1 creature up to 60 and vice versa.
Summoning Spells scriptIt'll eventually fire for every summoning spell on the server, including the custom ones, though right now only summon creature 1-9 and the 40+ section of planar binding are done, and I'm still hammering on the scaling function to get it as close to accurate as feasible.
Those are just two examples of scaling, both relying on engine hacks to modify ability scores, ab, and more. You can do much simpler stuff, like this, something another of our devs, Werehound, hammered out as a sort of rough draft:
void ScaleSummon(object oMon, int nPower)
{
effect eScan;
for (eScan = GetFirstEffect(oMon); GetIsEffectValid(eScan); eScan = GetNextEffect(oMon))
if (GetEffectType(eScan) != EFFECT_TYPE_VISUALEFFECT)
RemoveEffect(oMon, eScan);
int i, nDigit0, nDigit1, nDigit2, nDigit3, nDigit4, nDigit5, nDigit6, nDigit7, nDigit8, nDigit9;
SetLocalInt(oMon, "AddAttacks", GetLocalInt(oMon, "AddAttacks") + nPower / 10);
SetLocalInt(oMon, "Conceal", GetLocalInt(oMon, "Conceal") + 2 * nPower);
if (GetLocalInt(oMon, "SR") > 0)
SetLocalInt(oMon, "SR", GetLocalInt(oMon, "SR") + nPower * 2);
if (GetLocalInt(oMon, "Regen") > 0)
SetLocalInt(oMon, "SR", GetLocalInt(oMon, "SR") + nPower * 5);
SetLocalInt(oMon, "DeflectionAC", GetLocalInt(oMon, "DeflectionAC") + nPower/5);
SetLocalInt(oMon, "DodgeAC", GetLocalInt(oMon, "DodgeAC") + nPower/5);
SetLocalInt(oMon, "ArmorAC", GetLocalInt(oMon, "ArmorAC") + nPower/5);
SetLocalInt(oMon, "ShieldAC", GetLocalInt(oMon, "ShieldAC") + nPower/5);
SetLocalInt(oMon, "NaturalAC", GetLocalInt(oMon, "NaturalAC") + nPower/5);
int nRes = GetLocalInt(oMon, "PhysImmunities");
if (nRes) {
nDigit0 = (nRes % 10);
nDigit1 = (nRes /= 10) % 10 + nPower/5;
nDigit2 = (nRes /= 10) % 10 + nPower/5;
nDigit3 = (nRes /= 10) % 10 + nPower/5;
nDigit4 = (nRes /= 10) % 10 + nPower/5;
nDigit5 = (nRes /= 10) % 10 + nPower/5;
nDigit6 = (nRes /= 10) % 10 + nPower/5;
nDigit7 = (nRes /= 10) % 10 + nPower/5;
nDigit8 = (nRes /= 10) % 10 + nPower/5;
nDigit9 = (nRes /= 10) % 10 + nPower/5;
}
nRes = (nRes > 9 ? 9 : nRes < 0 ? 0 : nRes);
nRes = nRes * 10 + (nDigit8 > 9 ? 9 : nDigit8 < 0 ? 0 : nDigit8);
nRes = nRes * 10 + (nDigit7 > 9 ? 9 : nDigit7 < 0 ? 0 : nDigit7);
nRes = nRes * 10 + (nDigit6 > 9 ? 9 : nDigit6 < 0 ? 0 : nDigit6);
nRes = nRes * 10 + (nDigit5 > 9 ? 9 : nDigit5 < 0 ? 0 : nDigit5);
nRes = nRes * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nRes = nRes * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nRes = nRes * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nRes = nRes * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nRes = nRes * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "PhysImmunities", nRes);
// Modification of abilities handles additional AB, saves, AC
int nAbility = GetLocalInt(oMon, "Ability");
nDigit0 = (nAbility % 10) + nPower/2;
nDigit1 = (nAbility /= 10) % 10 + nPower/2;
nDigit2 = (nAbility /= 10) % 10 + nPower/2;
nDigit3 = (nAbility /= 10) % 10 + nPower/2;
nDigit4 = (nAbility /= 10) % 10 + nPower/2;
nDigit5 = (nAbility /= 10) % 10 + nPower/2;
if (nDigit5 < 0) ModifyAbilityScore(oMon, 0, nDigit5);
if (nDigit4 < 0) ModifyAbilityScore(oMon, 1, nDigit4);
if (nDigit3 < 0) ModifyAbilityScore(oMon, 2, nDigit3);
if (nDigit2 < 0) ModifyAbilityScore(oMon, 3, nDigit2);
if (nDigit1 < 0) ModifyAbilityScore(oMon, 4, nDigit1);
if (nDigit0 < 0) ModifyAbilityScore(oMon, 5, nDigit0);
if (nAbility > 9)
nAbility = nAbility * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nAbility = nAbility * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nAbility = nAbility * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nAbility = nAbility * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nAbility = nAbility * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "Ability", nAbility);
int nSkill = GetLocalInt(oMon, "Skill");
if (nSkill) {
nDigit0 = (nSkill % 10) + nPower * 3 / 2;
nDigit1 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit2 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit3 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit4 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit5 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit6 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit7 = (nSkill /= 10) % 10 + nPower * 3 / 2;
nDigit8 = (nSkill /= 10) % 10 + nPower * 3 / 2;
}
nSkill = (nSkill > 9 ? 9 : nSkill < 0 ? 0 : nSkill);
nSkill = nSkill * 10 + (nDigit7 > 9 ? 9 : nDigit7 < 0 ? 0 : nDigit7);
nSkill = nSkill * 10 + (nDigit6 > 9 ? 9 : nDigit6 < 0 ? 0 : nDigit6);
nSkill = nSkill * 10 + (nDigit5 > 9 ? 9 : nDigit5 < 0 ? 0 : nDigit5);
nSkill = nSkill * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nSkill = nSkill * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nSkill = nSkill * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nSkill = nSkill * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nSkill = nSkill * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "Skill", nSkill);
string sSoak = GetLocalString(oMon, "Soak"), sNewSoak = "";
if (sSoak != "") {
struct SubString ss, spss;
int nSoak, nPlus;
ss.rest = sSoak;
while (ss.rest != "") {
ss = GetFirstSubString(ss.rest, " ");
if (ss.first != "0+0" && ss.first != "0") {
spss = GetFirstSubString(ss.first, "+");
nSoak = StringToInt(spss.first) + nPower*2;
nPlus = (nPlus + StringToInt(spss.rest) + nPower/5 > 6 ? nPlus + StringToInt(spss.rest) + nPower/5 : nPlus + StringToInt(spss.rest) + nPower/5 + 1);
if (nSoak < 0) nSoak = 0;
if (nPlus < 0) nPlus = 0;
}
sNewSoak += IntToString(nSoak) + "+" + IntToString(nPlus) + " ";
}
}
SetLocalString(oMon, "Soak", sNewSoak);
nRes = GetLocalInt(oMon, "EnergyImmunities");
if (nRes) {
nDigit0 = (nRes % 10);
nDigit1 = (nRes /= 10) % 10 + nPower/8;
nDigit2 = (nRes /= 10) % 10 + nPower/8;
nDigit3 = (nRes /= 10) % 10 + nPower/8;
nDigit4 = (nRes /= 10) % 10 + nPower/8;
nDigit5 = (nRes /= 10) % 10 + nPower/8;
nDigit6 = (nRes /= 10) % 10 + nPower/8;
nDigit7 = (nRes /= 10) % 10 + nPower/8;
nDigit8 = (nRes /= 10) % 10 + nPower/8;
nDigit9 = (nRes /= 10) % 10 + nPower/8;
nRes = (nRes > 9 ? 9 : nRes < 0 ? 0 : nRes);
nRes = nRes * 10 + (nDigit8 > 9 ? 9 : nDigit8 < 0 ? 0 : nDigit8);
nRes = nRes * 10 + (nDigit7 > 9 ? 9 : nDigit7 < 0 ? 0 : nDigit7);
nRes = nRes * 10 + (nDigit6 > 9 ? 9 : nDigit6 < 0 ? 0 : nDigit6);
nRes = nRes * 10 + (nDigit5 > 9 ? 9 : nDigit5 < 0 ? 0 : nDigit5);
nRes = nRes * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nRes = nRes * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nRes = nRes * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nRes = nRes * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nRes = nRes * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "EnergyImmunities", nRes);
}
nRes = GetLocalInt(oMon, "EnergyVulnerabilities");
if (nRes || nPower < 0) {
nDigit0 = (nRes % 10);
nDigit1 = (nRes /= 10) % 10 + -nPower/8;
nDigit2 = (nRes /= 10) % 10 + -nPower/8;
nDigit3 = (nRes /= 10) % 10 + -nPower/8;
nDigit4 = (nRes /= 10) % 10 + -nPower/8;
nDigit5 = (nRes /= 10) % 10 + -nPower/8;
nDigit6 = (nRes /= 10) % 10 + -nPower/8;
nDigit7 = (nRes /= 10) % 10 + -nPower/8;
nDigit8 = (nRes /= 10) % 10 + -nPower/8;
nDigit9 = (nRes /= 10) % 10 + -nPower/8;
nRes = (nRes > 9 ? 9 : nRes < 0 ? 0 : nRes);
nRes = nRes * 10 + (nDigit7 > 9 ? 9 : nDigit7 < 0 ? 0 : nDigit7);
nRes = nRes * 10 + (nDigit6 > 9 ? 9 : nDigit6 < 0 ? 0 : nDigit6);
nRes = nRes * 10 + (nDigit5 > 9 ? 9 : nDigit5 < 0 ? 0 : nDigit5);
nRes = nRes * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nRes = nRes * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nRes = nRes * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nRes = nRes * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nRes = nRes * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "EnergyVulnerabilities", nRes);
}
nRes = GetLocalInt(oMon, "EnergyResistance");
if (nRes) {
nDigit0 = (nRes % 10) + nPower / 5;
nDigit1 = (nRes /= 10) % 10 + nPower / 5;
nDigit2 = (nRes /= 10) % 10 + nPower / 5;
nDigit3 = (nRes /= 10) % 10 + nPower / 5;
nDigit4 = (nRes /= 10) % 10 + nPower / 5;
nDigit5 = (nRes /= 10) % 10 + nPower / 5;
nDigit6 = (nRes /= 10) % 10 + nPower / 5;
nDigit7 = (nRes /= 10) % 10 + nPower / 5;
nDigit8 = (nRes /= 10) % 10 + nPower / 5;
nRes = (nRes > 9 ? 9 : nRes < 0 ? 0 : nRes);
nRes = nRes * 10 + (nDigit7 > 9 ? 9 : nDigit7 < 0 ? 0 : nDigit7);
nRes = nRes * 10 + (nDigit6 > 9 ? 9 : nDigit6 < 0 ? 0 : nDigit6);
nRes = nRes * 10 + (nDigit5 > 9 ? 9 : nDigit5 < 0 ? 0 : nDigit5);
nRes = nRes * 10 + (nDigit4 > 9 ? 9 : nDigit4 < 0 ? 0 : nDigit4);
nRes = nRes * 10 + (nDigit3 > 9 ? 9 : nDigit3 < 0 ? 0 : nDigit3);
nRes = nRes * 10 + (nDigit2 > 9 ? 9 : nDigit2 < 0 ? 0 : nDigit2);
nRes = nRes * 10 + (nDigit1 > 9 ? 9 : nDigit1 < 0 ? 0 : nDigit1);
nRes = nRes * 10 + (nDigit0 > 9 ? 9 : nDigit0 < 0 ? 0 : nDigit0);
SetLocalInt(oMon, "EnergyResistance", nRes);
}
}
It all depends exactly how accurate you need the scaling to be. Hell, right now we're debating decreasing the SR increase on paragon spawns, and those have been that way for years.
Funky