Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

New Premium Module: Tyrants of the Moonsea! Read More
Attention, new and old users! Please read the new rules of conduct for the forums, and we hope you enjoy your stay!

[Tool] Weidu functions for modding HLAs

subtledoctorsubtledoctor Member Posts: 11,460
edited August 2017 in General Modding
PREAMBLE: HLAs are terrible. They were coded quickly, they are limited in number, scope, and imagination, and some of them just make no sense. Monks get fighter HLAs? Bards get traps? Huh?

Unfortunately, the extraordinary modding scene for BG2 has not produced much in the way of HLA mods. Off the top of my head I can think of Oversight for Monks, Rogue Rebalancing for rogues, maybe Divine Remix for priests (did DR include HLAs?), and of course, Refinements, the best HLA mod ever. AFAIK these mods all overwrite the HLA tables, they are largely incompatible, and they still only leave each class with like 10-12 HLAs. Other modders have, for whatever reasons, stayed away from modding HLAs.

I aim to fix that. :sunglasses: If you have any interest in modding HLAs, keep reading.

First, grab the "source code (zip)" from this Github project. Open it up and you'll see a little mod with a .tp2 file, a /lib folder, and a file inside that called "hla_actions.tpa." This .tpa file contains some Weidu patch functions and action functions, which are designed to help modders add HLAs to the game in a way that is completely compatible with any other mods that use the same tool. I'll explain how they work. But for newbies, let's first talk about HLA basics.

