Quantcast
Channel: Modding — Beamdog Forums
Viewing all articles
Browse latest Browse all 11774

[MOD} Several BGEE/SOD BG2EE/TOB Leveling Progress Bar tweaks

$
0
0

I love playing multi-class characters, but I'm old and lazy. While I appreciate the progress bar label telling me how much class XP is needed to level up, I hate having to do the mental multiplication to determine the total XP I need to earn. So I created an alternative to the getNextLevelString function named getAdjustedNextLevelString that does the arithmetic for me.

Also, while I like the idea of the quick 'How far I've progressed to the next level' visual that the progress bar provides; I'm not fond of the fact that it is displaying an absolute (ratio of total current XP to total required XP) rather than a level relative (ratio of XP earned since last level up to additional XP needed to reach next level) value. So, I created a Lua function, getNextLevelXPDeltas, that returns the deltas needed to feed the existing getPercent function.

I've hidden all of the previous stuff behind a spoiler tag. It's concerned with doing manual editing - interesting perhaps from a historical point of view but no longer relevant :smile:

I'm going to start with how to do the tweaks - then I'll finish with a few comments on the implementation and limitations of the getNextLevelXPDeltas function.

First, copy and paste the following code into your preferred text editor and save it as a file named LvlPgBar.lua in the {installation-directory}/00806/override directory.

function getAdjustedNextLevelString()
local multiplier = 1
if (characters[currentID].race ~= 1096) then -- Not human
if (characters[currentID].classlevel.third) then
multiplier = 3
elseif (characters[currentID].classlevel.second) then
multiplier = 2
end
end
local nextLevelXp = (characters[currentID].level.nextLvlXp - characters[currentID].level.xp) * multiplier
local str = ""
if(nextLevelXp > 0) then
str = t("NEXT_LEVEL_LABEL")
str = str .. " "
str = str .. nextLevelXp
str = str .. " "
str = str .. t("XP_LABEL")
else
str = t("READY_TO_LEVEL_LABEL")
end
return str

end

-- Extracted from clastext.2DA
local MixIdtoClassIdMap = {
-- MixedId ClassId Class/kit name
[502] = 1, --ABJURER
[504] = 1, --CONJURER
[1076] = 2, --FIGHTER
[1077] = 12, --RANGER
[1078] = 6, --PALADIN
[1079] = 3, --CLERIC
[1080] = 11, --DRUID
[1081] = 1, --MAGE
[1082] = 4, --THIEF
[1083] = 5, --BARD
[2012] = 1, --DIVINER
[2022] = 1, --ENCHANTER
[10369] = 12, --FALLEN_RANGER
[10371] = 6, --FALLEN_PALADIN
[12785] = 1, --ILLUSIONIST
[12786] = 1, --INVOKER
[12787] = 1, --NECROMANCER
[12788] = 1, --TRANSMUTER
[24205] = 3, --FALLEN_CLERIC
[24206] = 3, --FALLEN_GODLATHANDER
[24208] = 6, --FALLEN_CAVALIER
[24210] = 6, --FALLEN_INQUISITOR
[24212] = 6, --FALLEN_UNDEAD_HUNTER
[24214] = 12, --FALLEN_FERALAN
[24216] = 12, --FALLEN_STALKER
[24218] = 12, --FALLEN_BEAST_MASTER
[24227] = 19, --SORCERER
[24228] = 20, --MONK
[24229] = 2, --BARBARIAN
[24261] = 2, --BERSERKER
[24262] = 2, --WIZARD_SLAYER
[24263] = 2, --KENSAI
[24264] = 6, --CAVALIER
[24265] = 6, --INQUISITOR
[24266] = 6, --UNDEAD_HUNTER
[24267] = 12, --FERALAN
[24268] = 12, --STALKER
[24269] = 12, --BEAST_MASTER
[24270] = 4, --ASSASSIN
[24271] = 4, --BOUNTY_HUNTER
[24272] = 4, --SWASHBUCKLER
[24273] = 5, --BLADE
[24274] = 5, --JESTER
[24275] = 5, --SKALD
[24276] = 3, --GODTALOS
[24277] = 3, --GODHELM
[24278] = 3, --GODLATHANDER
[24279] = 11, --TOTEMIC_DRUID
[24280] = 11, --SHAPESHIFTER
[24281] = 11, --AVENGER
[24282] = 1, --WILD_MAGE
[28605] = 6, --BLACKGUARD
[31971] = 4, --SHADOWDANCER
[31974] = 2, --DWARVEN_DEFENDER
[31977] = 19, --DRAGON_DISCIPLE
[31980] = 20, --DARK_MOON
[31983] = 20, --SUN_SOUL
[32307] = 6, --FALLEN_BLACKGUARD
[32342] = 21, --SHAMAN
[32345] = 3 --OHTYR
}

