Jump to content

best way to search through .BCS scripts?


Recommended Posts

I want to search through every script for a particular trigger, and change it. This will likely be in about six scripts out of several thousand. Doing a mass DECOMPILE_AND_PATCH / REPLACE_TEXTUALLY seems incredibly wasteful if most scripts won't have hits. Is there an effective way to search scripts for a string and make a short list of positive hits, and then only decompile the ones on the list?

Link to comment

Tweaks does it:

 
  COPY_EXISTING_REGEXP GLOB ~^.+\.bcs$~ ~override~
    DECOMPILE_AND_PATCH BEGIN
      PATCH_PHP_EACH cd_stackables AS item => stack BEGIN
        PATCH_IF stack < 2 BEGIN
          REPLACE_TEXTUALLY ~TakePartyItem("%item%")~ ~TakePartyItemNum("%item%",1)~
        END  
      END
    END
    BUT_ONLY IF ~116OB~

The key bit is the IF ~116OB~ in the closing predicate. The action I'm looking for is 116 and (for whatever reason) that's always followed by OB in a compiled script. In NI, you can view the 'script code' tab to try and find something there to use as a key.

edit: looks like triggers are the trigger's decimal value and always at the beginning of a line, so IF ~^16399~ would work for something like a Global() trigger.

Edited by CamDawg
Link to comment
4 hours ago, subtledoctor said:

I want to search through every script for a particular trigger, and change it. This will likely be in about six scripts out of several thousand. Doing a mass DECOMPILE_AND_PATCH / REPLACE_TEXTUALLY seems incredibly wasteful if most scripts won't have hits. Is there an effective way to search scripts for a string and make a short list of positive hits, and then only decompile the ones on the list?

You didn't say what exactly you want to find and replace. If it's possible to search the raw bcs files and only decompile and patch on a successful hit, it can greatly improve performance.

Link to comment

I want to replace incidences of

CheckStat("Myself",1,"CLERIC_REGENERATION")
CheckStatGT("Myself",0,"CLERIC_REGENERATION")

with

CheckSpellState("Myself",[regeneration spellstate added by my mod)]

