Jump to content

Toss your semi-useful WeiDU macros here


Recommended Posts

9 hours ago, kjeron said:
DEFINE_ACTION_FUNCTION CLONE_ARRAY STR_VAR array = ~~ RET_ARRAY EVAL ~%array%~ BEGIN END

LAF CLONE_ARRAY STR_VAR array = ~old~ RET_ARRAY new = old END

Simple method to clone array "old" into array "new" with the new RET_ARRAY feature.

Maybe define it as dimorphic, just in case you need it inside a patch...

Edited by Luke
Link to comment

Filling another little gap, here's ALTER_STORE_CURE:

Spoiler

DEFINE_PATCH_FUNCTION ALTER_STORE_CURE
INT_VAR
  price            = "-1"
STR_VAR
  match_resref  = ~~
  resref        = ~~
BEGIN
  PATCH_IF (NOT ~%match_resref%~ STR_EQ ~~) BEGIN
    READ_LONG 0x70 ofs_cures
    READ_LONG 0x74 num_cures
    FOR (idx = 0; idx < num_cures; ++idx) BEGIN
      SET ofs = ofs_cures + idx*0xc
      READ_ASCII ofs s (8) NULL
      PATCH_IF (~%match_resref%~ STR_EQ ~%s%~) BEGIN
        PATCH_IF (NOT ~%resref%~ STR_EQ ~~) BEGIN
          WRITE_ASCIIE ofs ~%resref%~ (8)
        END
        PATCH_IF (price != "-1") BEGIN
          WRITE_LONG (ofs + 0x8) price
        END
      END
    END
  END
END

Cures are simpler than just about everything else - only two elements and twelve bytes per cure. But it's still annoying to have to break out the low-level tools for them.

Anyway, here's the documentation for the function, with a sample use taken from the tweak mod I'm putting together.

Spoiler

ALTER_STORE_CURE: This is a patch function for store files that will match an existing cure and alter it based on the defined variables.

STR_VAR match_resref: The cure to match. Defaults to an empty string, and the function will do nothing if you leave it like that.

Once the matching cure is found, the remaining variables allow the function to change its fields to their values.

STR_VAR resref to the altered cure's resource reference. Defaults to an empty string for no change.

INT_VAR price to the altered cure's price. Defaults to -1 for no change.

 

And here's a sample snippet of my code using it. This replaces all instances of CLERIC_DISPEL_MAGIC (dispel magic at the recipient's divine caster level) in temple stores with FORCE_DISPEL_MAGIC (dispel magic with no level check).

COPY_EXISTING_REGEXP GLOB ~^.+\.sto$~ ~override~

  LPF ALTER_STORE_CURE STR_VAR match_resref=SPPR303 resref=SPIN866 END

BUT_ONLY

