subtledoctor Posted October 10, 2022 Share Posted October 10, 2022 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? Quote Link to comment
CamDawg Posted October 10, 2022 Share Posted October 10, 2022 (edited) 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 October 10, 2022 by CamDawg Quote Link to comment
argent77 Posted October 10, 2022 Share Posted October 10, 2022 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. Quote Link to comment
subtledoctor Posted October 10, 2022 Author Share Posted October 10, 2022 (edited) 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 October 10, 2022 by subtledoctor Quote Link to comment
argent77 Posted October 10, 2022 Share Posted October 10, 2022 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. Quote Link to comment
subtledoctor Posted October 10, 2022 Author Share Posted October 10, 2022 Ha ha okay yeah, I would never have come up with something like that on my own. Just to clarify: I know that stat I want to check is 124. So that 2nd line could be: OUTER_SPRINT reg_params "[-0-9]+ [-0-9]+ 124" Right? (A space after each plus sign?) Quote Link to comment
argent77 Posted October 10, 2022 Share Posted October 10, 2022 10 minutes ago, subtledoctor said: Just to clarify: I know that stat I want to check is 124. So that 2nd line could be: OUTER_SPRINT reg_params "[-0-9]+ [-0-9]+ 124" Right? (A space after each plus sign?) Yes, that looks fine. Quote Link to comment
Guest morpheus562 Posted October 10, 2022 Share Posted October 10, 2022 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. Quote Link to comment
subtledoctor Posted October 10, 2022 Author Share Posted October 10, 2022 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" Quote Link to comment
Guest morpheus562 Posted October 10, 2022 Share Posted October 10, 2022 Sure thing. If not your mod, I'll make my own spellstate version of CLERIC_REGENERATION, and if your mod is installed I'll just piggyback off CLERIC_REGENERATION. Quote Link to comment
Guest morpheus562 Posted October 10, 2022 Share Posted October 10, 2022 (edited) 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 October 10, 2022 by morpheus562 Quote Link to comment
subtledoctor Posted October 10, 2022 Author Share Posted October 10, 2022 (edited) 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 October 10, 2022 by subtledoctor Quote Link to comment
jmerry Posted October 11, 2022 Share Posted October 11, 2022 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. Quote Link to comment
Guest morpheus562 Posted October 11, 2022 Share Posted October 11, 2022 AI is updated on my end. I have some other things to do, but should be good to go whenever you release. Quote Link to comment
subtledoctor Posted October 11, 2022 Author Share Posted October 11, 2022 17 hours ago, jmerry said: Because all forms of "set variable" essentially ignore the duration mode and are permanent once applied Ah. The IESDP says they all use “instant” timing but that doesn’t preclude mode 0 “instant/limited.” Could stand to be clarified… Quote Link to comment
Recommended Posts
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.