Jump to content

connecting wizard spells with their scrolls


subtledoctor

Recommended Posts

I'm facing the prospect of editing the schools of various wizard spells. This involves a number of things, but two principal issues are the school in the .spl file, and the wizard kit usability restrictions in the .itm file. In the game, these things are in lock-step with each other... but in the files, they are totally unrelated and you have to carefully edit them together,

 

To make the mod a bit friendlier to future development and player personalization, I have been using 2 associative arrays: one with spell RES filenames and the school I want to spell to be in,

spwi101 => alteration
spwi102 => abjuration
etc.

...and one with scroll filenames and the school I want the spell to be in:

scrl66 => alteration
scrl67 => abjuration
etc.

This has been working fairly well and it's easy enough to modify after the fact... but, it is far from ideal. To change anything, you have to make the exact same change to both arrays. It's annoying and unwieldy. I would love to simplify: make a single array with, say, a spell's IDS name and the school I want it to be in:

WIZARD_GREASE => alteration
WIZARD_ARMOR => abjuration
etc.

I can then run ACTION_PHP_EACH on that array, run RES_NUM_OF_SPELL_NAME to get the spell, change its school as appropriate, check the spell's NAME1 and then run a COPY_EXISTING_REGEXP_GLOB ~.*\.itm~ ~override~ to find the scroll whose name STRING_EQUALS_CASE the spell's name, and then patch that scroll.

 

But, running COPY_EXISTING_REGEXP_GLOB ~.*\.itm~ ~override~ sequentially for each of several dozen or even hundred items in the array, seems slow and wasteful.

 

Any ideas for how to get that result more efficiently?

 

--------------------------------------------------------------------

 

Alternatively, I've tried to read the school from the scroll's description... but STRING_CONTAINS_REGEXP resolves to 0 if true, which is making it hard to use.

PATCH_IF (%desc% STRING_CONTAINS_REGEXP ~(school)~) BEGIN

and

PATCH_IF (%desc% STRING_CONTAINS_REGEXP ~(school)~ = 0) BEGIN

and

PATCH_IF NOT (%desc% STRING_CONTAINS_REGEXP ~(school)~) BEGIN

all fail to patch scrolls...

Link to comment

David's got code to do almost just that in Tweaks, to boot. For the BG2 scrolls, it runs a regexp to find items that include the learn spell opcode and builds a variable array on the fly.

 

 

      COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~

          PATCH_IF ~SOURCE_SIZE~ >0x71 BEGIN



              READ_SHORT 0x1c ~type~

              PATCH_IF ~type~=11 BEGIN // only check scrolls

                       GET_OFFSET_ARRAY ab_array 0x64 4 0x68 2 0 0 0x38

                       PHP_EACH ab_array AS int => ab_off BEGIN

                                CLEAR_ARRAY fx_array

                                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

                                         PATCH_IF fx_type = 147 BEGIN

                                             READ_ASCII fx_off + 0x14 spell

                                             TO_LOWER ~spell~

                                             SPRINT $scrollmap(~%spell%~) ~%SOURCE_RES%~

                                         END

                                END

                       END

              END

          END

      BUT_ONLY
Link to comment

Make an array that associates the .spl with the scroll .itm.

 

Oh, I could definitely do it by hand, but then it would miss added spells/scrolls, and altered spells/scrolls. I want to make a "set it and forget it" routine that will generate the array on the fly.

 

 

David's got code to do almost just that in Tweaks, to boot. For the BG2 scrolls, it runs a regexp to find items that include the learn spell opcode and builds a variable array on the fly.

 

Ah! That's what I wasn't seeing! The spell information is already in the .itm file!

 

...in which case, I don't even need an array. I can just check for the Learn Spell opcode, read the resource field, find the spell file, read its school, and patch the scroll accordingly. Awesome.

Link to comment

Hmm. I don't understand every part of that. I'm not sure what that "$scrollmap" is for and don't think it's applicable to me, so I commented it out. Then I tried to adapt the rest like so:

  ACTION_DEFINE_ASSOCIATIVE_ARRAY spell_schools BEGIN
     WIZARD_GREASE => alteration
     WIZARD_ARMOR => abjuration
  END

  COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~
        PATCH_IF ~SOURCE_SIZE~ >0x71 BEGIN
            READ_SHORT 0x1c ~type~
            PATCH_IF ~type~=11 BEGIN // only check scrolls
                GET_OFFSET_ARRAY ab_array 0x64 4 0x68 2 0 0 0x38
                PHP_EACH ab_array AS int => ab_off BEGIN
                    CLEAR_ARRAY fx_array
                    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
                        PATCH_IF fx_type = 147 BEGIN
                            READ_ASCII fx_off + 0x14 spell