(Also ... this is my first post here. I've been over at Beamdog for a while, and lurked here reading some things, but I only registered just now.)

Edited by jmerry
Link to comment

EDIT - to put it more succinctly: this method allows portable functions (like the ones in this thread) to use strings, without regard for the traified state of a given mod that might include and use such functions.

I have a set of functions that are portable, and used in different ways across several different mods. I don't want to worry about matching the .tra files across all those mods so I bundle a .tra file with the portable set of functions. This can then be translated and it can use the appropriate .tra for the player's game, even though the various individual mods that use the functions may or may not themselves be translated into a given language.

I'm not sure I've done this particularly well or efficiently, or if the utility of this extends beyond my admittedly odd use-case. And note, it currently uses an EE-only Weidu variable. Anyway:

  •  I start out with folders that match the game's language folders:  en_US / fr_FR / pl_PL / de_DE / es_ES / cs_CZ / ru_RU / ko_KR
  •  I put "my_functions.tra" in the "en_US" folder, with:
@10  = ~^Duration:.+$~

...and a bunch of other strings, as needed. For purposes of these functions, I use @10 to check the word "duration" in spell descriptions to verify the game's language is English.

  •  Then the function to find the game language:
DEFINE_ACTION_FUNCTION set_lang RET game_lang BEGIN

  ACTION_IF !(VARIABLE_IS_SET %EE_LANGUAGE%) BEGIN
    COPY_EXISTING ~spwi101.spl~ ~override~
      READ_STRREF 0x50 description_string
    BUT_ONLY
    ACTION_FOR_EACH lang IN ~en_US~ /*~fr_FR~ ~pl_PL~ ~de_DE~ ~es_ES~ ~cs_CZ~ ~ru_RU~ ~ko_KR~*/ BEGIN
      WITH_TRA ~%MOD_FOLDER%/lib/my_functions/%lang%/my_functions.tra~ BEGIN
        OUTER_SPRINT duration_lang @10 
      END	
      ACTION_IF !(~%description_string%~ STRING_CONTAINS_REGEXP ~%duration_lang%~) BEGIN
        OUTER_SPRINT game_lang ~%lang%~
      END
    END
  END
  ACTION_IF (VARIABLE_IS_SET %EE_LANGUAGE%) BEGIN
    OUTER_SPRINT ee_lang ~%EE_LANGUAGE%~
    ACTION_IF (FILE_EXISTS ~%MOD_FOLDER%/lib/my_functions/%ee_lang%/my_functions.tra~) BEGIN
      OUTER_SPRINT game_lang ~%EE_LANGUAGE%~
    END
  END
  ACTION_IF !(VARIABLE_IS_SET %game_lang%) BEGIN
    OUTER_SPRINT game_lang ~en_US~
  END

END

Right now the functions are not traified - the strings only exist in English. So I only enable the ~en_US~ check. If and as people volunteer to translate it into different languages, I can un-comment each language's designation in the 7th line of the function. When the strings are translated, the "duration" word at @10 will be translated as well so the function will automatically work with the new language.

Then, in each. function that uses strings, I add this at the top of the function:

LAF set_lang RET game_lang END

WITH_TRA ~%MOD_FOLDER%/lib/my_functions/%game_lang%/my_functions.tra~ BEGIN

(As I say, this is for a portable set of functions and. it is assumed that they exist in each mod at %MOD_FOLDER%/lib/ )

This way, I don't need to worry about the different mods all being identically traified. As long as these particular functions are traified, they will try to match themselves to the player's chosen game language even if the mod containing the functions cannot.

Edited by subtledoctor
Link to comment

I recall seeing some kind of "clone item effects to spell" of vice-versa function somewhere... but I don't see it in the list for this thread. Am I imagining it?

If I have an item that has a "magical" header with various effects, and I want to move those effects out into a spell and replace them with opcode 146 in the item, is there a reasonably efficient/effective method for that? I don't want to do it manually because the particular effects may be very different depending on what mods were installed prior.

Link to comment

That converts a spell into item effects? The opposite of what I want to do, but still a very good jumpstart, thank you.

...oh wait, I just realized where I saw that once: in the Weidu documentation itself! :laugh: I guess I was just searching in the wrong place.

Quote

ITEM_EFFECT_TO_SPELL: copies all extended effects from the current item to the first extended header of a spell. This is a PATCH macro and function.

  • SET type to the header type to copy the effect from (use 99 to specify ’all types’).
  • SET header to number of extended header the effect should be copied from (use 99 to specify ’every header’).
  • SET insert_point to the index into the spell file at which the effects should be inserted. A value of 0 will have the effects inserted as the first effects of the spell. Effects are inserted as the last effects by default.
  • SPRINT new_itm_spl to a spell you want to copy effects to.

Will take a look at using that.

Link to comment

Here's alter_area_actor_ex, an extension of ALTER_AREA_ACTOR which allows matching by more than just actor name. All of the fields get match value/new value pairs, and all variables are optional. Unlike the original, in which no variables are paired, the actor name variable is required, and that one is the only one that determines matches. If an area has multiple actors with the same name and you want to modify one, you're out of luck.

The code:

Spoiler
/* ALTER_AREA_ACTOR_EX my own code. Free for anyone to use. */

/* alter_area_actor_ex : an extension of ALTER_AREA_ACTOR from base WeiDU to allow match-checking of all fields and alteration of the actor's name. All variables present in the original ALTER_AREA_ACTOR function are present here with the same meaning, with the exception of the string variable actor_name. The old actor_name is now match_actor_name and is no longer required, and the new actor_name is the value to change the actor's name to. In addition, each variable #### has a corresponding variable match_#### to help determine which actors to patch.

This function patches actors in the current area. Each field has a match_#### and #### variable pair. If all match_#### values match the actor's values, the fields for that actor will be updated with the #### values. All match_#### variables default to a value that results in all actors being considered matches and all #### variables default to a value that results in no changes being made; if no variables are set, the function will consider all actors matches but then make no changes to them.

All integer variables other than match_expiry and expiry default to -1, and all negative values result in the default behavior of all match/no change. For match_expiry and expiry, the default is -2 and only that value has the default behavior. For the flag variables, all values other than 0 and 1 result in the default behavior. These default conventions are the same as in the original ALTER_AREA_ACTOR.

All string variables default to "This string is too long to be valid", and strings longer than the field's size result in the default behavior of all match/no change. This size is 32 characters for actor_name/match_actor_name and 8 characters for the other string fields. Note that this default convention is different from the original ALTER_AREA_ACTOR, which used "same" as its defaults.

As a special note, if x_coord is defined and dest_x is not, then x_coord will be used as the x_value of the destination. The same is true for y_coord, dest_y, and the y-value of the destination.

See also the information on the original ALTER_AREA_ACTOR in the WeiDU documentation for details on each field. */

DEFINE_PATCH_FUNCTION alter_area_actor_ex
INT_VAR
  match_x_coord = ~-1~
  match_y_coord = ~-1~
  match_dest_x = ~-1~
  match_dest_y = ~-1~
  match_spawned = ~-1~
  match_animation = ~-1~
  match_orient = ~-1~
  match_expiry = ~-2~
  match_wander = ~-1~
  match_follow = ~-1~
  match_times_talked = ~-1~
  match_flag_cre_unattached = ~-1~
  match_flag_seen_party = ~-1~
  match_flag_invulnerable = ~-1~
  match_flag_override_script = ~-1~
  match_flag_time_0 = ~-1~
  match_flag_time_1 = ~-1~
  match_flag_time_2 = ~-1~
  match_flag_time_3 = ~-1~
  match_flag_time_4 = ~-1~
  match_flag_time_5 = ~-1~
  match_flag_time_6 = ~-1~
  match_flag_time_7 = ~-1~
  match_flag_time_8 = ~-1~
  match_flag_time_9 = ~-1~
  match_flag_time_10 = ~-1~
  match_flag_time_11 = ~-1~
  match_flag_time_12 = ~-1~
  match_flag_time_13 = ~-1~
  match_flag_time_14 = ~-1~
  match_flag_time_15 = ~-1~
  match_flag_time_16 = ~-1~
  match_flag_time_17 = ~-1~
  match_flag_time_18 = ~-1~
  match_flag_time_19 = ~-1~
  match_flag_time_20 = ~-1~
  match_flag_time_21 = ~-1~
  match_flag_time_22 = ~-1~
  match_flag_time_23 = ~-1~
  x_coord = ~-1~
  y_coord = ~-1~
  dest_x = ~-1~
  dest_y = ~-1~
  spawned = ~-1~
  animation = ~-1~
  orient = ~-1~
  expiry = ~-2~
  wander = ~-1~
  follow = ~-1~
  times_talked = ~-1~
  flag_cre_unattached = ~-1~
  flag_seen_party = ~-1~
  flag_invulnerable = ~-1~
  flag_override_script = ~-1~
  flag_time_0 = ~-1~
  flag_time_1 = ~-1~
  flag_time_2 = ~-1~
  flag_time_3 = ~-1~
  flag_time_4 = ~-1~
  flag_time_5 = ~-1~
  flag_time_6 = ~-1~
  flag_time_7 = ~-1~
  flag_time_8 = ~-1~
  flag_time_9 = ~-1~
  flag_time_10 = ~-1~
  flag_time_11 = ~-1~
  flag_time_12 = ~-1~
  flag_time_13 = ~-1~
  flag_time_14 = ~-1~
  flag_time_15 = ~-1~
  flag_time_16 = ~-1~
  flag_time_17 = ~-1~
  flag_time_18 = ~-1~
  flag_time_19 = ~-1~
  flag_time_20 = ~-1~
  flag_time_21 = ~-1~
  flag_time_22 = ~-1~
  flag_time_23 = ~-1~
STR_VAR
  match_actor_name = ~This string is too long to be valid~
  match_dlg_file = ~This string is too long to be valid~
  match_script_override = ~This string is too long to be valid~
  match_script_general = ~This string is too long to be valid~
  match_script_class = ~This string is too long to be valid~
  match_script_race = ~This string is too long to be valid~
  match_script_default = ~This string is too long to be valid~
  match_script_specifics = ~This string is too long to be valid~
  match_cre_file = ~This string is too long to be valid~
  actor_name = ~This string is too long to be valid~
  dlg_file = ~This string is too long to be valid~
  script_override = ~This string is too long to be valid~
  script_general = ~This string is too long to be valid~
  script_class = ~This string is too long to be valid~
  script_race = ~This string is too long to be valid~
  script_default = ~This string is too long to be valid~
  script_specifics = ~This string is too long to be valid~
  cre_file = ~This string is too long to be valid~
BEGIN
  READ_LONG 0x54 offset_actors
  READ_SHORT 0x58 num_actors
  FOR (idx = 0; idx < num_actors; ++idx) BEGIN // Loop through actors in area
    SET test_match = 1 // Actor is match until proven otherwise
    SET offset_x_coord = offset_actors + (idx * 0x110) + 0x20
    READ_SHORT offset_x_coord read_x_coord
    PATCH_IF (match_x_coord >= 0 AND match_x_coord != read_x_coord) BEGIN
      SET test_match = 0
    END
    SET offset_y_coord = offset_actors + (idx * 0x110) + 0x22
    READ_SHORT offset_y_coord read_y_coord
    PATCH_IF (match_y_coord >= 0 AND match_y_coord != read_y_coord) BEGIN
      SET test_match = 0
    END
    SET offset_dest_x = offset_actors + (idx * 0x110) + 0x24
    READ_SHORT offset_dest_x read_dest_x
    PATCH_IF (match_dest_x >= 0 AND match_dest_x != read_dest_x) BEGIN
      SET test_match = 0
    END
    SET offset_dest_y = offset_actors + (idx * 0x110) + 0x26
    READ_SHORT offset_dest_y read_dest_y
    PATCH_IF (match_dest_y >= 0 AND match_dest_y != read_dest_y) BEGIN
      SET test_match = 0
    END
    SET offset_spawned = offset_actors + (idx * 0x110) + 0x2c
    READ_SHORT offset_spawned read_spawned
    PATCH_IF (match_spawned >= 0 AND match_spawned != read_spawned) BEGIN
      SET test_match = 0
    END
    SET offset_animation = offset_actors + (idx * 0x110) + 0x30
    READ_LONG offset_animation read_animation
    PATCH_IF (match_animation >= 0 AND match_animation != read_animation) BEGIN
      SET test_match = 0
    END
    SET offset_orient = offset_actors + (idx * 0x110) + 0x34
    READ_SHORT offset_orient read_orient
    PATCH_IF (match_orient >= 0 AND match_orient != read_orient) BEGIN
      SET test_match = 0
    END
    SET offset_expiry = offset_actors + (idx * 0x110) + 0x38
    READ_SLONG offset_expiry read_expiry
    PATCH_IF ((match_expiry + 2) != 0 AND match_expiry != read_expiry) BEGIN
      SET test_match = 0
    END
    SET offset_wander = offset_actors + (idx * 0x110) + 0x3c
    READ_SHORT offset_wander read_wander
    PATCH_IF (match_wander >= 0 AND match_wander != read_wander) BEGIN
      SET test_match = 0
    END
    SET offset_follow = offset_actors + (idx * 0x110) + 0x3e
    READ_SHORT offset_follow read_follow
    PATCH_IF (match_follow >= 0 AND match_follow != read_follow) BEGIN
      SET test_match = 0
    END
    SET offset_times_talked = offset_actors + (idx * 0x110) + 0x44
    READ_SHORT offset_times_talked read_times_talked
    PATCH_IF (match_times_talked >= 0 AND match_times_talked != read_times_talked) BEGIN
      SET test_match = 0
    END
    SET offset_flags = offset_actors + (idx * 0x110) + 0x28
    READ_LONG offset_flags read_flags
    PATCH_IF (match_flag_cre_unattached = 0 AND (read_flags BAND BIT0) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_cre_unattached = 1 AND (read_flags BAND BIT0) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_seen_party = 0 AND (read_flags BAND BIT1) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_seen_party = 1 AND (read_flags BAND BIT1) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_invulnerable = 0 AND (read_flags BAND BIT2) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_invulnerable = 1 AND (read_flags BAND BIT2) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_override_script = 0 AND (read_flags BAND BIT3) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_override_script = 1 AND (read_flags BAND BIT3) = 0) BEGIN
      SET test_match = 0
    END
    SET offset_times = offset_actors + (idx * 0x110) + 0x40
    READ_LONG offset_times read_times
    PATCH_IF (match_flag_time_0 = 0 AND (read_times BAND BIT0) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_0 = 1 AND (read_times BAND BIT0) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_1 = 0 AND (read_times BAND BIT1) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_1 = 1 AND (read_times BAND BIT1) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_2 = 0 AND (read_times BAND BIT2) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_2 = 1 AND (read_times BAND BIT2) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_3 = 0 AND (read_times BAND BIT3) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_3 = 1 AND (read_times BAND BIT3) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_4 = 0 AND (read_times BAND BIT4) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_4 = 1 AND (read_times BAND BIT4) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_5 = 0 AND (read_times BAND BIT5) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_5 = 1 AND (read_times BAND BIT5) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_6 = 0 AND (read_times BAND BIT6) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_6 = 1 AND (read_times BAND BIT6) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_7 = 0 AND (read_times BAND BIT7) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_7 = 1 AND (read_times BAND BIT7) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_8 = 0 AND (read_times BAND BIT8) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_8 = 1 AND (read_times BAND BIT8) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_9 = 0 AND (read_times BAND BIT9) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_9 = 1 AND (read_times BAND BIT9) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_10 = 0 AND (read_times BAND BIT10) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_10 = 1 AND (read_times BAND BIT10) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_11 = 0 AND (read_times BAND BIT11) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_11 = 1 AND (read_times BAND BIT11) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_12 = 0 AND (read_times BAND BIT12) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_12 = 1 AND (read_times BAND BIT12) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_13 = 0 AND (read_times BAND BIT13) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_13 = 1 AND (read_times BAND BIT13) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_14 = 0 AND (read_times BAND BIT14) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_14 = 1 AND (read_times BAND BIT14) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_15 = 0 AND (read_times BAND BIT15) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_15 = 1 AND (read_times BAND BIT15) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_16 = 0 AND (read_times BAND BIT16) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_16 = 1 AND (read_times BAND BIT16) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_17 = 0 AND (read_times BAND BIT17) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_17 = 1 AND (read_times BAND BIT17) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_18 = 0 AND (read_times BAND BIT18) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_18 = 1 AND (read_times BAND BIT18) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_19 = 0 AND (read_times BAND BIT19) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_19 = 1 AND (read_times BAND BIT19) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_20 = 0 AND (read_times BAND BIT20) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_20 = 1 AND (read_times BAND BIT20) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_21 = 0 AND (read_times BAND BIT21) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_21 = 1 AND (read_times BAND BIT21) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_22 = 0 AND (read_times BAND BIT22) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_22 = 1 AND (read_times BAND BIT22) = 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_23 = 0 AND (read_times BAND BIT23) != 0) BEGIN
      SET test_match = 0
    END
    PATCH_IF (match_flag_time_23 = 1 AND (read_times BAND BIT23) = 0) BEGIN
      SET test_match = 0
    END
    SET offset_actor_name = offset_actors + (idx * 0x110) + 0
    READ_ASCII offset_actor_name read_actor_name (32)
    PATCH_IF ((STRING_LENGTH ~%match_actor_name%~) < 33 AND NOT (~%match_actor_name%~ STR_EQ ~%read_actor_name%~)) BEGIN
      SET test_match = 0
    END
    SET offset_dlg_file = offset_actors + (idx * 0x110) + 0x48
    READ_ASCII offset_dlg_file read_dlg_file
    PATCH_IF ((STRING_LENGTH ~%match_dlg_file%~) < 9 AND NOT (~%match_dlg_file%~ STR_EQ ~%read_dlg_file%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_override = offset_actors + (idx * 0x110) + 0x50
    READ_ASCII offset_script_override read_script_override
    PATCH_IF ((STRING_LENGTH ~%match_script_override%~) < 9 AND NOT (~%match_script_override%~ STR_EQ ~%read_script_override%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_general = offset_actors + (idx * 0x110) + 0x58
    READ_ASCII offset_script_general read_script_general
    PATCH_IF ((STRING_LENGTH ~%match_script_general%~) < 9 AND NOT (~%match_script_general%~ STR_EQ ~%read_script_general%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_class = offset_actors + (idx * 0x110) + 0x60
    READ_ASCII offset_script_class read_script_class
    PATCH_IF ((STRING_LENGTH ~%match_script_class%~) < 9 AND NOT (~%match_script_class%~ STR_EQ ~%read_script_class%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_race = offset_actors + (idx * 0x110) + 0x68
    READ_ASCII offset_script_race read_script_race
    PATCH_IF ((STRING_LENGTH ~%match_script_race%~) < 9 AND NOT (~%match_script_race%~ STR_EQ ~%read_script_race%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_default = offset_actors + (idx * 0x110) + 0x70
    READ_ASCII offset_script_default read_script_default
    PATCH_IF ((STRING_LENGTH ~%match_script_default%~) < 9 AND NOT (~%match_script_default%~ STR_EQ ~%read_script_default%~)) BEGIN
      SET test_match = 0
    END
    SET offset_script_specifics = offset_actors + (idx * 0x110) + 0x78
    READ_ASCII offset_script_specifics read_script_specifics
    PATCH_IF ((STRING_LENGTH ~%match_script_specifics%~) < 9 AND NOT (~%match_script_specifics%~ STR_EQ ~%read_script_specifics%~)) BEGIN
      SET test_match = 0
    END
    SET offset_cre_file = offset_actors + (idx * 0x110) + 0x80
    READ_ASCII offset_cre_file read_cre_file
    PATCH_IF ((STRING_LENGTH ~%match_cre_file%~) < 9 AND NOT (~%match_cre_file%~ STR_EQ ~%read_cre_file%~)) BEGIN
      SET test_match = 0
    END
    /* All match variables checked. Making changes now. */
    PATCH_IF (test_match = 1) BEGIN
      PATCH_IF (x_coord >= 0) BEGIN
        WRITE_SHORT offset_x_coord x_coord
      END
      PATCH_IF (y_coord >= 0) BEGIN
        WRITE_SHORT offset_y_coord y_coord
      END
      PATCH_IF (dest_x < 0 AND x_coord >= 0) BEGIN
        SET dest_x = x_coord
      END
      PATCH_IF (dest_x >= 0) BEGIN
        WRITE_SHORT offset_dest_x dest_x
      END
      PATCH_IF (dest_y < 0 AND y_coord >= 0) BEGIN
        SET dest_y = y_coord
      END
      PATCH_IF (dest_y >= 0) BEGIN
        WRITE_SHORT offset_dest_y dest_y
      END
      PATCH_IF (spawned >= 0) BEGIN
        WRITE_SHORT offset_spawned spawned
      END
      PATCH_IF (animation >= 0) BEGIN
        WRITE_LONG offset_animation animation
      END
      PATCH_IF (orient >= 0) BEGIN
        WRITE_SHORT offset_orient orient
      END
      PATCH_IF (expiry + 2 != 0) BEGIN
        WRITE_LONG offset_expiry expiry
      END
      PATCH_IF (wander >= 0) BEGIN
        WRITE_SHORT offset_wander wander
      END
      PATCH_IF (follow >= 0) BEGIN
        WRITE_SHORT offset_follow follow
      END
      PATCH_IF (times_talked >= 0) BEGIN
        WRITE_SHORT offset_times_talked times_talked
      END
      PATCH_IF (flag_cre_unattached = 0) BEGIN
        WRITE_LONG offset_flags (THIS BAND (BNOT BIT0))
      END
      PATCH_IF (flag_cre_unattached = 1) BEGIN
        WRITE_LONG offset_flags (THIS BOR BIT0)
      END
      PATCH_IF (flag_seen_party = 0) BEGIN
        WRITE_LONG offset_flags (THIS BAND (BNOT BIT1))
      END
      PATCH_IF (flag_seen_party = 1) BEGIN
        WRITE_LONG offset_flags (THIS BOR BIT1)
      END
      PATCH_IF (flag_invulnerable = 0) BEGIN
        WRITE_LONG offset_flags (THIS BAND (BNOT BIT2))
      END
      PATCH_IF (flag_invulnerable = 1) BEGIN
        WRITE_LONG offset_flags (THIS BOR BIT2)
      END
      PATCH_IF (flag_override_script = 0) BEGIN
        WRITE_LONG offset_flags (THIS BAND (BNOT BIT3))
      END
      PATCH_IF (flag_override_script = 1) BEGIN
        WRITE_LONG offset_flags (THIS BOR BIT3)
      END
      PATCH_IF (flag_time_0 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT0))
      END
      PATCH_IF (flag_time_0 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT0)
      END
      PATCH_IF (flag_time_1 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT1))
      END
      PATCH_IF (flag_time_1 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT1)
      END
      PATCH_IF (flag_time_2 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT2))
      END
      PATCH_IF (flag_time_2 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT2)
      END
      PATCH_IF (flag_time_3 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT3))
      END
      PATCH_IF (flag_time_3 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT3)
      END
      PATCH_IF (flag_time_4 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT4))
      END
      PATCH_IF (flag_time_4 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT4)
      END
      PATCH_IF (flag_time_5 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT5))
      END
      PATCH_IF (flag_time_5 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT5)
      END
      PATCH_IF (flag_time_6 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT6))
      END
      PATCH_IF (flag_time_6 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT6)
      END
      PATCH_IF (flag_time_7 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT7))
      END
      PATCH_IF (flag_time_7 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT7)
      END
      PATCH_IF (flag_time_8 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT8))
      END
      PATCH_IF (flag_time_8 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT8)
      END
      PATCH_IF (flag_time_9 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT9))
      END
      PATCH_IF (flag_time_9 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT9)
      END
      PATCH_IF (flag_time_10 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT10))
      END
      PATCH_IF (flag_time_10 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT10)
      END
      PATCH_IF (flag_time_11 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT11))
      END
      PATCH_IF (flag_time_11 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT11)
      END
      PATCH_IF (flag_time_12 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT12))
      END
      PATCH_IF (flag_time_12 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT12)
      END
      PATCH_IF (flag_time_13 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT13))
      END
      PATCH_IF (flag_time_13 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT13)
      END
      PATCH_IF (flag_time_14 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT14))
      END
      PATCH_IF (flag_time_14 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT14)
      END
      PATCH_IF (flag_time_15 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT15))
      END
      PATCH_IF (flag_time_15 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT15)
      END
      PATCH_IF (flag_time_16 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT16))
      END
      PATCH_IF (flag_time_16 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT16)
      END
      PATCH_IF (flag_time_17 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT17))
      END
      PATCH_IF (flag_time_17 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT17)
      END
      PATCH_IF (flag_time_18 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT18))
      END
      PATCH_IF (flag_time_18 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT18)
      END
      PATCH_IF (flag_time_19 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT19))
      END
      PATCH_IF (flag_time_19 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT19)
      END
      PATCH_IF (flag_time_20 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT20))
      END
      PATCH_IF (flag_time_20 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT20)
      END
      PATCH_IF (flag_time_21 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT21))
      END
      PATCH_IF (flag_time_21 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT21)
      END
      PATCH_IF (flag_time_22 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT22))
      END
      PATCH_IF (flag_time_22 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT22)
      END
      PATCH_IF (flag_time_23 = 0) BEGIN
        WRITE_LONG offset_times (THIS BAND (BNOT BIT23))
      END
      PATCH_IF (flag_time_23 = 1) BEGIN
        WRITE_LONG offset_times (THIS BOR BIT23)
      END
      PATCH_IF ((STRING_LENGTH ~%actor_name%~) < 33) BEGIN
        WRITE_ASCII offset_actor_name ~%actor_name%~ #32
      END
      PATCH_IF ((STRING_LENGTH ~%dlg_file%~) < 9) BEGIN
        WRITE_ASCII offset_dlg_file ~%dlg_file%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_override%~) < 9) BEGIN
        WRITE_ASCII offset_script_override ~%script_override%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_general%~) < 9) BEGIN
        WRITE_ASCII offset_script_general ~%script_general%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_class%~) < 9) BEGIN
        WRITE_ASCII offset_script_class ~%script_class%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_race%~) < 9) BEGIN
        WRITE_ASCII offset_script_race ~%script_race%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_default%~) < 9) BEGIN
        WRITE_ASCII offset_script_default ~%script_default%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%script_specifics%~) < 9) BEGIN
        WRITE_ASCII offset_script_specifics ~%script_specifics%~ #8
      END
      PATCH_IF ((STRING_LENGTH ~%cre_file%~) < 9) BEGIN
        WRITE_ASCII offset_cre_file ~%cre_file%~ #8
      END
    END // Edits complete
  END // Actor loop closed
