Unfortunately, most of these just naively called Champion:setClass() without doing much else. This leaves the champion with all the extra bonuses they earned from their old skills - health, energy, strength, dexterity, vitality, willpower, and traits like quick strike and combat caster.
All of this is easily avoidable! The only thing that isn't trivial to fix is the champion's collected experience, since as far as I can tell the script interface doesn't have access to that - only their level. You could track it via hooks on every monster (and any other source of experience), but I won't address that here.
This script allows you to cleanly change the class (or reset the skill points) of a champion. Just stick this code in a script entity in your dungeon, and whenever you get the urge to call Champion:setClass(), call the script's cleanSetClass() function instead.
Code: Select all
-- Clean setClass
-- Version 1.0
-- Written by Andrew Minton (minmay)
-- I place this script in the public domain to be used however you wish.
-- If you don't believe code can be in the public domain, feel free to
-- interpret under any version of the GNU GPL or the WTFPL.
-- You can even remove this comment if you want!
--
-- Usage: When you want to change a champion's class, call
-- cleanSetClass(champion,class,refundPoints)
-- For example, "cleanSetClass(party:getChampion(1),"Mage",true)
--
-- champion must be a champion.
-- class must be "Ranger", "Fighter", "Rogue", or "Mage".
-- If refundPoints is present, the champion will get any spent skill points
-- back. If the refundPoints parameter is false or absent, spent skill points
-- will be lost (but unspent skill points are still kept).
-- This function can also be used to allow champions to "respec" by refunding
-- their skill points and keeping their original class. To allow a champion
-- to respec, use a line like this:
-- cleanSetClass(party:getChampion(1),party:getChampion(1):getClass(),true)
--
-- Like Champion:setClass(), cleanSetClass() leaves the champion with all
-- skills at 0. Unlike Champion:setClass(), it will also remove all stats
-- and traits gained from skills, and retain the champion's experience level
-- (although they will lose any experience gained between their current level
-- and the next).
-- Note that in order to retain the champion's experience level, the champion
-- has to be leveled up a number of times equal to their current level minus one.
-- If your mod has onLevelUp hooks, make sure that you account for this.
SKILL_BONUSES = {
air_magic = {
[5]={resist_shock=10},
[11]={dexterity=1},
[17]={resist_shock=10},
[25]={dexterity=1},
[27]="greater_lightning_bolt",
[30]={resist_shock=10},
[32]="air_circle",
[35]={dexterity=1},
[39]={resist_shock=10},
[44]={dexterity=1},
[50]={resist_shock=100},
},
armors = {
[2]={protection=1},
[5]={health=10},
[8]="light_armor",
[12]={health=10},
[16]="heavy_armor",
[19]={health=15},
[22]={protection=2},
[25]="shield_expert",
[28]={health=15},
[33]={evasion=5},
[35]={health=25},
[38]={protection=2},
[44]={health=25},
[50]={protection=25}
},
assassination = {
[4]={strength=1},
[8]="backstab",
[12]="reach_attack",
[16]={strength=2},
[20]="improved_backstab",
[23]="quick_strike",
[27]={strength=2},
[31]="improved_critical",
[35]="piercing_attack",
[40]={strength=2},
[45]="improved_quick_strike",
[50]="assassin_master"
},
athletics = {
[2]={strength=1},
[5]={vitality=2},
[8]={health=10},
[10]="endurance",
[12]={dexterity=2},
[16]={strength=2},
[20]="porter",
[22]={vitality=2},
[24]={dexterity=2},
[28]={health=10},
[30]={resist_fire=10,resist_cold=10},
[33]={strength=2},
[38]={vitality=2},
[40]={resist_poison=10,resist_shock=10},
[45]={health=10},
[50]={health=100}
},
axes = {
[4]={strength=1},
[7]={health=5},
[10]="chop",
[14]={health=5},
[18]={strength=1},
[22]="cleave",
[26]={health=5},
[29]={strength=1},
[33]="rampage",
[38]={health=5},
[42]={strength=1},
[46]={health=5},
[50]="axe_master",
},
daggers = {
[4]={dexterity=1},
[7]={energy=5},
[10]="stab",
[14]={energy=5},
[18]={dexterity=1},
[22]="piercing_strike",
[26]={energy=5},
[29]={dexterity=1},
[33]="flurry_slashes",
[38]={energy=5},
[42]={dexterity=1},
[46]={energy=5},
[50]="dagger_master"
},
dodge = {
[2]={evasion=5},
[5]={resist_fire=5,resist_shock=5},
[8]={health=15},
[11]="stealth",
[14]={evasion=5},
[17]="light_armor",
[20]={health=15},
[24]="improved_stealth",
[28]={resist_poison=20},
[32]={evasion=5},
[34]={resist_fire=25,resist_shock=25},
[38]={health=15},
[42]={evasion=5},
[46]={health=15},
[50]={evasion=50}
},
earth_magic = {
[9]={resist_poison=10},
[17]={vitality=1},
[19]={resist_poison=10},
[23]="improved_poison_bolt",
[26]={vitality=1},
[29]={resist_poison=10},
[32]="earth_circle",
[35]={vitality=1},
[39]={resist_poison=10},
[44]={vitality=1},
[50]={resist_poison=100}
},
fire_magic = {
[6]={resist_fire=10},
[10]={strength=1},
[19]={resist_fire=10},
[21]={strength=1},
[24]="greater_fireball",
[28]={resist_fire=10},
[32]="fire_circle",
[35]={strength=1},
[39]={resist_fire=10},
[44]={strength=1},
[50]={resist_fire=100}
},
ice_magic = {
[5]={resist_cold=10},
[10]={willpower=1},
[16]={resist_cold=10},
[21]={willpower=1},
[24]="improved_frostbolt",
[28]={resist_cold=10},
[32]="ice_circle",
[35]={willpower=1},
[39]={resist_cold=10},
[44]={willpower=1},
[50]={resist_cold=100},
},
maces = {
[4]={vitality=1},
[7]={health=5},
[10]="bash",
[13]={vitality=1},
[17]={health=5},
[20]="crushing_blow",
[24]={vitality=1},
[28]={health=5},
[33]="devastating_blow",
[38]={vitality=1},
[42]={health=5},
[46]={vitality=1},
[50]="mace_master"
},
missile_weapons = {
[4]={dexterity=1},
[8]={energy=5},
[12]="quick_shot",
[16]={dexterity=1},
[20]={energy=5},
[24]="improved_quick_shot",
[28]={energy=5},
[32]="volley",
[36]={dexterity=1},
[40]={energy=5},
[45]={dexterity=1},
[50]="master_archer"
},
spellcraft = {
[2]={willpower=1},
[6]={energy=10},
[8]={willpower=1},
[10]="combat_caster",
[12]={energy=10},
[15]={willpower=1},
[18]="improved_combat_caster",
[22]={energy=10},
[26]={willpower=1},
[30]={energy=10},
[34]={willpower=1},
[38]={energy=10},
[42]={willpower=1},
[46]={energy=10},
[50]="archmage"
},
staves = {
[2]={protection=1},
[5]={evasion=5},
[8]={health=10},
[11]={protection=2},
[14]="light_armor",
[18]={evasion=5},
[22]={health=10},
[27]={evasion=5},
[32]={health=10},
[37]={protection=2},
[42]={evasion=5},
[50]={protection=10,evasion=30}
},
swords = {
[4]={strength=1},
[7]={health=5},
[10]="slash",
[13]={energy=5},
[16]="parry",
[19]={dexterity=1},
[23]="thrust",
[26]={health=5},
[29]={strength=1},
[33]="flurry_slashes",
[37]={energy=5},
[42]={health=5},
[46]={dexterity=1},
[50]="sword_master"
},
throwing_weapons = {
[4]={strength=1},
[8]={health=5},
[12]="quick_throw",
[16]={strength=1},
[20]={health=5},
[24]="improved_quick_throw",
[28]={strength=1},
[32]="double_throw",
[36]={strength=1},
[40]={health=5},
[45]={strength=1},
[50]="throwing_master"
},
unarmed_combat = {
[4]={dexterity=1},
[7]={health=10},
[11]="jab",
[15]={dexterity=2},
[19]={health=10},
[23]="kick",
[27]={health=10},
[30]={evasion=20},
[34]={strength=2},
[38]={dexterity=2},
[42]={strength=5,dexterity=5},
[45]={health=10},
[50]="three_point_technique"
}
}
-- Pass a champion to remove all bonuses gained from skills.
-- Does not remove the skill levels themselves.
-- Returns the total number of skill levels possessed by the champion.
function stripSkillBonuses(champ)
local totalPoints = 0
for skill,bonuses in pairs(SKILL_BONUSES) do
local sklev = champ:getSkillLevel(skill)
totalPoints = totalPoints+sklev
for i = 1, sklev do
local bonus = bonuses[i]
if bonus then
if type(bonus) == "table" then -- stats
for stat,amount in pairs(bonus) do
champ:modifyStat(stat,-amount)
champ:modifyStatCapacity(stat,-amount)
end
else -- trait
champ:removeTrait(bonus)
end
end
end
end
return totalPoints
end
-- Pass a champion to set their class while retaining their level
-- and removing all skill bonuses from the old class.
-- Like the native setClass, this sets all of the champion's skills to 0.
-- (You can set a champion to their own class for a "respec" feature.)
-- However, it retains their level and eliminates all bonuses and traits
-- gained from those skills.
-- If refundPoints parameter is left out (or false), skill points
-- spent on skills will be lost. Unspent skill points are still kept.
-- NOTE: The champion will lose any experience gained between their
-- current level and the next.
-- NOTE: This will trigger any onLevelUp hook a number of times equal
-- to the champion's level.
function cleanSetClass(champ,class,refundPoints)
local spentSkillPoints = stripSkillBonuses(champ)
local unspentSkillPoints = champ:getSkillPoints()
local level = champ:getLevel()
local health = champ:getStatMax("health")
local energy = champ:getStatMax("energy")
champ:setClass(class)
while champ:getLevel() < level do champ:levelUp() end
-- Eliminates skill point gains from leveling up. In vanilla Grimrock
-- this can obviously be done without storing unspent skill points by subtracting
-- level*4, but I've seen numerous mods where champions don't always
-- gain 4 points per level, so I might as well account for that case.
champ:addSkillPoints((refundPoints and spentSkillPoints or 0)+unspentSkillPoints-champ:getSkillPoints())
-- Eliminate health and energy gains from leveling up.
champ:setStatMax("health",health)
champ:setStatMax("energy",energy)
end