...to free up stat 124 for use as a proficiency. (Stat 124 is used very rarely, near as I can tell it is only used by e.g. Morpheus' Powergaming Scripts to prevent regeneration spells being cast for healing if the caster is already regenerating. A spellstate is perfectly well suited for that purpose. EDIT - or even local variables? Why use up spellstates or stats for this sort of thing? Why not apply a local variable via op187, for the duration of the regeneration spell?))

5 hours ago, argent77 said:

If it's possible to search the raw bcs files and only decompile and patch on a successful hit, it can greatly improve performance.

I guess that's exactly what I'm asking how to do - I don't know how to search a raw bcs file.

Edited by subtledoctor
Link to comment

Try this:

OUTER_SET stats_val = IDS_OF_SYMBOL("stats" "CLERIC_REGENERATION")
OUTER_SPRINT reg_params "[-0-9]+ [-0-9]+ %stats_val%"
COPY_EXISTING_REGEXP ".+\.bcs" "override"
  PATCH_IF (INDEX_BUFFER("^TR[%WNL%]1645[23] %reg_params%") >= 0) BEGIN  // CheckStat() and CheckStatGT()
    DECOMPILE_AND_PATCH BEGIN
      // find and replace...
    END
  END
BUT_ONLY

Change "1645[23]" to "1645[234]" if you want to include CheckStatLT() in the test as well.

You can easily compare decompiled and compiled script code in NI. IESDP provides the syntax for the bytecode.

Link to comment
6 hours ago, subtledoctor said:

I want to replace incidences of

CheckStat("Myself",1,"CLERIC_REGENERATION")
CheckStatGT("Myself",0,"CLERIC_REGENERATION")

with

CheckSpellState("Myself",[regeneration spellstate added by my mod)]

...to free up stat 124 for use as a proficiency. (Stat 124 is used very rarely, near as I can tell it is only used by e.g. Morpheus' Powergaming Scripts to prevent regeneration spells being cast for healing if the caster is already regenerating. A spellstate is perfectly well suited for that purpose. EDIT - or even local variables? Why use up spellstates or stats for this sort of thing? Why not apply a local variable via op187, for the duration of the regeneration spell?))

I guess that's exactly what I'm asking how to do - I don't know how to search a raw bcs file.

If I am the only issue, then I can create a new spellstate and track using that. Easy peasy and can probably have that done tonight. Spellstates are used in case the main spell gets dispelled where local variables will not get updated. Not sure if any regeneration can be debuffed, but I prefer using spellstates when tracking spells.

Link to comment
4 minutes ago, morpheus562 said:

If I am the only issue, then I can create a new spellstate and track using that. Easy peasy and can probably have that done tonight. Spellstates are used in case the main spell gets dispelled where local variables will not get updated. Not sure if any regeneration can be debuffed, but I prefer using spellstates when tracking spells.

I don't think your mod is the only issue, though I think your scripts probably make more use of this particular one. You can use a spellstate _IF FILE_EXISTS_IN_GAME ~d5__combat_skills.d5~...  that way your mod alone will use the stat, but it will get switched to a spellstate if my mod is installed, regardless  of install order. (Or you can just use a spellstate regardless of that condition, since it will work just as well and involve less code for you.)

We should just coordinate as to the name  of the spellstate. I  recommend (wait for it)... "CLERIC_REGENERATION"  ;)

Link to comment

As a follow up, do you have a list of the regeneration spells and abilities that will need to be updated? I'm sure mods add a few more too that will need to be updated.

I think I can just iterate through all spells and anything with the old value gets removed and replaced with the new spellstate. I can get this built out tonight if you want to copy it for your stuff.

Edited by morpheus562
Link to comment

I don't look for regeneration spells, I just look for spells that use op233 to set stat 124.

Here's my code to generate the new spellstate:

//ADD SPELLSTATE MACRO______________________________________________________________
//
DEFINE_ACTION_FUNCTION d5_resolve_state STR_VAR new_state_id = ~blah~ RET new_state_ind BEGIN
  OUTER_SET state_ind = IDS_OF_SYMBOL (~splstate~ ~%new_state_id%~)
  ACTION_IF !(state_ind = 0 - 1) BEGIN
    OUTER_SET new_state_ind = state_ind
  END
  ACTION_IF (state_ind = 0 - 1) BEGIN
    OUTER_SET new_state_ind = 0
    COPY_EXISTING ~splstate.ids~ ~override~
      READ_2DA_ENTRIES_NOW rows 2
      PATCH_IF (rows < 256) BEGIN
        SET found = 0
        FOR (row = 1; row < rows; ++row) BEGIN
          PATCH_IF (found = 0) BEGIN
            READ_2DA_ENTRY_FORMER rows row 0 ind
            READ_2DA_ENTRY_FORMER rows row 1 state
            SET poss_ind = (ind + 1)
            PATCH_IF (poss_ind < 256) BEGIN
              LOOKUP_IDS_SYMBOL_OF_INT poss_state ~splstate~ poss_ind
              PATCH_IF (~%poss_state%~ STRING_EQUAL_CASE ~%poss_ind%~) BEGIN
                SET found = 1
                SET new_state_ind = poss_ind
              END
            END
          END
        END
      END
    BUT_ONLY
    ACTION_IF (new_state_ind > 0) BEGIN
      APPEND ~splstate.ids~ ~%new_state_ind%  %new_state_id%~
    END
  END
END 

LAF d5_resolve_state STR_VAR new_state_id = ~CLERIC_REGENERATION~ RET new_state_ind END
OUTER_SET regeneration_state = %new_state_ind%

And this is my test code:

//free up stat 124___________________________________________________________________
//
COPY_EXISTING_REGEXP GLOB ~^.+\.spl$~ ~override~
  GET_OFFSET_ARRAY ab_array SPL_V10_HEADERS
  SET remove_this = 0
  PHP_EACH ab_array AS int => ab_off BEGIN
	GET_OFFSET_ARRAY2 fx_array ab_off SPL_V10_HEAD_EFFECTS
	PHP_EACH fx_array AS int => fx_off BEGIN
	  READ_SHORT fx_off fx_type
	  READ_SHORT (fx_off + 0x08) fx_prof
	  PATCH_IF (fx_type = 233) AND (fx_prof = 124) BEGIN
	    SET remove_this = 1
	  END
	END
  END
  PATCH_IF (remove_this = 1) BEGIN
    LPF DELETE_EFFECT INT_VAR match_opcode = 233 match_parameter2 = (124 + (0x10000 * 1)) END
    LPF DELETE_EFFECT INT_VAR match_opcode = 233 match_parameter2 = 124 END
  END
BUT_ONLY


COPY_EXISTING_REGEXP GLOB ~^.+\.itm$~ ~override~
  GET_OFFSET_ARRAY ab_array ITM_V10_HEADERS
  PHP_EACH ab_array AS int => ab_off BEGIN
	GET_OFFSET_ARRAY2 fx_array ab_off ITM_V10_HEAD_EFFECTS
	PHP_EACH fx_array AS int => fx_off BEGIN
	  READ_SHORT fx_off fx_type
	  READ_SHORT (fx_off + 0x08) fx_prof
	  PATCH_IF (fx_type = 233) AND (fx_prof = 124) BEGIN
	    SET remove_this = 1
	  END
	END
  END
  PATCH_IF (remove_this = 1) BEGIN
    LPF DELETE_EFFECT INT_VAR match_opcode = 233 match_parameter2 = (124 + (0x10000 * 1)) END
    LPF DELETE_EFFECT INT_VAR match_opcode = 233 match_parameter2 = 124 END
  END
BUT_ONLY

Just need to replace DELETE_EFFECT with ALTER_EFFECT and change it fro 233 to 328 to set %regeneration_state%

 

Edited by subtledoctor
Link to comment
15 hours ago, subtledoctor said:

Why not apply a local variable via op187, for the duration of the regeneration spell?

Because all forms of "set variable" essentially ignore the duration mode and are permanent once applied. They're not stored as effects that can even have an expiration time, so the only way to set "for the duration" is to unset when the duration expires. Which is prone to failure when stuff like resurrection gets involved.

Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...