END

 

Documentation, also included as a comment in the code:

Spoiler

alter_area_actor_ex : an extension of ALTER_AREA_ACTOR from base WeiDU to allow match-checking of all fields and alteration of the actor's name. All variables present in the original ALTER_AREA_ACTOR function are present here with the same meaning, with the exception of the string variable actor_name. The old actor_name is now match_actor_name and is no longer required, and the new actor_name is the value to change the actor's name to. In addition, each variable #### has a corresponding variable match_#### to help determine which actors to patch.

This function patches actors in the current area. Each field has a match_#### and #### variable pair. If all match_#### values match the actor's values, the fields for that actor will be updated with the #### values. All match_#### variables default to a value that results in all actors being considered matches and all #### variables default to a value that results in no changes being made; if no variables are set, the function will consider all actors matches but then make no changes to them.

All integer variables other than match_expiry and expiry default to -1, and all negative values result in the default behavior of all match/no change. For match_expiry and expiry, the default is -2 and only that value has the default behavior. For the flag variables, all values other than 0 and 1 result in the default behavior. These default conventions are the same as in the original ALTER_AREA_ACTOR.

All string variables default to "This string is too long to be valid", and strings longer than the field's size result in the default behavior of all match/no change. This size is 32 characters for actor_name/match_actor_name and 8 characters for the other string fields. Note that this default convention is different from the original ALTER_AREA_ACTOR, which used "same" as its defaults.