-- Extracted from xplist.2DA
local ClassIdToXPLevelMap = {
-- Class ID Level ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?0
--[[MAGE]] [1] ={ 0, 2500, 5000, 10000, 20000, 40000, 60000, 90000, 135000, 250000,
375000, 750000, 1125000, 1500000, 1875000, 2250000, 2625000, 3000000, 3375000, 3750000,
4125000, 4500000, 4875000, 5250000, 5625000, 6000000, 6375000, 6750000, 7125000, 7500000,
7875000, 8250000, 8625000, 9000000, 9375000, 9750000,10125000,10500000,10875000,11250000,
13000000},
--[[FIGHTER]] [2] ={ 0, 2000, 4000, 8000, 16000, 32000, 64000, 125000, 250000, 500000,
750000, 1000000, 1250000, 1500000, 1750000, 2000000, 2250000, 2500000, 2750000, 3000000,
3250000, 3500000, 3750000, 4000000, 4250000, 4500000, 4750000, 5000000, 5250000, 5500000,
5750000, 6000000, 6250000, 6500000, 6750000, 7000000, 7250000, 7500000, 7750000, 8000000,
8250000},
--[[CLERIC]] [3] ={ 0, 1500, 3000, 6000, 13000, 27500, 55000, 110000, 225000, 450000,
675000, 900000, 125000, 1350000, 1575000, 1800000, 2025000, 2250000, 2475000, 2700000,
2925000, 3150000, 3375000, 3600000, 3825000, 4050000, 4275000, 4500000, 4725000, 4950000,
5175000, 5400000, 5625000, 5850000, 6075000, 6300000, 6525000, 6750000, 6975000, 8000000,
9000000},
--[[THIEF]] [4] ={ 0, 1250, 2500, 5000, 10000, 20000, 40000, 70000, 110000, 160000,
220000, 440000, 660000, 880000, 1100000, 1320000, 1540000, 1760000, 1980000, 2200000,
2420000, 2640000, 2860000, 3080000, 3300000, 3520000, 3740000, 3960000, 4180000, 4400000,
4620000, 4840000, 5060000, 5280000, 5500000, 5720000, 5940000, 6160000, 6380000, 8000000,
9000000},
--[[BARD]] [5] ={ 0, 1250, 2500, 5000, 10000, 20000, 40000, 70000, 110000, 160000,
220000, 440000, 660000, 880000, 1100000, 1320000, 1540000, 1760000, 1980000, 2200000,
2420000, 2640000, 2860000, 3080000, 3300000, 3520000, 3740000, 3960000, 4180000, 4400000,
4620000, 4840000, 5060000, 5280000, 5500000, 5720000, 5940000, 6160000, 6380000, 8000000,
9000000},
--[[PALADIN]] [6] ={ 0, 2250, 4500, 9000, 18000, 36000, 75000, 150000, 300000, 600000,
900000, 1200000, 1500000, 1800000, 2100000, 2400000, 2700000, 3000000, 3300000, 3600000,
3900000, 4200000, 4500000, 4800000, 5100000, 5400000, 5700000, 6000000, 6300000, 6600000,
6900000, 7200000, 7500000, 7800000, 8100000, 8400000, 8700000, 9000000, 9300000, 9600000,
9900000},
--[[DRUID]] [11]={ 0, 2000, 4000, 7500, 12500, 20000, 35000, 60000, 90000, 125000,
200000, 300000, 750000, 1500000, 3000000, 3150000, 3300000, 3450000, 3600000, 3750000,
3900000, 4150000, 4400000, 4700000, 5000000, 5500000, 6000000, 6500000, 7000000, 7500000,
8000000, 8500000, 9000000, 9500000,10000000,10500000,11000000,11500000,12000000,12500000,
13000000},
--[[RANGER]] [12]={ 0, 2250, 4500, 9000, 18000, 36000, 75000, 150000, 300000, 600000,
900000, 1200000, 1500000, 1800000, 2100000, 2400000, 2700000, 3000000, 3300000, 3600000,
3900000, 4200000, 4500000, 4800000, 5100000, 5400000, 5700000, 6000000, 6300000, 6600000,
6900000, 7200000, 7500000, 7800000, 8100000, 8400000, 8700000, 9000000, 9300000, 9600000,
9900000},
--[[SORCERER]][19]={ 0, 2500, 5000, 10000, 20000, 40000, 60000, 90000, 135000, 250000,
375000, 750000, 1125000, 1500000, 1875000, 2250000, 2625000, 3000000, 3375000, 3750000,
4125000, 4500000, 4875000, 5250000, 5625000, 6000000, 6375000, 6750000, 7125000, 7500000,
7875000, 8250000, 8625000, 9000000, 9375000, 9750000,10125000,10500000,10875000,11250000,
13000000},
--[[MONK]] [20]={ 0, 1500, 3000, 6000, 13000, 27500, 55000, 110000, 225000, 450000,
675000, 900000, 1125000, 1350000, 1575000, 1800000, 2025000, 2250000, 2475000, 2700000,
2925000, 3150000, 3375000, 3600000, 3825000, 4050000, 4275000, 4500000, 4725000, 4950000,
5175000, 5400000, 5625000, 5850000, 6075000, 6300000, 6525000, 6750000, 6975000, 8000000,
9000000},
--[[SHAMAN]] [21]={ 0, 2500, 5000, 10000, 20000, 40000, 60000, 90000, 135000, 250000,
375000, 750000, 1125000, 1500000, 1875000, 2250000, 2625000, 3000000, 3375000, 3750000,
4125000, 4500000, 4875000, 5250000, 5625000, 6000000, 6375000, 6750000, 7125000, 7500000,
7875000, 8250000, 8625000, 9000000, 9375000, 9750000,10125000,10500000,10875000,11250000,
13000000}
}

