Jump to content

DavidW

Gibberlings
  • Posts

    7,984
  • Joined

  • Last visited

Posts posted by DavidW

  1. I think that approach will cause trouble:

    (i) you'll have to hardcode character abilities into the item, which is compatibility-unfriendly

    (ii) taking the object off and back on again will recharge the ability.

    My immediate thought is to get the item to set a spellstate. Then edit the spell to block it, and display a 'this power can't be used while wearing blah' message, when the spellstate is set.

  2. Well, just being Turing-complete, by itself, isn't enough to make a programming language fit for a given purpose. Turing machines are Turing-complete, but I'm not planning to mod with them! My point was that WEIDU actually is a fairly powerful programming language, in which you can do most of the things you need for high-level programming.

    The actual Turing machine implementation was a joke, mostly. (Obviously WEIDU is Turing complete - all you need for that is variables and basic control flow, and it obviously has those.) But the fact that I can do a fast smooth implementation of a Turing machine in a few dozen lines of WEIDU code and in twenty minutes of my time does provide a reasonable demonstration that WEIDU can easily be used for abstract programming and not just as a low-level interface into IE files.

  3. 29 minutes ago, Taylan said:

    I still think a language like Python would be beneficial in the long term, but to each their own. We'll have to see if my project goes anywhere; it depends mostly on whether I actually find enough time and energy to put into it.

    Oh, you might well be right (though the backwards-compatibility issues are serious). WEIDU is a lot more powerful than it looks, and I actually really enjoy programming in it, but I don't think anyone thinks it's the language that you'd build from scratch for modding.

     

  4. A somewhat tongue-in-cheek response to the discussion of WEIDU on this thread:

    A WEIDU implementation of a Turing machine, using the conventions here:

    Spoiler

    DEFINE_ACTION_FUNCTION turing_machine
        INT_VAR display_steps=0
        STR_VAR initial_tape=""
                machine=""
    BEGIN
        // initialise array, just in case
        ACTION_PHP_EACH turing_tape AS k=>v BEGIN
            OUTER_SET $turing_tape("%k%")=0
        END
        // read the initial_tape into the turing_tape array
        OUTER_PATCH "%initial_tape%" BEGIN
            FOR (ind=0;ind<BUFFER_LENGTH;++ind) BEGIN
                READ_ASCII ind val (1)
                SET $turing_tape("%ind%")=val
            END
        END
        // put the pointer at zero
        OUTER_SET pointer=0
        // initialise the machine
        OUTER_SPRINT state "A"
        // start the counter
        OUTER_SET counter=0
        OUTER_WHILE !("%state%" STRING_EQUAL "HALT") BEGIN
            // increment the counter
            OUTER_SET counter+=1
            // get the current state of the tape (0=blank)
            ACTION_IF VARIABLE_IS_SET $turing_tape("%pointer%") BEGIN
                OUTER_SET tape=$turing_tape("%pointer%")
            END ELSE BEGIN
                OUTER_SET tape=0
            END
            // run the machine
            LAF "%machine%" INT_VAR tape STR_VAR state RET state mark_tape move END
            // mark the tape
            OUTER_SET $turing_tape("%pointer%")=mark_tape 
            // move the pointer
            ACTION_MATCH "%move%" WITH
            L BEGIN
                OUTER_SET pointer +=1
            END
            R BEGIN
                OUTER_SET pointer -=1
            END
            DEFAULT
                FAIL "%move% is an illegal move output from Turing machine %machine%"
            END
            // display the tape and machine 
            ACTION_IF display_steps BEGIN
                OUTER_SPRINT display ""
                ACTION_SORT_ARRAY_INDICES turing_tape NUMERICALLY
                ACTION_PHP_EACH turing_tape AS ind=>data BEGIN
                    OUTER_SPRINT display "%display%%data%"
                END
                PRINT "Step %counter%: Machine state: %state%; Tape state: %display%"
            END
        END
        PRINT "Turing machine %machine%, with input %initial_tape%, halted after %counter% steps"
    END

    You use it like this:

    LAF turing_machine INT_VAR display_steps=1 STR_VAR machine=my_machine END

    'my_machine' is an action function encoding the machine's lookup table. Here's an example:

    Spoiler

    DEFINE_ACTION_FUNCTION busy_beaver
        INT_VAR tape=0
        STR_VAR state=""  
        RET state
            mark_tape
            move
    BEGIN
            ACTION_MATCH "%state%" WITH
            A BEGIN
                OUTER_SET mark_tape=1
                ACTION_IF tape=0 BEGIN
                    OUTER_SPRINT state B
                    OUTER_SPRINT move R
                END ELSE BEGIN
                    OUTER_SPRINT state C
                    OUTER_SPRINT move L            
                END
            END
            B BEGIN
                OUTER_SET mark_tape=1
                ACTION_IF tape=0 BEGIN
                    OUTER_SPRINT state A
                    OUTER_SPRINT move L
                END ELSE BEGIN
                    OUTER_SPRINT state B
                    OUTER_SPRINT move R    
                END        
            END
            C BEGIN
                OUTER_SET mark_tape=1
                ACTION_IF tape=0 BEGIN
                    OUTER_SPRINT state B
                    OUTER_SPRINT move L
                END ELSE BEGIN
                    OUTER_SPRINT state HALT
                    OUTER_SPRINT move R
                END                
            END
            DEFAULT
                FAIL "%state% is an illegal internal state for busy_beaver"
            END
    END

     

  5. 8 hours ago, Taylan said:

    @kjeron Why not use SNDSLOT.IDS when it tells me which symbolic name corresponds to which integer index? No need to create one's own mappings. A sound set can define a WAV file for BATTLE_CRY1, SELECT_COMMON1, etc., and it will be automatically translated to the correct integer index based on the SNDSLOT.IDS of the game for which you're installing the sound set.

    The issue is that the game engine (mostly) doesn't care what the symbolic names are. Take class.ids - it assigns a numeric value to each class, so that class 1 is labeled MAGE and so forth, but the engine (mostly) doesn't store or manipulate the symbolic names, only the numeric values. If you changed 'MAGE' to 'WIZARD' in class.ids, it would (mostly) leave the game unaffected.

    (The 'mostly' caveat is because dialog files store their fragments of script uncompiled and compile it at run-time. So if there's a trigger in a dialog that checks if you're a mage, that change will confuse it.)

    So if you change the label on sndslot 9 (currently BATTLE_CRY1) to SELECT_COMMON1, it won't make any difference to how the sound in slot 9 is used by the engine; it'll just mean it's labelled misleadingly. More seriously, if IWDEE uses BATTLECRY1 rather than BATTLE_CRY1, you still want your first battle-cry to be put in that slot.

    Usually the various EE games are consistent across their ids files so that you can write multi-game code that refers to the ids symbols, at least if we're talking about BG2EE, BGEE, and IWDEE, all of which use the same engine. But there are exceptions, and I don't have much experience modding PSTEE, which has a different version of the engine. (My code above was to make a general point about WEIDU capabilities, not actually to thrash out what a working soundset program would be.) My advice here would probably be to standardize on the BG2EE version of sndslot.ids and just read the symbolic names in from a local copy in your mod folder rather than relying on the in-game version. In WEIDU I'd just read them into an array; I assume you can do something similar in Python (which I think is probably Turing-complete).

  6. I'll stop after this, but here's a direct implementation of Taylan's design spec, in WEIDU rather than Python (again helping myself to a couple of auxiliary functions):

    Spoiler

    DEFINE_ACTION_FUNCTION add_soundset
        STR_VAR path=""
    BEGIN

    // # Initialize our theoretical WeiDU library
    // # This would do some magic to find the game files and such
    INCLUDE "%MOD_FOLDER%/lib/sfo_lite.tph"

    // # Define mapping from human-readable sound IDs to game-specific integers
    // no need: they're in sndslot.ids and WEIDU can read them directly

    // # Read the definitions for the sound set
    ACTION_FOR_EACH category IN config sounds subs BEGIN
        LAF parse_ini STR_VAR file="%path%/definitions.txt" category RET_ARRAY "%category%"=ini_array END
    END

    OUTER_SPRINT name $config("name")

    // # Copy the WAV files to where they belong
    ACTION_PHP_EACH sounds AS k=>file BEGIN
        COPY "%path%/%file%" override
    END

    // # In this dictionary we will save mappings from WAV file name to string reference
    ACTION_CLEAR_ARRAY strRefs

    // # Open the TLK file for our language
    // no need, WEIDU opened it automatically

    ACTION_PHP_EACH subs AS wav=>string BEGIN
      // # Add the string to the game, receiving its numeric ref
      // # Remember what WAV file name it belongs to
      OUTER_PATCH_SAVE resref "%wav%" BEGIN REPLACE_TEXTUALLY "\..*" "" END // strip the .wav
      OUTER_SET $strRefs("%wav%")=RESOLVE_STR_REF("%string%" ["%resref%"])
    END

    // # Save the modified TLK file
    // no need to do this in WEIDU

    // # Let's say the function parse2DA gives us a "2DA object"
    COPY_EXISTING "charsnd.2da" override

    // # We will build up a new column to add to the 2DA
         LPF add_blank_column STR_VAR header="%name%" entry="-1" END

    // # The 2DA object lets us get the rows via the rows property
    // # And each row in turn has a cols property
    // WEIDU does this slightly differently
        
        COUNT_2DA_COLS colcount
        COUNT_2DA_ROWS colcount rowcount
        FOR (row=0;row<rowcount;++row) BEGIN
        
      // # The sound's game-specific integer id is in the first column
            READ_2DA_ENTRY row 0 colcount intId
      // # Get the human-readable sound ID for that integer
            LOOKUP_IDS_SYMBOL_OF_INT soundId sndslots intId
      // # Get the WAV file name for that sound ID
            SPRINT filename $sounds("%soundId%")
      // # Get the string ref for that WAV file name
            strRef = $strRefs("%filename%")
      // # Add that to the column we're building
            SET_2DA_ENTRY row (colcount - 1) colcount "%strRef%"
      
        END
      
    // # The function .addColumn takes a column title and the list of values for it
    // we don't need this in WEIDU: we've been working directly with the file

    // # Install the modified 2DA file into the game directory
    // That's happening automatically
        PRETTY_PRINT_2DA

    END

     

  7. I feel the need to defend the honor of WEIDU.

    Here's how I'd code this task in WEIDU, assuming exactly the input format for descriptions.txt in Taylan's post:

     

    Spoiler

    DEFINE_ACTION_FUNCTION install_custom_soundset
        STR_VAR soundset_path=""
                soundset_definitions="definitions.txt"
                file_path="%soundset_path%" // default to same location unless overwritten
    BEGIN

        // parse the definitions
        LAF parse_ini STR_VAR file="%soundset_path%/%soundset_definitions%" RET_ARRAY ini_array END
        
        // copy over the files, and get an array of tlk entries to add to the table
        ACTION_PHP_EACH ini_array AS k=>v BEGIN
            ACTION_IF FILE_EXISTS "%file_path%/%v%" BEGIN
                //install it
                COPY "%file_path%/%v%" "override"
                // get its non-suffix name
                OUTER_PATCH_SAVE resref "%v%" BEGIN 
                    REPLACE_TEXTUALLY EXACT_MATCH ".wav" "" 
                END
                // get its associated text line, if any
                ACTION_IF VARIABLE_IS_SET $ini_array("%v%") BEGIN
                    OUTER_SPRINT text_line $ini_array("%v%")
                END ELSE BEGIN
                    OUTER_SPRINT text_line ""
                END
                // add to dialog.tlk
                OUTER_SET strref=RESOLVE_STR_REF ("%text_line%" [%resref%])
                // get the number for the command type
                OUTER_SET code=IDS_OF_SYMBOL (sndslot "%k%")
                OUTER_SET $soundset_write_array("%code%")=strref
            END
        END

        // add the entries
        
        LAF add_blank_column STR_VAR 2da="charsnd.2da" header="%ini_array_name%" entry="-1" END
        COPY_EXISTING "charsnd.2da" override
            COUNT_2DA_COLS colcount
            FOR (row=0;row<rowcount;++row) BEGIN
                READ_2DA_ENTRY row 0 colcount row_number
                PATCH_IF VARIABLE_IS_SET $soundset_write_array("%row_number%") BEGIN
                    v=$soundset_write_array("%row_number%")
                    SET_2DA_ENTRY row (colcount - 1) colcount v
                END
            END
        BUT_ONLY

    END

    It appeals to a couple of subsidiary functions which I haven't defined; they'd be about 5-10 lines long each, I think. It took about 20 minutes to code, and it's about the same length as Taylan's Python script (and without appealing to a hypothetical library of weidu functions). The syntax is a little more cumbersome than python, to be sure.

    WEIDU isn't perfect, but it's not just nominally Turing-complete; it's a reasonably effective programming language, even if 95% of mods don't use it that way. Would it be nice to have object-orientation, anonymous functions, more sophisticated handling of arrays as values, or a less idiosyncratic syntax? Sure. But it's way more powerful and flexible than it gets credit for.

  8. @Angel: the only caveat I should give is that I have tattered shards of my own rewrite locally, so might not be able to incorporate third-party fixes very directly. (This is what happened for v31/v32 of SCS: CamDawg put together v31; v32 was built on v30 code with some manual incorporation of things from v31.)

  9. Yes. (It reflects the fact that the party are teleported in without warning, so it's implausible for the chess pieces to prebuff.)

    This is externalized to stratagems\mage\override\[bg1/bg2]\boolean.2da. Setting the Boolean DoNotPrebuff turns off buffing. You'll see that in BG1 it applies to the chess pieces, to a couple of dopplegangers, to Silke in Beregost, and (iirc) to a couple of adventurers you can meet abruptly in Baldur's Gate.

     

  10. Don't overestimate SCS's AI. It's not clever enough to do a relative-armor check on spellcasters. Mostly it's working with a class-based priority list: if for the particular attack we're dealing with that priority list has cleric>bard, clerics will be prioritized. 

    If you think that's anomalous, and it would be better to do bard>cleric, I can tweak it. (Sometimes, e.g. when we're explicitly targeting mages, it's that way round already.)

×
×
  • Create New...