As a special note, if x_coord is defined and dest_x is not, then x_coord will be used as the x_value of the destination. The same is true for y_coord, dest_y, and the y-value of the destination.

See also the information on the original ALTER_AREA_ACTOR in the WeiDU documentation for details on each field.

Example use, from my tweak mod:

Spoiler

One of the Amnish Soldiers in BG2's Government District starts in a wall. This code in my mod moves him out of it.

	COPY_EXISTING ~AR1000.ARE~ ~override~ // Government District, Amnish Soldier
		READ_LONG 0x54 ofsActors
		READ_SHORT 0x58 numActors
		FOR (idx = 0; idx < numActors; ++idx) BEGIN
			SET offsetx = ofsActors + (idx * 0x110) + 0x20
			SET offsety = ofsActors + (idx * 0x110) + 0x22
			READ_SHORT offsetx xpos
			READ_SHORT offsety ypos
			PATCH_IF ((xpos = 2796) AND (ypos = 538)) BEGIN
				WRITE_SHORT offsety 558
			END
		END // Multiple actors with same name, long version needed
	BUT_ONLY

Or I could do it with a call of the new function.

	COPY_EXISTING ~AR1000.ARE~ ~override~ // Government District, Amnish Soldier
		LPF alter_area_actor_ex INT_VAR match_x_coord = 2796 match_y_coord = 538 y_coord = 558 END
	BUT_ONLY