local u8asciiMatchString = "^([^:]-)%s*:%D+(%d+)"
local u3colonMatchString = "^(..-)[\239][\188][\154]%D-(%d+)"
local nbspaceMatchString = "^([^:]-)[\194][\160]:%D+(%d+)"

local LanguageMatch = {
['zh_CN'] = u3colonMatchString,
['cs_CZ'] = u8asciiMatchString,
['en_US'] = u8asciiMatchString,
['fr_FR'] = nbspaceMatchString,
['de_DE'] = u8asciiMatchString,
['it_IT'] = u8asciiMatchString,
['ja_JP'] = u3colonMatchString,
['ko_KR'] = u8asciiMatchString,
['nb_NO'] = u8asciiMatchString,
['pl_PL'] = u8asciiMatchString,
['pt_BR'] = u8asciiMatchString,
['ru_RU'] = u8asciiMatchString,
['es_ES'] = u8asciiMatchString,
['tr_TR'] = u8asciiMatchString,
['uk_UA'] = u8asciiMatchString,
['hu_HU'] = u8asciiMatchString
}

local KitStringToXPMap = {}

local function KitStringToXPMapInit()
for k,v in pairs(MixIdtoClassIdMap) do
KitStringToXPMap[Infinity_FetchString(k)] = ClassIdToXPLevelMap[v]
end
KitStringToXPMap['Matchstring'] = LanguageMatch[Infinity_GetINIString('Language','Text')]
KitStringToXPMap['Initialized'] = true
end

local function getCurrentLevelXP(targetDetails)
local targetKit
local targetLevel
local currentlevelxp = 0

targetKit,targetLevel = string.match(targetDetails, KitStringToXPMap['Matchstring'])

if ( -- Fail safe for kit strings that haven't yet been put into language files
targetKit and
targetLevel and
#targetKit > 0 and
not string.match(targetKit, "placeholder")
)
then
currentlevelxp = KitStringToXPMap[targetKit][tonumber(targetLevel)]
end
return currentlevelxp
end

function getNextLevelXPDeltas()
if (not KitStringToXPMap['Initialized']) then KitStringToXPMapInit() end

-- Fail safe. Can't determine language so fail by returning non-delta values
if (not KitStringToXPMap['Matchstring']) then
return characters[currentID].level.xp, characters[currentID].level.nextLvlXp
end

local candidates = {} -- Array of current level base XP for each class that will next level up

