Jump to content

Applying an XP Boost (not XP Multiplier) to a perk post-character creation


MountainSage

Recommended Posts

So I've run into something that seems to have stumped me.

I'm currently experimenting with hobby traits (Traits that add an XP boost to a perk 75%/100%/125% and a point in a perk) when added to a character post-character creation via getTraits():add(""); and I've found that if a trait is added post character creation there are some aspects of them that won't apply.

 

One example being when First Aid is added to a character, while the trait shows up on the character, the perk level and perk xp boost remains unchanged (see below for example)

3f7809d7f8b9a1e7ecc5275b4032b37c.gif

 

I had expected that this might have been the case but needed to test to make sure before looking into how to go about adding a perk level and the boost.

 

I have a generally decent idea on where to look for the perk level addition itself, but what I can't figure out at all is how to apply an XP boost to the perk out of character creation.

 

Any help figuring this out would be greatly appreciated, specifically looking to find how to apply either increment the XP boost on a perk or get the current boost on it and then apply one to overwrite it.

Link to comment
Share on other sites

I come bearing ugly presents

function applyTraitBoostsToPlayer(player, traitName)    if not player or not instanceof(player, "IsoPlayer") then        print("Error in applyTraitToPlayer: invalid player = "..tostring(player));        return    end        if not traitName or type(traitName) ~= "string" or player:HasTrait(traitName) then        print("Error in applyTraitToPlayer: invalid traitName or player already has trait: "..tostring(traitName));        return    end            local trait = TraitFactory.getTrait(traitName);    if not trait then        print("Error in applyTraitToPlayer: trait "..tostring(traitName).." doesn't exist");        return    end    -- Get existing perks from the player xpBoostMap for caching    local xpBoostMap = transformIntoKahluaTable(player:getDescriptor():getXPBoostMap());    local cachedPerks = {};        -- Cache old perk/xp values and set the perk level to 0    for perk, value in pairs(xpBoostMap) do        local oldValues = {};        oldValues.level = player:getPerkLevel(perk);        local totalXP = player:getXp():getXP(perk);        local levelXP = getXPForPerkLevel(perk, oldValues.level);        oldValues.xp = totalXP - levelXP;        -- Value here is actually java.lang.Integer so convert it        oldValues.boost = value:intValue();        cachedPerks[perk] = oldValues;        player:level0(perk);    end    -- Add trait to the player via applyTraits. This applies XP boost and appropriate levels    local traitList = ArrayList.new();    traitList:add(traitName);    player:applyTraits(traitList);    -- Add back old perk levels and XP from cached values    for perk, oldValues in pairs(cachedPerks) do        local xp = player:getXp();        local curLevel = player:getPerkLevel(perk);        local oldLevel = oldValues.level;        local oldBoost = oldValues.boost;        local targetLevel = oldLevel + curLevel - oldBoost;        -- counteracts applyTraits() str/fit +5        if perk == Perks.Strength or perk == Perks.Fitness then            targetLevel = targetLevel - 5;        end                if targetLevel > curLevel then            for i = curLevel, targetLevel do                player:LevelPerk(perk);            end        elseif targetLevel < curLevel then            for i = curLevel, targetLevel+1, -1 do                player:LoseLevel(perk);            end                    end                xp:setXPToLevel(perk, player:getPerkLevel(perk));        xp:AddXPNoMultiplier(perk, oldValues.xp);    endend
function getXPForPerkLevel(perk, level)    if not perk or not level then        return 0    end        local target = nil;    for i = 0, PerkFactory.PerkList:size() - 1 do        local info = PerkFactory.PerkList:get(i);        if info:getType() == perk then            target = info;            break;        end    end    if not target then        return 0    end    return target:getTotalXpForLevel(level);end

 

Gist in case I update anything

 

I might be able to do a cleaner method since it may be possible to use getXPBoostMap():put() if I can create (or clone) a java.lang.Integer object but I'll have to play with it to see if that will work out.

 

If you only want the XP Boost but not the initial perk levels from the trait then set targetLevel = oldLevel and remove the strength/fitness offset bit.

Link to comment
Share on other sites

Alternate way to do it:

-- This is magical.local __INTEGER_MAP__ = Trait.new("__INTEGER_MAP__", "__INTEGER_MAP__", 0, "__INTEGER_MAP__ Trait Not For Use", false, false);local function numberToInteger(num)    if not num or type(num) ~= "number" then        num = 0    end    __INTEGER_MAP__:addXPBoost(Perks.None, num);    local tempMap = transformIntoKahluaTable(__INTEGER_MAP__:getXPBoostMap());    return tempMap[Perks.None];endlocal function getXPForPerkLevel(perk, level)    if not perk or not level then        return 0    end        local target = nil;    for i = 0, PerkFactory.PerkList:size() - 1 do        local info = PerkFactory.PerkList:get(i);        if info:getType() == perk then            target = info;            break;        end    end    if not target then        return 0    end    return target:getTotalXpForLevel(level);endfunction applyTraitBoostsToPlayer(player, traitName)    if not player or not instanceof(player, "IsoPlayer") then        print("Error in applyTraitBoostsToPlayer: invalid player = "..tostring(player));        return    end    -- Remove this bit if you do not want it    if player:HasTrait(traitName) then        print("Error in applyTraitBoostsToPlayer: player already has trait: "..tostring(traitName));        return;        else        player:getTraits():add(traitName);    end    if not traitName or type(traitName) ~= "string" then        print("Error in applyTraitBoostsToPlayer: invalid traitName or player already has trait: "..tostring(traitName));        return    end            local trait = TraitFactory.getTrait(traitName);    if not trait then        print("Error in applyTraitBoostsToPlayer: trait "..tostring(traitName).." doesn't exist");        return    end    local playerBoostMap = player:getDescriptor():getXPBoostMap();    local traitBoostMap = transformIntoKahluaTable(trait:getXPBoostMap());    local xp = player:getXp();    for perk, value in pairs(traitBoostMap) do        -- Add the appropriate value to xp boost based off of current value        local oldBoost = playerBoostMap:get(perk);        if not oldVal then            oldBoost = 0;        end            local newBoost = value:intValue();        newBoost = math.min(3, oldBoost + newBoost);        playerBoostMap:put(perk, numberToInteger(newBoost));        -- Level perk trait-value times and set appropriate experience        local oldLevel = player:getPerkLevel(perk);        local oldXP = xp:getXP(perk) - getXPForPerkLevel(perk, oldLevel);        for i = 0, value:intValue()-1 do            player:LevelPerk(perk);        end        xp:setXPToLevel(perk, player:getPerkLevel(perk));        xp:AddXPNoMultiplier(perk, oldXP);    endend
Link to comment
Share on other sites

Just woke up, checked indiestone, see 2 notifications.

 

Brybry you just made my day <3

 

Edit: Dear god Brybry this is bloody beautiful. Shared this in Discord with a friend of mine I've been bouncing off ideas with for a whole bunch of mods we wanted to do and this solves a lot of issues/provides framework to learn more about PZ modding. Massive thank you

 

d2690bccf1005d050d7f0333c8fe33fb.jpg

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...