Vastly simpler and more intuitive once the infrastructure's in place.

This function has not been tested. If someone spots an error, I'll edit in a correction. It has already been changed slightly from the original posting in another thread, with one READ_LONG changed to a READ_SLONG.

Edit (6 Jan 2023): I put it to use in my tweak mod (for exactly the example shown), and in the process discovered and fixed a staggering number of syntax errors. The corrected version has now been edited into the post.

Edited by jmerry
Fixed code
Link to comment

Library for implementing arcane spellcasting in armor

This is a bit higher-level than most of the functions here, but I suppose it’s worth including for the sake of improved compatibility. 

Several years ago I wrote up a utility that shifts armors’ opcode 145 effects preventing arcane spellcasting into .EFF files called by opcode 177, and differing by armor category (broadly: leather, chain, plate). Because the .EFF files have parent resource names, you can selectively exempt an individual or class of individuals from having their casting disabled, by applying opcode 206/318/324. This can be done in the .ITM files themselves, or via a spell in a kit table, or via some scripted event… you could have arcane casters learn “light/medium/heavy armor proficiency” to cast spells in certain armors. It’s quite flexible. 

As a real-world example, Aquadrizzt’s Magus kit mod applies a spell in the kit table which enables that sorcerer kit to cast spells in any kind of armor; and I have a mod that enables all bards to cast spells in light armors, per 3E rules. These two applications can be installed separately, in any order, and they both work fine together.

