Skip to content

[MOD] EEex (v0.10.2-alpha)

1111214161748

Comments

  • RaduzielRaduziel Member Posts: 4,714
    @Bubb Can you lift (or increase) the limit of kits the game may have added?

    Thanks.
    BubbGrammarsalad
  • ThelsThels Member Posts: 1,416
    @subtledoctor Why not run the game on your desktop and stream it to your ipad, whenever you need to do something that you can't do on an ipad directly?
  • JuliusBorisovJuliusBorisov Member, Administrator, Moderator, Developer Posts: 22,714
    @Bubb I've passed your question, and as long as the file is hosted on this forum, you are ok.
    Bubbfearless
  • BubbBubb Member Posts: 998
    @JuliusBorisov: Thank you! :)



    @swit: Oops; I never tested the trigger in dialogs, only scripts.

    We have two problems now:
    1. The return won't work because the engine doesn't allow whitespace in dialog scripting...
    2. Dialog scripting is interpreted with something much like an C:Eval() on-demand, and this system is apparently too simple to understand that the comma inside the string does not belong to the trigger itself.

    #1 can be fixed by having the return value passed-back to the engine via a global, "EEex_LuaTrigger".
    #2 is bad... it is a fundamental flaw in the script parser, and might be beyond my ability to fix via exe hacking.

    You proposed something like this before, if I remember correctly: Would it be possible for you to use some sort of WeiDU trickery to move inlined Lua strings into a M_*.lua file, and then replace the EEex_LuaTrigger() string with a simple function call?

    This:
    EEex_LuaTrigger("B3MyTriggerFunc(1, 1)")
    

    Would become something like:
    EEex_LuaTrigger("someAutoGeneratedFunc()")
    

    With this ending up in a M_*.lua:
    function someAutoGeneratedFunc()
        B3MyTriggerFunc(1, 1)
    end
    

    It's the only hacky workaround I can think of if I can't get the Dialog parser to behave.



    @Raduziel: Sure. Can you link me to / explain what exactly the limit is and how it is enforced? Or we can just call @kjeron, :p



    @OlvynChuru: I've attached the .pdb files for both BG:EE 2.5.17.0 and BG2:EE 2.5.16.6. (Thanks again Julius / devs!)

    OlvynChuruswitJuliusBorisov
  • RaduzielRaduziel Member Posts: 4,714
    @Bubb Looks like the game can handle "only" 256 kits. And some mods (*cough* Deities of Faerûn *cough*) will pass that mark.

    I don't know exactly the mechanics as I'm just the bricklayer, not the engineer so I'll second your call for @kjeron
  • kjeronkjeron Member Posts: 2,367
    edited April 2019
    The limitations are related to KITLIST.2da (all row# references are ignoring the first 3 header rows):
    • Starting with row# 256 / 0x100, the game will crash if it attempts to apply the specified CLAB file for that row. Now, class/kit setups that ignore their CLAB entry still work just fine(as well as they do in rows 0-255 at least) in these rows: hardcoded kits (8 specialists, barbarian, wildmage), multiclass kits, and mismatched kits (using a fighter kit on a thief).
    • Starting with row# 16385 / 0x4001, the game will still crash if it attempts to apply the specified CLAB file for that row. However, class/kit setups that ignore their CLAB entry do not work, but do not crash.
    • Starting with row# 32768 / 0x8000, the game will crash at startup if the file contains at least this many rows.

    There is another issue, that occurs during character generation for wizard spell-users:
    Regardless of a kit's unusability flags, character generation imposes the mage specialist restrictions on spells selection based on the kits row, adding them to any restrictions the kit would have.
    All kits occupying any row# containing BIT6 (0x40) are subject to Abjurer spell restrictions.
    All kits occupying any row# containing BIT7 (0x80) are subject to Conjurer spell restrictions.
    All kits occupying any row# containing BIT8 (0x100) are subject to Diviner spell restrictions.
    etc...
    These restrictions are cumulative. Row# 0x140 (448) cannot select spells restricted from Abjurers or Diviners. However, this breaks down once (3?4) or more are present, resulting in a random selection of spell schools being restricted, both at character generation and in-game, redetermined every time a game is loaded.
    BubbAndreaColomboRaduzielGrammarsalad
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    @Bubb, when it comes to DLG files I'm using EEex_LuaTrigger only for IWD2 skill checks that I'm porting to EE. These are implemented with Lua in order to take into account additional bonuses/penalties based on talker stats like Charisma etc. Currently I'm using it with 3 parameters, but 2 of them can be ommited without any functionality regression (object - in dialogs always LastTalkedToBy; skill name - I will just create dedicated function for each skill). So yeah, as long as the single function parameter in DLG files is supported, all is good. I'm using more in BCS files, but the problem is limited just to DLG. Having the bool value passed-back to the engine via a global, "EEex_LuaTrigger" also sounds good. Thanks!
    Post edited by swit on
    Bubb
  • BubbBubb Member Posts: 998
    edited April 2019
    @swit: Ok, EEex_LuaTrigger now uses the "EEex_LuaTrigger" global as the result. Hopefully all is good now :)



    @kjeron:

    256 limit => Arbitrary limit of CLAB files that the engine keeps track of. Can be expanded via some trickery. Crashes because of an unbounded memory read...

    16385 limit => What do you mean by "However, class/kit setups that ignore their CLAB entry do not work"? Also, I don't experience an engine crash in this range, as the kit must have an ids that includes 0x4000 for the lookup to occur.

    32768 limit => Engine treats both x and y sizes of 2DA files as signed words. Engine sees 32768+ as negative and crashes due to trying to allocate a negative amount of memory.

    Regarding mage specializations, I believe I could force the engine to do a straight comparison instead of bitwise tests.



    @Raduziel: I should be able to expand the kit upper-id to 0x4FFF fairly easily... I assume Deities of Faerûn will never exceed 4,054 custom kits? Heh...
    Grammarsaladswit
  • RaduzielRaduziel Member Posts: 4,714
    Bubb wrote: »
    @Raduziel: I should be able to expand the kit upper-id to 0x4FFF fairly easily... I assume Deities of Faerûn will never exceed 4,054 custom kits? Heh...

    First, thanks for translating to stupid. Apparently it is my native language.

    Second, I never thought that DoF would get more than 10 kits, so I'm not so sure lol.
  • kjeronkjeron Member Posts: 2,367
    edited April 2019
    Bubb wrote: »
    16385 limit => What do you mean by "However, class/kit setups that ignore their CLAB entry do not work"?Also, I don't experience an engine crash in this range, as the kit must have an ids that includes 0x4000 for the lookup to occur.
    I mixed this one up:
    The crashes for this range occurr as soon as the game starts (in Candlekeep), not during chargen, which is likely where I stopped testing before.
    Loading the auto-save it creates does work, without further crashing (so far as I've seen).
    Class/Kit setups that ignore their CLAB entry work the same as those in the 256-16384 range.
    Class/Kit setups that apply their CLAB entry ignore it, defaulting to the TRUECLASS clab files.
  • RaduzielRaduziel Member Posts: 4,714
    @Bubb Do you have an EEex discussion at G3?

    I'm asking because this kind of ninja technology usually is discussed there.
  • BubbBubb Member Posts: 998
    @Raduziel: I don't. I made an account a few months back, but I'm not familiar with the setting, and managing a discussion on two fronts might be difficult.

    I should make a thread, yes. Or would it warrant its own board? I have no idea how anything works over there.
  • RaduzielRaduziel Member Posts: 4,714
    I think that @swit and @ALIEN can answer those questions better than I would as I'm not familiar with that forum either.
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    Raduziel, @CamDawg or other gibberlings would be better suited to answer questions regarding G3, but of course everyone is welcome to register and create topics there. I'm the last person to give advices though, considering I'm also one of the guys that don't bother to manage a discussion on more than one front :) (notice that there are no official topics regarding my mods on Beamdog forums)


    I've been thinking more about the IWD2 style multiclassing system. EEex already allows us to set custom stats (which solves the task of storing level and class data in saves, in a format that is usable by opcodes that work with stats) and choose exactly which buttons are accessible in GUI, so it's easy to use vanilla Fighter/Mage/Cleric multi-class as a base for the new IWD2 style multi-class system (in order to have access to both mage and cleric spell books) and handle levelling via custom lua code, completely forgetting about vanilla system.

    This approach however has some drawbacks: item usability and triggers/actions that depend on one of the harcoded classes rather than combination of our choice. I'd like to ask about viability of following EEex features that would solve these limitations:

    1. edit opcode 319 to work with STAT.IDS (extended EEex entries). Alternatively a copy of this opcode with parameters working like opcode 318/324/326 (splprot.2da) - whatever would be easier to implement

    2. extended object selector in the form of: [EA.GENERAL.RACE.CLASS.SPECIFIC.GENDER.ALIGN.CLASSMSK] where CLASSMSK is a new bitwise stat that can be applied to a CRE file via opcode 401. The purpose of it is exactly the same as in IWD2, where the same feature exists - classes that character level up in are stored as a bitwise value instead of using 1 hardcoded ids entry (managing the stat value would be a job for mod that uses this functionality, EEex is needed only to allow expanded object selector functionality).

    This approach would allow us to implement multi-class system without redesigning dlg/bcs code logic (a matter of simple weidu code to patch trigger and actions that refers to class.ids, replacing all of them with expanded object selector and completely swapping dedicated triggers like 0x400C Class with stat checks). Interesting enough none of this would actually conflict with the vanilla system considering CLASSMSK stats increments could be added into CLAB files used by characters that follows vanilla classing and levelling system too. Of course weidu code that adds such system would have to be installed after any other mod to ensure full compatibility.

    What do you think about feasibility of these 2 EEex features, @Bubb?
    Post edited by swit on
    Bubb
  • GrammarsaladGrammarsalad Member Posts: 2,582
    edited April 2019
    If anything deserves to be hosted on gibberlings, it's this...
    BubbRaduzielAndreaColomboswit
  • BubbBubb Member Posts: 998
    edited April 2019
    @swit: Ok, I've looked into them both.

    1) Shouldn't be too hard. Just need to add a new mode to Param2 and then use Param1 as the splprot row. (Also, fun fact, the Power of the effect changes how it evaluates. Power = 1 means allow item use if condition holds, Power != 1 means allow item use if condition fails.)

    2) I can add another condition to the selector just fine. The problem is getting that argument into the selector code...

    If I were to extend the selector itself, I would have to extend both the BCS format and the internal CAIObjectType struct. That is 1) Very hard to pull off, and 2) Would break all existing BCS compilers :/

    Of course I'm thinking of a Lua workaround. I can make a script object that works exactly like an object selector, but uses a Lua function to verify if a creature matches the wanted criteria. So, it would loop through all creatures in the area and expose their actorID as a global. If that function returns true, then the engine would use that creature for the object.
    // Do I see a creature with 1 or more luck?
    See(EEex_MatchObject("B3StatGT(32,0)"))
    
    function B3StatGT(stat, greaterThan)
        return EEex_GetActorStat(EEex_MatchObjectID, stat) > greaterThan
    end
    

    Or, if that is too disruptive for you, I guess it might be possible to pass-in the CLASSMSK selector arg via a global, which would be set right before using an object selector. Like:
    EEex_CLASSMSK(42) // Tell the following selector to use "42" as the CLASSMSK
    See([1.1.1.1.1.1.1]) // Whatever selector you want
    



    Edit: And apparently I've already forgotten our Lua-in-dialog woes. The match function wouldn't return anything; it would set a global like "EEex_MatchObjectResult" to pass it back to the engine.
    AndreaColomboswit
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    Sounds very promissing, thanks! :)

    First proposed implementation definitely seems more versatile since, unlike the second one, it would work nested in OR blocks and it allows to keep the rest of the selector intact, just passing class as a separate function argument like this:
    Class(Myself,MAGE_ALL)
      OR(4)
        See([GOODCUTOFF.0.0.BARD_ALL.0.MALE])
        See([GOODCUTOFF.0.0.DRUID_ALL.0.MALE])
        See([GOODCUTOFF.0.0.CLERIC_ALL.0.MALE])
        See([GOODCUTOFF.0.0.MAGE_ALL.0.MALE])
    
    =>
    EEex_LuaTrigger("ClassMask('Myself','MAGE_ALL')")
      OR(4)
        See(EEex_MatchObject("ClassMask('[GOODCUTOFF.0.0.0.0.MALE]','BARD_ALL')"))
        See(EEex_MatchObject("ClassMask('[GOODCUTOFF.0.0.0.0.MALE]','DRUID_ALL')"))
        See(EEex_MatchObject("ClassMask('[GOODCUTOFF.0.0.0.0.MALE]','CLERIC_ALL')"))
        See(EEex_MatchObject("ClassMask('[GOODCUTOFF.0.0.0.0.MALE]','MAGE_ALL')"))
    

    No problems when it comes to compatibility with other mods (if the regexp conversion code is installed after), but yeah, the DLG limitations still applies here.


    Part of the problem with common class checks in DLG files could be handled for example by storing the CLASSMSK value also as a local variable, which could be checked via existing BitCheck trigger nested in TriggerOverride (or we could use new CheckStat trigger with value argument working bitwise if EEex would provide it).

    Class showing up as a part of object selector in DLG file sounds very unlikely (can't think about any use case for it - will check if any instance is present in vanilla games), but who knows what kind of crazy stuff will be present in game with tons of mods installed.

    btw. regarding the recently implemented lua functions that handle converting objects into IDs - can they also accept DV (script names) as an object string argument or are they restricted to object/object selectors?

    edit: example implementation of the above mentioned ClassMask lua function to be used with EEex_LuaTrigger and EEex_MatchObject:
    function ClassMask(object, class)
    	if type(class) == "string" then
    		if K4_classMaskTable[class] == nil then
    			Infinity_DisplayString("WARNING: unrecognized class argument: " .. class)
    			EEex_LuaTrigger = false
    			return false
    		else
    			class = K4_classMaskTable[class] --table storing associated array used to convert IDS style class strings into values
    		end
    	end
    	local actorID = EEex_EvalObjectAsActor(EEex_ParseObjectString(object), EEex_GetActorIDSelected())
    	if actorID ~= 0x0 then
    		if EEex_IsMaskSet(EEex_GetActorStat(actorID, 217), class) then
    			EEex_LuaTrigger = true
    			return true
    		end
    	end
    	EEex_LuaTrigger = false
    	return false
    end
    
    Post edited by swit on
    Bubb
  • BubbBubb Member Posts: 998
    @swit: Could a classmsk check not be done inside dialog via a EEex_LuaTrigger?

    It appears that the object eval functions don't work with straight script names... no idea why, it's what the engine itself uses. I'll look into it.

    Some things need to change with your Lua mockup:

    1) Evaluating an object selector and then checking the match won't work correctly. You would be failing the first time [GOODCUTOFF.0.0.0.0.MALE] matches, but the classmsk doesn't. (instead of continuing on to see if another creature satisfies the classmsk)

    2) Because of prob1, running traditional object selectors in the match function won't work. The match function itself has to act like the object selector. This is what it would look like:
    function ClassMask(object, class)
        if type(class) == "string" then
            if K4_classMaskTable[class] == nil then
                Infinity_DisplayString("WARNING: unrecognized class argument: " .. class)
                EEex_LuaTrigger = false
                return
            else
                class = K4_classMaskTable[class] --table storing associated array used to convert IDS style class strings into values
            end
        end
        -- EEex_ActorOfType() would check if the given actor matches the object criteria
        if EEex_ActorOfType(EEex_LuaTriggerActorID, EEex_ParseObjectString(object)) then
            if EEex_IsMaskSet(EEex_GetActorStat(EEex_LuaTriggerActorID, 217), class) then
                EEex_LuaTrigger = true
                return
            end
        end
        EEex_LuaTrigger = false
    end
    

    A EEex_LuaTrigger() that calls ClassMask() would use it on the calling creature.

    A EEex_MatchObject() that calls ClassMask() would use it like the object selector, checking every creature along the way against the EEex_LuaTrigger result of the function. Once it reaches a true result it will use that creature as the object.
    swit
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    thanks, that makes sense :)
    Could a classmsk check not be done inside dialog via a EEex_LuaTrigger?
    that would be a bit tricky considering there can be different objects used by Class trigger (for example LastTalkedToBy, Myself, Player1, Protagonist etc.) and with just 1 argument allowed, each of them would need dedicated function (or better yet, when I now think about it, to eliminate the unexpected objects problem having dedicated function for each class sounds like a better approach). Either way it’s indeed doable.

    As expected none of the DLG files in vanilla games use object selectors with nested Class inside, and the chance that some mod would add them is extremely slim (can't think about any use case for it in DLG files compared to Class triggers).

    I think stuff discussed up to this point will be enough to implement D&D 3rd ed. multi-class system that maintains compatibility with other mods.

    edit: yep, DLG dedicated functions like these that calls proper one will do just fine:
    function ClassMask_MONK(object)
    	ClassMask(object, "MONK")
    end
    
    Post edited by swit on
    Bubb
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    I've found 1 more limitation when it comes to above discussed bitwise class checks - the feature requested by @subtledoctor (I think): https://support.baldursgate.com/issues/25886
    'While Equipped' timing doesn't work when effect is triggered via opcode 326, so it can't be used instead of 177 for redirecting global effects applied to items.
    Grammarsalad
  • BubbBubb Member Posts: 998
    Can you make up an example of how that theoretically should work? I don't quite understand the setup.
  • kjeronkjeron Member Posts: 2,367
    Bubb wrote: »
    Can you make up an example of how that theoretically should work? I don't quite understand the setup.
    What's desired, is an effect to apply an EFF file based on SPLPROT conditions, that functions like op183.
    Op183 is applied to the target for it's stated duration regardless of the itemtype condition. The EFF file it specifies is applied while the itemtype is equipped.
    The desired effect would be applied regardless of the SPLPROT condition for it's stated duration, while the EFF file it specifies is applied only while the SPLPROT condition is true.
    BubbGrammarsalad
  • switswit Member, Translator (NDA) Posts: 495
    edited April 2019
    Sure thing, see for example AMUL18. Let's say the global bonuses of that amulet should be applied only to druid class:
    COPY_EXISTING ~AMUL18.ITM~ ~override~
    	LPF ALTER_EFFECT INT_VAR match_opcode = 177 parameter1 = 11 /*DRUID*/ parameter2 = 5 /*CLASS.IDS*/ END
    
    Notice that both of these effects use 'While Equipped' timing even though they are applied externally via eff files.

    My initial idea was to redirect all opcodes that can reference CLASS.IDS entries, but don't work with SPLPROT.2DA yet, using opcode 326 if needed, but this can't be done in this hypothetical example since 'While Equipped' timing doesn't work when effect is applied by opcode 326.

    In other words the feature requested for opcode 319 would be also needed for 177 to make it 100% reliable.

    edit: ninja'd by kjeron :)
    Post edited by swit on
    Bubb
  • The user and all related content has been deleted.
    Bubb
  • BubbBubb Member Posts: 998
    @swit: I've got the Opcode #319 modification up:

    Power = 2 enables SPLPROT mode; Power = 3 enables SPLPROT mode, but in "cannot use" mode.
    Param1 is SPLPROT value, Param2 is SPLPROT row.

    It was a little trickier than I first thought, so it might be wise to test it to see if it is actually working before you start using it. :)

    The other requests will probably take me a few more days to get done.
    RaduzielGrammarsaladswitTimbo0o0o0
  • GrammarsaladGrammarsalad Member Posts: 2,582
    Bubb wrote: »
    @swit: I've got the Opcode #319 modification up:

    Power = 2 enables SPLPROT mode; Power = 3 enables SPLPROT mode, but in "cannot use" mode.
    Param1 is SPLPROT value, Param2 is SPLPROT row.

    It was a little trickier than I first thought, so it might be wise to test it to see if it is actually working before you start using it. :)

    The other requests will probably take me a few more days to get done.

    Wait, do I understand that with power set to 2 or 3, this allows the assigning of usability based on splprot?!?
  • BubbBubb Member Posts: 998
    @Grammarsalad: Correct! :)
    OlvynChuruGrammarsaladswitRaduziel
  • SeritySerity Member Posts: 16
    Is there a way to get the current-casts-left for Innate spells/abilities, such as Set Snare, etc? GetKnown/MemorizedInnateSpells doesn't seem to have this info.

    I don't see the info in EEKeeper/NearInfinity either, although the save obviously has to store it..
    Bubb
  • SeritySerity Member Posts: 16
    edited May 2019
    Bubb wrote: »
    EEex_GetMemorizedInnateSpells() and the variants for the other spell types can help you grab this info. Though, as I was testing it myself, I found that both the memorization functions and many of the bits utility functions were broken :#
    Thanks boss, works exactly as I had hoped. <3

    mcvlnva.png

    (...although the names are hardcoded because I can't figure out how to translate resref => name,description x_x)

    Also, strangely, the icons that are shown in spell.icon is not the same as the icons used in the actionBar(?). I'm having to change "XXXXXXXC" to "XXXXXXXB", although I think this may be a problem with the base game? Hardiness uses "SPCL907C.bam" as its spell icon but that file doesn't exist (causing NearInfinity to say 'no spell icon', even) so I have to change the ending C=>B. For comparison purposes, I also have to do that - comparing spell.icon to buttonArray:GetButtonBam(x), since the buttonArray uses B-type icons (wow what a thoroughly bug-free and error-proof way of activating skills).
    BubbGrammarsalad
Sign In or Register to comment.