MountainSage Posted January 26, 2016 Share Posted January 26, 2016 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) 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 More sharing options...
MountainSage Posted January 26, 2016 Author Share Posted January 26, 2016 Looking around a bit more into the game's source, I can find references being made to XPBoost in the java but can't see anything at first glance that points to a way of applying a boost to a perk on a character via Lua. Link to comment Share on other sites More sharing options...
Brybry Posted January 28, 2016 Share Posted January 28, 2016 I come bearing ugly presentsfunction 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); endendfunction 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. MountainSage 1 Link to comment Share on other sites More sharing options...
Brybry Posted January 28, 2016 Share Posted January 28, 2016 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 MountainSage 1 Link to comment Share on other sites More sharing options...
MountainSage Posted January 28, 2016 Author Share Posted January 28, 2016 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 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now