The macro is in a little .tpa file that can be copied and used by any mod. In Weidu, the mod simply has to INCLUDE the .tpa file, run the D5_ARMORED_CASTING macro, and then manually setup the mod’s intended exemptions. If six different mods include this and run the function to convert armors, the function will recognize that it’s already been applied and will get out of its own way. But any exemptions specified by all six mods will work appropriately and without conflicting, and then we can all join hands and sing Kumbaya.

Edited by subtledoctor
Link to comment

Something I recently coded for EEFP, cd_define_area_vertices let's you define vertices for an existing door (open or closed, normal or impeded), container, or region/trigger. The basic idea is simple: you define the array cd_define_area_vertices_array with new vertices and then run the function, where it will delete the existing vertices for the selected door/region/container and insert the new ones from the array. From my notes:

Quote

@Angel found a number of opened doors with broken/missing vertices, and fixes them in his MiH Fixes & Restorations. Unfortunately, they were fixed with SFO so they needed to be converted to bogstandard functions. This function allows you to alter the vertices of any area structure--containers, regions, or the four sets of vertices used by doors--by deleting the existing vertices and using an external cd_define_area_vertices_array for the new vertices. This array *must* be defined prior to the function launch.

The bounding box and launch points are optional to specify, though the bounding box values are ignored for impeded door vertices since they don't have a bounding box.