//                          SPRINT $scrollmap(~%spell%~) ~%SOURCE_RES%~
                            LPF NAME_NUM_OF_SPELL_RES
                                STR_VAR spell_res = EVAL ~%spell%~
                                RET spell_name spell_num
                            END
                            PHP_EACH spell_schools AS spell => school BEGIN
                                PATCH_IF (%spell% = %spell_name%) BEGIN
                                    PATCH_IF (%school% = ~alteration~) BEGIN
                                        WRITE_BYTE 0x2d ("0x00") //blocked to: none  
                                        WRITE_BYTE 0x2f ("0x00") //blocked to: none
                                    END
                                END
                            END
                        END
                    END
                END
            END
        END
  BUT_ONLY

...with the idea that it would simply remove usability restrictions for Grease scrolls. But I'm getting an error: "cannot convert r or %r% to an integer"

Link to comment

I handled a bunch of parse errors up through this line:

PHP_EACH spell_schools AS spell => school BEGIN

So I don't think the error is before that. I suspect I'm not treating the array right and the error is with one of these lines:

 

PATCH_IF (%spell% = %spell_name%) BEGIN
    PATCH_IF (%school% = ~alteration~) BEGIN

... but I'm having a hard time figuring out the problem, only because I can't see where that "r" or "%r%" even comes from.

Link to comment

Okay, ignoring my convoluted nonsense above... I'm still trying to wrap my head around what the Weidu readme calls "array constructs," i.e. creating an array with that SPRINT command and the "$" ...

 

SPRINT $scrollmap(~%spell%~) ~%SOURCE_RES%~
Is that an associative array? Can I follow it with something like (excuse the psuedocode):

ACTION_PHP_EACH scrollmap AS foo => bar BEGIN
  COPY_EXISTING ~%foo%.spl~ ~override~
    READ_BYTE 0x0025 school
  BUT_ONLY
  COPY_EXISTING ~%bar%.itm~ ~override~
    PATCH_IF (%school% = 1) BEGIN
      (patch school/usability of scroll)
    END
    PATCH_IF (%school% = 2) BEGIN
      (etc.)
    END
  BUT_ONLY
END
?
Link to comment

Try running this and see what it prints out

ACTION_DEFINE_ASSOCIATIVE_ARRAY spell_schools BEGIN
WIZARD_GREASE => alteration
WIZARD_ARMOR => abjuration

END
ACTION_PHP_EACH spell_schools AS spell_id_label => spell_school BEGIN

PRINT ~spell_id_label is %spell_id_label%, spell_school is %spell_school%~

END

 

 

SPRINT $scrollmap(~%spell%~) ~%SOURCE_RES%~

Is that an associative array?

 

Same thing, yes.

 

As for your error, pretty sure that this

PATCH_IF (%school% = ~alteration~) BEGIN

should be

PATCH_IF (%school% STRING_EQUAL_CASE ~alteration~) BEGIN

 

because the array which you run via PHP_EACH has the argument defined as "school" in the form of string rather than value.

Link to comment

Regarding STRING_EQUAL_CASE: [smacks forehead] That one should not ave gotten by me. I've spent a couple nights converting my spell-sphere arrays over to using strings instead of numerals, do I am well-versed in the difference. I'll chalk that one up to late-night coding.

 

Also, I realize that I have conflated two issues here. The first is, start with an array for spells and the schools I would like to put them in, and run a routine that will patch *both* the .spl files and the scroll .itm files to reflect the desired changes. For this first issue I want to use my own array.

 

The second thing is only to check what school a spell is in, and patch scrolls accordingly. I'm realizing that this is an independent question; even if a user decides *not* to change the schools of any spells, I want to be able to change opposition schools and have scrolls' usability reflect it. For this second issue, we want to generate the spell-scroll array on the fly.

 

By separating the two functions, they can be run sequentially (or just one, or just the other). Nice!

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...