if (
characters[currentID].classlevel.third and
characters[currentID].classlevel.third.active and
characters[currentID].classlevel.third.nextLvlXp == characters[currentID].level.nextLvlXp
)
then
candidates[#candidates+1] = getCurrentLevelXP(characters[currentID].classlevel.third.details)
end

if (
characters[currentID].classlevel.second and
characters[currentID].classlevel.second.active and
characters[currentID].classlevel.second.nextLvlXp == characters[currentID].level.nextLvlXp
)
then
candidates[#candidates+1] = getCurrentLevelXP(characters[currentID].classlevel.second.details)
end

if (
characters[currentID].classlevel.first and
characters[currentID].classlevel.first.nextLvlXp == characters[currentID].level.nextLvlXp
)
then
candidates[#candidates+1] = getCurrentLevelXP(characters[currentID].classlevel.first.details)
end

local currentlevelxp
if(#candidates > 0) then
currentlevelxp = candidates[1]
for i = 2, #candidates do
currentlevelxp = math.max(currentlevelxp, candidates[i])
end
else
currentlevelxp = 0 -- Something is bogus, fail safe
end

return characters[currentID].level.xp - currentlevelxp, characters[currentID].level.nextLvlXp - currentlevelxp
end

Next, open ui.menu. Near the beginning, you'll find a Lua if() then ... else ... end statement that makes a couple of calls to Infinity_DoFile. Add the following after that code:

Infinity_DoFile("LvlPgBar")

This will add the code from our LvlPgBar.lua file to the current environment. If you want to use a different file name be aware that, at least under Windows (all I can test), the file name must conform to the old 8.3 convention for Infinity_DoFile to work. Wasted an hour or so learning that. Good thing my grandsons weren't visiting at the time - they'd have learned a couple of new cuss words :smile: .

If you want to enable the getAdjustedNextLevelString tweak, search for the label (or button if you've incorporated one of my prior tweaks) that has the line that looks like this:

    text		lua "getNextLevelString()"

Change it to this:

    text		lua "getAdjustedNextLevelString()"

To enable the getNextLevelXPDeltas tweak, search for the label containing the line:

    progressbar lua "getPercent(characters[currentID].level.xp, characters[currentID].level.nextLvlXp)"

Change it to this:

    progressbar lua "getPercent(getNextLevelXPDeltas())"

That's it! Now for the why's and limits.

The obvious why is 'Why a separate override file?'. The equally obvious answer is that the ui.menu file is inconveniently large due to the interactive ui editing feature needing all of the ui code to be in one file. Since most of the code I've created here is Lua rather than ui code - I've opt'ed to not clutter ui.menu with an extra 250 lines of code.

Why create getAdjustedNextLevelString instead of just tweaking getNextLevelString? Because one of the next things I'm going to look at is the possibility of defining my own Options and this would be a simple one to investigate. I suspect that other folks already have or will soon implement this, so it may a fairly trivial exercise (for me - not them :smile:)

The next thing has to do with the behavior of getNextLevelXPDeltas. I decided early that if, for one reason or another, the function was unable to determine the current level's base XP value, that the function should 'fail safe' - which in this case means returning the same values as the existing 'non-tweaked' code. To look up the current level's base XP value for each of the (3) potential classes/kits that could be candidates for leveling, it's necessary to know the class and the current level for that class. The only way I could discover to do this from the Lua code was by extracting them from the 'details' string of the (potentially) 3 classlevel tables. Without going into the gory details, this/these string(s) is/are what you see at the top of the 'wall of text' Information panel of the character screen (disregarding the initial 'Multi-Class'/'Dual-Class' bit). To decode them, the code needs to know the language so it can properly decode the string(s) and identify the kit/class. Thus, getting back to the original point (at last!), there are 3 possibilities for the 'fail-safe' behavior. First, if the code can't recognize the language. If you're busy creating a Klingon .tlk file, this tweak won't do you any good (at least until you tell me what language string you're stuffing into Baldur.lua). Second, and most frequent, is where a new class/kit hasn't yet been added to the .tlk file or where 'placeholder' has been used until the translation team gets around to it. There are, for example, several of the languages that have 'placeholder' for the Shaman class (and yes, I know that a lot of the translation work is done by volunteers - what I'm saying should not be considered a complaint or criticism). In those cases, for characters of the 'placeholder' class(es) - you'll get the 'fail-safe' behavior. The final case is where there's a bug. Currently (2.1), the Turkish language 'details' string(s) are missing the class/kit portion. The strings (with the appropriate strrefs) are in the .tlk file. They're just not being used to build the 'details' strings. I'm assuming this is such an obvious problem that it's already been noted. So, for Turkish, this tweak doesn't accomplish anything. I'll obviously be checking this when 2.2 drops.

The last item to mention is where multiple classes will level up at the same point - with different current level base XP values. Which base value is used? The highest one. It makes the code a bit simpler (and thus faster) and also makes the progress bar a bit more sensitive to smaller XP gains. If you would prefer, you can change the line that invokes 'math.max' to 'max.min' - just be sure you're dealing with (or don't have to deal with) the case where the private getCurrentLevelXP function returns 0 (typically the 'placeholder' case above).

The current release is part of EEUITweaks
The file below contains information for configuring the mod by editing Baldur.lua (versus using Mods Options)

Enjoy - and if anyone wishes to use any or all of this (or anything else I've posted on this forum) in their own tweaks/mods, please feel free to do so. Posted publicly to be used by anyone.


Viewing all articles
Browse latest Browse all 11774

Latest Images

Trending Articles



Latest Images