Several examples are available in EEFP itself to make these door and region fixes.

Link to comment

There may be situations where you need to process data that is stored in arrays. This can be done within the main code, but it may become difficult to read and maintain eventually, and you're forced to write duplicate code if you need to perform the same operation more than once.

With the introduction of the RET_ARRAY keyword in WeiDU 245 it is possible to outsource array operations to functions. However, WeiDU documentation is very scarce about this feature and leads to the assumption that it is only possible to return newly created arrays from a function. Because of the dynamic nature of WeiDU variables this is not necessarily true. It is possible to pass arbitrary arrays to a function and return them with updated content.

This is example code to demonstrate this feature:

Spoiler
// A function for processing array content
DEFINE_ACTION_FUNCTION process_array
STR_VAR
  array_name = "" // provides the name of our array
RET_ARRAY
  "%array_name%"  // returns the array by the same name that is stored in "array_name"
BEGIN
  // Iterating over each element in our array
  // The EVAL keyword allows us to use %string% replacement for the array name
  ACTION_PHP_EACH EVAL "%array_name%" AS key => value BEGIN
    // Do something with the value
    ACTION_IF (IS_AN_INT "value") BEGIN
      OUTER_SET value = value * 2
    END ELSE BEGIN
      OUTER_SPRINT value "%value% by two"
    END

    // Result is written back to our array
    OUTER_SPRINT $EVAL "%array_name%"("%key%") "%value%"
  END
END

// The main code:
ACTION_DEFINE_ARRAY my_array BEGIN 1 2 3 4 "five" END // our arbitrary array

// Test output: 1 2 3 4 "five"
ACTION_PHP_EACH my_array AS _ => value BEGIN
  PRINT "%value%"
END

// Function call to update array elements
LAF process_array
STR_VAR
  array_name = "my_array"  // pass the name of our array as string parameter to the function
RET_ARRAY
  // Important: Name of the return value must be the same as the name assigned to "array_name".
  my_array
END

// Test output: 2 4 6 8 "five by two"
ACTION_PHP_EACH my_array AS _ => value BEGIN
  PRINT "%value%"
END

It is not only possible to update existing array elements but we can also expand the array by new elements. Theoretically it's even possible to use CLEAR_ARRAY, DEFINE_ARRAY and DEFINE_ASSOCIATIVE_ARRAY to completely clear or overwrite the array. In all of these cases we have to use EVAL to access the array itself.

Edited by argent77
Link to comment
On 10/19/2021 at 5:03 AM, DavidW said:

WEIDU is fine with multidimensional (or variable-dimensional) arrays. You can have a function that reads a 2da and spits out a 2D WEIDU array labelled by rows and columns. This is my (probably overcomplicated) version:

More than a year later ... definitely overcomplicated. The built-in operation READ_2DA_ENTRIES_NOW already does this. See here for an example use: https://www.gibberlings3.net/forums/topic/36117-weidu-question-scope-from-read_2da_entries_now/

 

Link to comment

Here's a column inserter I made for an iwd2 mod where column position matters. It supports a default value, so you can override only the needed cells.

Thanks go to CamDawg for the idea and debugging help.

Link to comment

