Jump to content
DavidW

A family of BCS-editing functions

Recommended Posts

I've been irritated for a while by how difficult it is to do fine-grained editing of existing BCS files. There are no very good options:

- REPLACE_BCS_BLOCK is brittle, breaks easily, and only allows for wholesale replacement

- Search-and-replace is insensitive to the file structure and often too crude

- SSL is fine for writing complex scripts from scratch, but no good for editing existing scripts.

This collection of five functions is an attempt to solve that problem. I'll describe them one at a time in the thread below. The library is alter_script.tpa, and can be found here in the current SCS repo.

Share this post


Link to post

DELETE_SCRIPT_BLOCK (INT_VAR only_once (default=0), STR_VAR script, match, match1, match2, match3, match4, match5)

Set 'script' to the script you want to edit. Set 'match' to some string that appears only in the blocks you want to delete, and all blocks containing that string will be deleted. (Set only_once to 1 and only the first block will be deleted.) If you want finer-grained identification, set any of match1-match5 to some string; the only blocks deleted will be those that match all strings that you set.

Example: VAMPANO.bcs contains a rudimentary use of vampire Domination, which it's convenient to remove on SCS.

LAF DELETE_SCRIPT_BLOCK STR_VAR script=vampano match=VAMPIRE_DOMINATION END

Share this post


Link to post

ALTER_SCRIPT_BLOCK (INT_VAR only_once, STR_VAR match,match1-match5, swap_out, swap_in, swap_out1-swap_in5, swap_in1-swap_in5)

Blocks are matched as in DELETE_SCRIPT_BLOCK. Then swap_out is replaced with swap_in (and swap_out1 with swap_in1, etc) in any block that's matched.

Example: the EE summons AI script bdsum00 has a bug that causes it to stutter whenever its target goes invisible, because its attack blocks don't contain a See(LastSeenBy(Myself)) check. 

LAF ALTER_SCRIPT_BLOCK STR_VAR script=bdsum00 match="AttackOneRound" swap_out="THEN" swap_in="See(LastSeenBy(Myself)) THEN" END

Share this post


Link to post

INSERT_SCRIPT_BLOCK (INT_VAR insert_above (default=0), only_once, STR_VAR script match, match1-match5, insert)

'insert' needs to be a path to a BAF file. Blocks are matched as in DELETE_SCRIPT_BLOCK. The BAF code in 'insert' is then inserted below any block that matches. (Set insert_above=1 to have it inserted above instead.)

Example: Suppose (recapitulating Ascension) that you want to add a 'reinforcements' block to the area script of Gromnir's throne room, to appear above the block that grants XP. 

<<<<<<<< .../ascension-inline/gromnir_insert.baf
[the script]
>>>>>>>>

   LAF INSERT_SCRIPT_BLOCK
     INT_VAR insert_above=1
     STR_VAR script=ar5002
            match=~SetGlobal("AddXP","AR5002",1)~
            insert=".../ascension-inline/gromnir_insert.baf"
   END

Share this post


Link to post

REPLACE_SCRIPT_BLOCK (INT_VAR only_once, STR_VAR script, match, match1-match5, insert)

'insert' needs to be a path to a BAF file. Blocks are matched as in DELETE_SCRIPT_BLOCK. The BAF code in 'insert' then replaces any matched blocks.

 

Share this post


Link to post

CLONE_SCRIPT_BLOCK (INT_VAR only_once, insert_above, STR_VAR script, match, match1-match5, swap_in, swap_in1-swap_in5, swap_out, swap_out1-swap_out5, original_swap_in, original_swap_in1-original_swap_in5, original_swap_out, original_swap_out1-original_swap_out5)

Blocks are matched as in DELETE_SCRIPT_BLOCK. Matched blocks are copied, then text swaps are carried out on the new block as per ALTER_SCRIPT_BLOCK, and on the old block as per ALTER_SCRIPT_BLOCK but using original_swap_in and original_swap_out.