HLAs are just spells. Some of them are actual spells that go in your wizard spell scroll (like Dragon's Breath) or priest spell scroll (like Globe of Blades). Others are spells that become innate abilities when you choose them (like Whirlwind, or the paladin version of Summon Deva). And others are spells that are cast on you when you choose them, resulting in some permanent effect (like bonus wizard spells). In the game engine, these are all just .SPL files. The HLA tables work just like clab tables for kit abilities: those first three categories I just mentioned are listed with "GA_" in front of them, and the last category is listed with "AP_" in front of them.

So, makign an HLA is exactly like making a spell. It is making a spell, a spell that is either given to the player to cast, or is cast on the player. Before going any further, if you don't know how to make spells, go learn that.

...

Back? Okay. Now, on to the functions in this download.

First, grab that hla_actions.tpa file and put it somewhere in your mod, and add an INCLUDE line referring to it in your .tp2 file:
INCLUDE ~mymod/lib/hla_actions.tpa~

...

The first function is "add_hla." This function accepts a number of variables, which conveniently correspond to the column headings in an HLA .2da table. The variables are pre-set to default values of "*" - except min_lev and max_level, which are pre-set to 1 and 99, respectively. You don't need to enter all variables; just the ones that you would need to put in a line in an HLA table if you were adding the HLA manually in Near Infinity, or something.

So, for example, to add the paladin's Summon Deva to the trueclass fighter's HLA table, you would do this:
COPY_EXISTING ~lufi0.2da~ ~override~
LPF add_hla STR_VAR ability = ~GA_SPCL923~ num_allowed = ~20~ END
BUT_ONLY
That will have the effect of adding this row to the fighter's HLA table:
1      GA_SPCL923     *          *         1         99         20           *            *             *
And now fighters can summon devas!

Basically, to add your HLA with this function, open up the HLA table and add variables that match the columns you would fill out manually. Hopefully that is pretty simple. :smile:

Post edited by subtledoctor on
IllustairinethelminsterGrammarsaladjackjackmf2112Abel
«1

Comments

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited April 2016
    The second function is "remove_hla." This does exactly what it sounds like: it removes an HLA from a table. It only takes a single variable: remove_ability. For this variable you just enter the GA_xxxx or AP_xxxx line for the HLA you want to remove.

    So, say you don't think it's appropriate for bards to set spike traps. You can do this:
    COPY_EXISTING ~luba0.2da~ ~override~
    LPF remove_hla STR_VAR remove_ability = ~GA_SPCL910~ END
    BUT_ONLY
    Simple, right?

    Grammarsaladmf2112
  • subtledoctorsubtledoctor Member Posts: 11,460
    edited April 2016
    The third function is "replace_hla," and it is pretty much exactly what it sounds like. It runs one instance of remove_hla and one instance of add_hla, and the end result is that you replace an existing HLA in a table with a new one of your choosing. This function accepts all of the variables of the first two functions combined, i.e. one variable for each column in an HLA table for the HLA you want to add, plus "remove_ability" for the HLA you want replaced.

    Here's an example: instead of just removing Spike Trap from bards, you can replace it with the paladin Summon Deva ability:
    COPY_EXISTING ~luba0.2da~ ~override~
    LPF replace_hla STR_VAR remove_ability = ~GA_SPCL910~ ability = ~GA_SPCL923~ num_allowed = ~20~ END
    BUT_ONLY

    Grammarsaladmf2112
  • subtledoctorsubtledoctor Member Posts: 11,460
    edited April 2016
    The final function, "get_hla_table," is a bit different. This simply makes it easy to look up the HLA table currently in use for a kit or class. It takes a single input - kit_name -and outputs that kit's HLA table in the variable %hla_table%.

    In use, it looks like this:
    LAF get_hla_table STR_VAR kit_name = ~BOUNTY_HUNTER~ RET hla_table END
    So let's look at an example of how I might want to use these things. Say I created a really cool ability: Web Trap. And I want to give it specifically to the Bounty Hunter as a new HLA. Well, if you look in LUABBR.2da, the Bounty Hunter currently uses "th0," i.e. luth0.2da, as its HLA table. But, the trueclass thief and the Assassin both use that table as well! :(

    So, I will need to create a new table for the Bounty Hunter. Well that's not hard, I can just do something like
    COPY_EXISTING ~luth0.2da~ ~override/lud5bh.2da~
    LPF add_hla STR_VAR ability = ~GA_d5webtrp~ num_allowed = ~20~ END
    COPY_EXISTING ~LUABBR.2DA~ ~override~
    SET_2DA_ENTRY 37 1 2 ~d5bh~
    Pretty easy... BUT: what if the player has installed Refinements? With Refinements, the Bounty Hunter uses luth3.2da for its HLA table. By associating it with lud5bh.2da, I have just made sure the player cannot use any Refinements Bounty Hunter HLAs. Incompatibility is bad!

    So, instead I do this:
    INCLUDE ~mymod/lib/hla_actions.tpa~

    COPY ~mymod/spell/d5webtrp.spl~ ~override~
    SAY NAME1 ~Web Trap~
    SAY UNIDENTIFIED_DESC ~When this trap is triggered, nearby enemies must save vs. Breath Weapon or be ensnared by gooey webs!~

    LAF get_hla_table STR_VAR kit_name = ~BOUNTY_HUNTER~ RET hla_table END

    ACTION_IF %hla_table% STRING_EQUAL_CASE ~th0~ BEGIN
    COPY_EXISTING ~luth0.2da~ ~override/lud5bh.2da~
    LPF add_hla STR_VAR ability = ~GA_d5webtrp~ num_allowed = ~20~
    COPY_EXISTING ~LUABBR.2DA~ ~override~
    SET_2DA_ENTRY 37 1 2 ~d5bh~
    END
    ELSE BEGIN
    COPY_EXISTING ~lu%hla_table%.2da~ ~override~
    LPF add_hla STR_VAR ability = ~GA_d5webtrp~ num_allowed = ~20~ END
    END
    Without even knowing what HLA table a player happens to be using for any particular kit, my mod can patch it to add my new HLA, in a non-destructive non-overwriting way.

    Hopefully that was clear. If anyone has questions, go ahead and ask in this thread. If anyone doesn't have questions and is clear on how to make new HLAs and add them to the game this way... then get out there and do it! :smiley:

    Post edited by subtledoctor on
    Illustairmf2112
  • subtledoctorsubtledoctor Member Posts: 11,460
    edited August 2017
    Updated to 0.6... this adds action functions in addition to the patch functions. With the patch functions, you need to know the name of the HLA table you want to edit and as described two posts up (April 28), if you're not sure whether some other mod might change a kit's table, you need to use a bit of "ACTION_IF STRING_EQUAL_CASE trickery to cover all bases.

    No more - now you only need to know the name of the kit whose table you want to change in LUABBR.2da, and code it like this:
    LAF action_remove_hla STR_VAR kit_name = ~RANGER~ remove_ability = ~GA_SPCL922~ END

    LAF action_add_hla STR_VAR kit_name = ~RANGER~ ability = ~GA_SPCL923~ num_allowed = ~20~ END

    LAF action_replace_hla STR_VAR kit_name = ~RANGER~ remove_ability = ~GA_SPCL900~ ability = ~GA_heya!~ num_allowed = ~1~ END
    Those three commands modify the trueclass ranger HLA table: the first removes Tracking, the second adds Summon Deva, and the third replaces Power Attack with a nonexisting "Heya!" HLA. (You know, Noober's innate ability.) The variables in each are the same as in the patch functions: action_add_hla has a variable for each column in an HLA table .2da file; action_remove_hla has a "remove_ability" variable to tell i which HLA to remove (which must include the "GA_" or "AP_" prefix), and action_replace_hla has all of those variables. The difference here is that these action functions have an additional variable, "kit_name," which you use to tell the function which table to edit. You get this from LUABBR.2da, or in the case of mod kits, from the name used in their ADD_KIT code.

    As you can see from the above snippet, you don't need to know ahead of time the .2da name of the kit's HLA table that you want to edit. If these functions are working as they should be, it does not matter whether another mod has changed the HLA table, or what the name of the new table is. This function will find it, edit the table in a non-destructive way, save the result as a new table, and associate the kit with the new table. The name of the new table is based on the row # of the kit in LUABBR.2da, so every kit is guaranteed a unique table name. If any other mods use these functions as well, they should all work together. These functions will work when run multiple times; after the first run sets the new HLA table name, each successive run will retain that name.

    Here's a download with some example code; if you want to use it in your mod, just grab the /lib folder with the .tpa file containing the functions, put it in your mod folder, and INCLUDE it in your .tp2.

    https://github.com/UnearthedArcana/Modify_HLAs/releases/

    Post edited by subtledoctor on
    rapsam2003mf2112
  • GrammarsaladGrammarsalad Member Posts: 2,509
  • GreenerGreener Member Posts: 385
    Is there anyway to allow a particular kit access to HLAs ahead of other kits in the same class?

    For example if I wanted a caviler to gain access to HLAs at level 15 instead of 20, is there a way to isolate that?

  • AquadrizztAquadrizzt Member Posts: 958
    @Greener, not to my knowledge. Pretty sure HLA level access is assigned on a class basis unfortunately.

  • subtledoctorsubtledoctor Member Posts: 11,460
    No. I spent an inordinate amount of time trying to make the HLA system more flexible and work at lower levels. But it is extremely inflexible.

    Basically as the devs were coding it ahead of the TOB release, and as soon as it worked for the specific system they had created (HLA every level starting at 3,000,000 XP) they stopped working on it and left the rest in an unfinished state. Two examples that have particularly vexed me:

    1) The LUxxx.2da files have a "min_level" column. One would think this could certain HLA to Bly be available at certain levels. That would be extremely useful, no? A cleric could summon a deva at 3,000,000 XP, and a planetar at 4,000,000 XP. Makes sense! But the column doesn't work that way. It's basically just there for arcane/divine spell HLAs, to make sure multiclasses can't get 9th level HLA spells before they get normal 9th level spells.

    2) lunumab.2da has a "rate" column which I thought could be very useful: maybe it could let a class get a new HLA every 2 levels, instead of every level. But, instead, putting a 2 in that column makes you get 2 HLAs per level. Which is crazy and I don't know how it could ever be useful. But it's what we're stuck with.

    Long story short, the HLA system is very very limited and inflexible, from a structural standpoint. On-topic, @Greener, no you can only change access to them on a per-class basis, not on a per-kit basis.

    (Anyway, honestly, HLAs were designed very specifically to be used during the TOB portion of the game, IMHO it would be silly and unfunny to be throwing them around at level 15.)

  • rapsam2003rapsam2003 Member Posts: 1,636

    2) lunumab.2da has a "rate" column which I thought could be very useful: maybe it could let a class get a new HLA every 2 levels, instead of every level. But, instead, putting a 2 in that column makes you get 2 HLAs per level. Which is crazy and I don't know how it could ever be useful. But it's what we're stuck with.

    Just musing here...I assume that putting "0.5" as a value isn't valid?

  • kjeronkjeron Member Posts: 2,131
    You can simulate it:
    - Drop the required level for HLA's to 1 for the class.
    - Assign a dummy prerequisite HLA to each HLA that doesn't already have one or use the MIN_LEV column.
    - Apply that dummy prerequisite HLA with Opcode 313 in each of that class's CLAB files one level before the desired level for it to become available (HLA's are done before the CLAB at level-up, so it has to be done on the previous level).

    Note - You cannot choose HLA's at character creation, so SoA and ToB starts will lose any HLA picks they would have gotten at those pre-starting levels. This might be something the new UI could actually be modified to fix.

  • AquadrizztAquadrizzt Member Posts: 958
    It might be possible with the new UI modding possibilities to make a better HLA system, but it would probably be a lot of work...

  • subtledoctorsubtledoctor Member Posts: 11,460
    Interesting ideas, but I don't think it would pan out in practice.

    - If you try to level up and have no available abilities, the game may not let you progress through the level-up process. I'm not certain if this with all the recent changes, but IIRC this is still a problem if, for instance, you run out if proficiency slots to spend.

    - if you want to give players a choice of multiple HLAs, you can give them all the same pre-requisite... but then they all become available. And then they are available every level, instead of every other level. I suppose you could cast another spell on the odd levels using opcode 321 to cancel the 313 effect... but that sounds less-than-reliable. I suppose you could use local variables too, but mixing script/variables with spell effects with HLA tables with UI picking mechanism... also sounds less than reliable. And I don't have the time to experiment with it.

    At this point I have a working mechanism to choose feats/low-level abilities... the only real issue with it is 1) it can't work with arcane casters, and 2) you can't read the full descriptions of the abilities while picking them. Actually this last issue might be something UI modding could address - instead of using the NRD mechanism, perhaps using the "choose a feat" spell could open a Sequencer-style or HLA-style UI sheet to pick abilities from. But, that couldn't be done with Weidu, so screw it.

    Just musing here...I assume that putting "0.5" as a value isn't valid?

    No other .2da file in the game works like that so I strongly suspect it would not work. Take a look at e.g. wspatck.2da.

  • kjeronkjeron Member Posts: 2,131

    - If you try to level up and have no available abilities, the game may not let you progress through the level-up process. I'm not certain if this with all the recent changes, but IIRC this is still a problem if, for instance, you run out if proficiency slots to spend.

    The game handles this just fine for HLA's, surprisingly.

    - if you want to give players a choice of multiple HLAs, you can give them all the same pre-requisite... but then they all become available. And then they are available every level, instead of every other level.

    It was meant as a response to Greener's selectively early access, sorry for confusion.

  • subtledoctorsubtledoctor Member Posts: 11,460
    Ah! Got it. Yeah that would work perfectly for that.

  • kjeronkjeron Member Posts: 2,131

    Just musing here...I assume that putting "0.5" as a value isn't valid?

    No other .2da file in the game works like that so I strongly suspect it would not work. Take a look at e.g. wspatck.2da.
    Funny thing is, it does accept such values. But, it doesn't keep track of the fractional values between levels, so you have to gain enough levels at one time for the factor to be greater than 1.
    So with a 0.5 you would have to gain 2 levels at a time to get an HLA.
    With a .25 you would have to gain 4 levels at a time to get an HLA.
    With a .2 you would have to gain 5 levels at a time to get an HLA.
    etc...
    Fractional values greater than 1 also work(1.2, 1.5, 2.3, etc...) in the same way - fractional values are dropped after each levelup.

    So yeah, not remotely practical.

  • subtledoctorsubtledoctor Member Posts: 11,460
    Another example we looked at was doing a kit-switch every level, and only one of the two kits gets HLAs. But that is broken if you DO level up multiple times at once.

    The only way to make it work that I've discovered are 1) using wizard spells and NRD, as with my Rogue Feats component; and 2) using dialogue and local variables, as with my Revised Kensai component.

  • GreenerGreener Member Posts: 385

    (Anyway, honestly, HLAs were designed very specifically to be used during the TOB portion of the game, IMHO it would be silly and unfunny to be throwing them around at level 15.)

    Thank you for the response, while I agree with you, I was simply exploring options, not attempting to create broken kits ect...

    In addition if the system was in fact more flexible I was envisioning a NWN feats style system with abilities that are more appropriate for the earlier levels.

    Regardless thank you for your hard work to date!

  • rapsam2003rapsam2003 Member Posts: 1,636
    edited July 2016
    Greener said:

    In addition if the system was in fact more flexible I was envisioning a NWN feats style system with abilities that are more appropriate for the earlier levels.

    IF ONLY. That would be SO awesome. But, no, this is why the InfinityEngine isn't fun. It's such a pain changing anything.

  • subtledoctorsubtledoctor Member Posts: 11,460

    Greener said:

    In addition if the system was in fact more flexible I was envisioning a NWN feats style system with abilities that are more appropriate for the earlier levels.

    IF ONLY. That would be SO awesome. But, no, this is why the InfinityEngine isn't fun. It's such a pain changing anything.
    Well, to be fair, I have actually implemented a low-level feat system in Might & Guile. Like I said, there are two possible ways to do it. It's just very... complex, either way.

  • rapsam2003rapsam2003 Member Posts: 1,636

    Well, to be fair, I have actually implemented a low-level feat system in Might & Guile. Like I said, there are two possible ways to do it. It's just very... complex, either way.

    Whereas the NWN feat system is pretty easy, by comparison.

  • subtledoctorsubtledoctor Member Posts: 11,460

    Well, to be fair, I have actually implemented a low-level feat system in Might & Guile. Like I said, there are two possible ways to do it. It's just very... complex, either way.

    Whereas the NWN feat system is pretty easy, by comparison.
    I meant it was complex to create...

  • rapsam2003rapsam2003 Member Posts: 1,636

    Well, to be fair, I have actually implemented a low-level feat system in Might & Guile. Like I said, there are two possible ways to do it. It's just very... complex, either way.

    Whereas the NWN feat system is pretty easy, by comparison.
    I meant it was complex to create...
    I understand that. The nature of IE is that changing anything significant requires complex systems on top of what's already there.

  • GreenerGreener Member Posts: 385
    edited August 2016
    @subtledoctor

    Does the action action_add_hla function allow for prerequisites?

    ABILITY ICON STRREF MIN_LEV MAX_LEVEL NUM_ALLOWED PREREQUISITE EXCLUDED_BY ALIGNMENT_RESTRICT 8 GA_SPCL914 * * 1 99 20 GA_SPCL913 * *

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited August 2016
    Yup. The variable names are identical to the column names in the HLA table. So it would be something like
    STR_VAR prerequisite = ~GA_SPCL901~

    Post edited by subtledoctor on
    Greener
  • GreenerGreener Member Posts: 385
    edited August 2016
    @subtledoctor

    BEGIN ~Kensai High Level Ability revision~ ACTION_IF GAME_IS ~bg2ee~ THEN BEGIN // BG2EE INCLUDE ~Kensai Kit Revision/library/hla_actions.tpa~ // Modify HLA functions (subtledoctor) COPY ~Kensai Kit Revision/Spells/KENSAIH.spl~ ~override~ // Custom HLA Second Wind SAY NAME1 ~Second Wind~ SAY UNIDENTIFIED_DESC ~With extraordinary effort, a high-level kensai can avoid almost certain death. The effect lasts for 2 rounds, and during this time, the kensai cannot be reduced below 1 HP.~ LAF get_hla_table STR_VAR kit_name = ~KENSAI~ RET hla_table END ACTION_IF %hla_table% STRING_EQUAL_CASE ~fi0~ BEGIN COPY_EXISTING ~lufi0.2da~ ~override/lud5bh.2da~ LAF action_add_hla STR_VAR ability = ~GA_KENSAIH~ num_allowed = ~1~ END COPY_EXISTING ~LUABBR.2DA~ ~override~ SET_2DA_ENTRY 31 1 1 ~d5bh~ END ELSE BEGIN COPY_EXISTING ~lu%hla_table%.2da~ ~override~ LAF action_add_hla STR_VAR ability = ~GA_KENSAIH~ num_allowed = ~1~ END END END

    Something seems to be array when I try and add a custom HLA. For some reason I'm not coping the lufi0.2da to the override folder not am I copying or editing the LUABBR.2da.

    Thank you in advance...

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited August 2016
    You only need to do all that "get_hla_table" and "ACTION_IF" stuff if you are using the patch_add_hla function. The action_add_hla function has it all built-in. :)

    So you should be able to simply do this (or something like this - I'm posting code from my phone):
    LAF action_add_hla STR_VAR kit_name = ~KENSAI~ ability = ~GA_KENSAIH~ num_allowed = ~1~ END
    That's it! Way simpler, right?

  • GreenerGreener Member Posts: 385
    edited August 2016
    It works! Thank you.

    In addition to adding a custom HLA, I used your code to rearrange the order in which they appear.BEGIN ~Kensai High Level Ability revision~ ACTION_IF GAME_IS ~bg2ee~ THEN BEGIN INCLUDE ~Kensai Kit Revision/library/hla_actions.tpa~ COPY ~Kensai Kit Revision/Spells/KENSAIH.spl~ ~override~ SAY NAME1 ~Second Wind~ SAY UNIDENTIFIED_DESC ~With extraordinary effort, a high-level kensai can avoid almost certain death. The effect lasts for 2 rounds, and during this time, the kensai cannot be reduced below 1 HP.~ LAF action_remove_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL900~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL901~ ability = ~GA_SPCL901~ num_allowed = ~20~ END LAF action_remove_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL902~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL903~ ability = ~GA_SPCL903~ num_allowed = ~20~ END LAF action_remove_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL908~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL906~ ability = ~GA_SPCL906~ num_allowed = ~20~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL905~ ability = ~GA_SPCL905~ num_allowed = ~20~ prerequisite = ~GA_SPCL906~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL909~ ability = ~GA_SPCL909~ num_allowed = ~20~ prerequisite = ~GA_SPCL905~ END LAF action_add_hla STR_VAR kit_name = ~KENSAI~ ability = ~GA_SPCL913~ num_allowed = ~20~ END LAF action_add_hla STR_VAR kit_name = ~KENSAI~ ability = ~GA_SPCL914~ num_allowed = ~20~ prerequisite = ~GA_SPCL913~ END LAF action_add_hla STR_VAR kit_name = ~KENSAI~ ability = ~GA_KENSAIH~ num_allowed = ~1~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL907~ ability = ~GA_SPCL907~ num_allowed = ~20~ END LAF action_replace_hla STR_VAR kit_name = ~KENSAI~ remove_ability = ~GA_SPCL904~ ability = ~GA_SPCL904~ num_allowed = ~20~ END END

  • subtledoctorsubtledoctor Member Posts: 11,460
    Nice! That is more is less what I have the new version of Refinements doing, as well as Might & Guile. It looks dense, but you can alter and build whole tables that way, and if multiple mods all use this function, they will all be compatible with one another. :)

Sign In or Register to comment.