Here's a pair of macros to fix an issue I ran into in a couple places recently:

  • fix_missing_re_table : This is a PATCH macro. Apply it to add an empty rest encounter table to any ARE that lacks that element. As this assumes SOURCE_SIZE is equal to the file's size, it should be used before any patches that alter size, such as adding or removing basically any element. As a public service, I then alter SOURCE_SIZE to reflect the new size.
  • fix_missing_re_tables : This is an ACTION macro. It applies fix_missing_re_table to all ARE files.

Why use these? Some areas, even in the base games, may lack this table. They generally have the table's offset equal to zero to mark that lack. However, this doesn't necessarily persist when changes are made; patches that change the file's size need to recalculate various offsets, and may set the table's offset to the file's size. At that point, anything that tries to read the table causes an error attempting to read out of bounds.

Notably, the fj_are_structure function included in standard WeiDU hits both sides of this. Using the function twice on an ARE that lacks a rest encounter table will cause a "read out of bounds" error.

Full code, suitable for saving as a tph and including the above as comments:

Spoiler
/* fix_missing_re_table : This is a PATCH macro. Apply it to add an empty rest encounter table to any ARE that lacks that element. As this assumes SOURCE_SIZE is equal to the file's size, it should be used before any patches that alter size, such as adding or removing basically any element. As a public service, I then alter SOURCE_SIZE to reflect the new size.

fix_missing_re_tables : This is an ACTION macro. It applies fix_missing_re_table to all ARE files.

Why use these? Some areas, even in the base games, may lack this table. They generally have the table's offset equal to zero to mark that lack. However, this doesn't necessarily persist when changes are made; patches that change the file's size need to recalculate various offsets, and may set the table's offset to the file's size. At that point, anything that tries to read the table causes an error attempting to read out of bounds.
Notably, the fj_are_structure function included in standard WeiDU hits both sides of this. Using the function twice on an ARE that lacks a rest encounter table will cause a "read out of bounds" error. */

DEFINE_PATCH_MACRO ~fix_missing_re_table~ BEGIN
	PATCH_IF (SOURCE_SIZE >= 0xe4) BEGIN // Check for too-small corrupted files
		READ_LONG 0xc0 re_offset
		PATCH_IF (re_offset = 0 OR re_offset = SOURCE_SIZE) BEGIN
			PATCH_PRINT ~Rest encounter table missing. Inserting blank table.~
			WRITE_LONG 0xc0 SOURCE_SIZE
			INSERT_BYTES SOURCE_SIZE 228 // Inserts table of all zeros
			SET SOURCE_SIZE += 228
		END
	END
END

DEFINE_ACTION_MACRO ~fix_missing_re_tables~ BEGIN
	COPY_EXISTING_REGEXP GLOB ~.*\.ARE~ ~override~
		LAUNCH_PATCH_MACRO ~fix_missing_re_table~
	BUT_ONLY
END

 

May 30: Another edit, this time adding a PATCH_PRINT command so you can see when changes are actually made. And it's actually been tested now, so I know it works.

 

Additional note: it's not just size-changing patches you have to worry about with the individual patch macro, as I found out the hard way when using this in my mod. The component involved did a bunch of complicated read-only stuff with the area before reaching the point I wanted to apply the macro, including loading other CRE resources in an INNER_ACTION. And that broke things; the SOURCE_ variables are not scope-restricted, so testing SOURCE_SIZE after all that gives the size of the latest CRE I loaded rather than the ARE I'm working on. Very troublesome, until I figured out what was going on and set SOURCE_SIZE to its proper value.

The lesson of all this? If you're going to use the patch macro here directly, make doubly sure that SOURCE_SIZE is actually the size of the ARE you're working on.

Edited by jmerry
Added a note
Link to comment

I've got a macro which can update mod-included BG1 creatures for EET audio compatibility. I don't think this is added to the EET cpmvars atm, so just sharing it here for visibility.

Unfortunately, this can't be adopted well for BGT because BGT doesn''t have a fixed offset for the BG1 strings (The migration of the creature slots would work alone, but that also needs ToBExAl).

DEFINE_PATCH_MACRO patch_eet_audio BEGIN
	PATCH_IF (GAME_IS ~eet~) BEGIN
		// add 200000 as per EET BG1 TLK references
		FOR (n = 0; n < 100; ++n) BEGIN
			READ_LONG 0xA4 + n*4 temp_str
			PATCH_IF (temp_str > 0) BEGIN
				WRITE_LONG 0xA4 + n*4 (200000 + temp_str)
			END
		END
		// move the nine-click RARE_SELECT audio to the BG2EE slots
		READ_LONG SELECT_ACTION4 rareselect1
		READ_LONG SELECT_ACTION5 rareselect2
		READ_LONG SELECT_ACTION6 rareselect3
		READ_LONG SELECT_ACTION7 rareselect4
		WRITE_LONG SELECT_ACTION4 (BNOT 0x0)
		WRITE_LONG SELECT_ACTION5 (BNOT 0x0)
		WRITE_LONG SELECT_ACTION6 (BNOT 0x0)
		WRITE_LONG SELECT_ACTION7 (BNOT 0x0)
		WRITE_LONG 0x1D0 rareselect1 /* BG2EE_SELECT_RARE1 */
		WRITE_LONG 0x1D4 rareselect2 /* BG2EE_SELECT_RARE2 */
		WRITE_LONG 0x1D8 rareselect3 /* BG2EE_SELECT_RARE3 */
		WRITE_LONG 0x1DC rareselect4 /* BG2EE_SELECT_RARE4 */
	END
END

An example usage is available at https://github.com/The-Gate-Project/AerieBG1/pull/1/commits/74f1776236ba4b63a29ef589bfb7e5bce03692c1 .

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...