Example (again, recapitulating Ascension): suppose you want to alter the epilogues so that Sarevok gets different epilogues depending on his alignment. 

   LAF CLONE_SCRIPT_BLOCK
      STR_VAR script=ar6200
              match=~"sarevnd"~
              swap_out=~Global("SarevokBio","GLOBAL",0)~
              swap_in=~Alignment("Sarevok",MASK_EVIL)Global("SarevokBio","GLOBAL",0)~
              swap_out1=~"sarevnd"~
              swap_in1=~"sarevnd2"~
              original_swap_out=~Global("SarevokBio","GLOBAL",0)~
              original_swap_in=~!Alignment("Sarevok",MASK_EVIL)Global("SarevokBio","GLOBAL",0)~
   END

Share this post


Link to post

For advanced users: you can also do functional matching and patching.

- All these functions take an additional STR_VAR argument, 'match_function'. Set this to the name of a PATCH_FUNCTION that operates on the decompiled block as if it were a text file, and returns 'value'. A match is found if value=1 (and if all conventional 'match' variables also match).

- ALTER_SCRIPT_BLOCK takes an additional STR_VAR argument, 'function'. Set this to the name of a PATCH_FUNCTION which operates on the decompiled contents of any matched block as if it were a text file.

- CLONE_SCRIPT_BLOCK takes two additional STR_VAR arguments, 'function' and 'original_function', which work as for ALTER_SCRIPT_BLOCK applied to the new and old blocks respectively.

Share this post


Link to post

Do you know why INSERT_SCRIPT_BLOCK fails to correctly insert the following script block?

Spoiler

IF
	AttackedBy([ANYONE],DEFAULT)
	Allegiance(Myself,NEUTRAL)
	OR(2)
		SpellCastOnMeRES("",[ANYONE])
		SpellCastOnMe([ANYONE],0)
THEN
	RESPONSE #100
		Enemy()
END

 

Your function inserts it as

Spoiler

IF
	AttackedBy([ANYONE],DEFAULT)
	Allegiance(Myself,NEUTRAL)
	OR(2)
		SpellCastOnMe([ANYONE],0)	// Where is the RES version?
		SpellCastOnMe([ANYONE],0)
THEN
	RESPONSE #100
		Enemy()
END

 

 

Share this post


Link to post

I would expect that the RES not being named, is the reason why that version is not used.

Share this post


Link to post
1 hour ago, Jarno Mikkola said:

I would expect that the RES not being named, is the reason why that version is not used.

But that is the empty string, thus a valid string....

@DavidW Could you fix this issue?

Share this post


Link to post

I'm pretty sure SpellCastOnMeRES("",[ANYONE]) and SpellCastOnMe([ANYONE],0) compile to the same BCS code, so they're just notational variants of each other. Try compiling and decompiling your code and see what you get.

Share this post


Link to post
8 minutes ago, DavidW said:

I'm pretty sure SpellCastOnMeRES("",[ANYONE]) and SpellCastOnMe([ANYONE],0) compile to the same BCS code, so they're just notational variants of each other. Try compiling and decompiling your code and see what you get.

Yeah, I obtain the same result with a simple COMPILE..... And it decompiles into two SpellCastOnMe([ANYONE],0)...

Anyway, are those two triggers really equivalent? I mean, I expect the non-RES version to trigger only with spells listed in SPELL.ids... Am I right?

Share this post


Link to post

The engine doesn’t see the text, only what it compiles to. So if two bits of script compile to the same thing, then yes: they’re equivalent.

Share this post


Link to post

Jarno: I just accidentally deleted your post while trying to reply. Apologies, and feel free to repost - but in any case, BCS isn’t a hex code.

Share this post


Link to post

Ahh, well, those things happen. Yeah, it's not exactly simple as being hex code... but it's still just numbers to the computer. That can be expressed as a hexadecimal, or binary product.

Share this post


Link to post
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...