Luke Posted November 10, 2021 Share Posted November 10, 2021 (edited) 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 November 10, 2021 by Luke Quote Link to comment
jmerry Posted November 27, 2021 Share Posted November 27, 2021 (edited) 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 November 27, 2021 by jmerry Quote Link to comment
tipun Posted December 3, 2021 Share Posted December 3, 2021 Hello. I have a question. Whether the weidu functions support overload mode. I just need to get more data. Quote Link to comment
subtledoctor Posted March 1, 2022 Share Posted March 1, 2022 (edited) 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 March 3, 2022 by subtledoctor Quote Link to comment
subtledoctor Posted March 3, 2022 Share Posted March 3, 2022 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. Quote Link to comment
CamDawg Posted March 3, 2022 Author Share Posted March 3, 2022 I did it for IWD Fixpack (cd_items_casting_spells defined, and actually being used) so that items which cast spells can get the underlying spell fixes. Unlike BG2, using op 146/148 on an item or spell forces a casting animation so you have to do effects directly for on-hit spells (and no subspells). Quote Link to comment
subtledoctor Posted March 3, 2022 Share Posted March 3, 2022 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! 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. Quote Link to comment
jmerry Posted May 29, 2022 Share Posted May 29, 2022 (edited) 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 January 6, 2023 by jmerry Fixed code Quote Link to comment
subtledoctor Posted June 17, 2022 Share Posted June 17, 2022 (edited) 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 June 23, 2022 by subtledoctor Quote Link to comment
CamDawg Posted October 30, 2022 Author Share Posted October 30, 2022 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. Quote Link to comment
argent77 Posted November 17, 2022 Share Posted November 17, 2022 (edited) 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 November 17, 2022 by argent77 Quote Link to comment
jmerry Posted December 29, 2022 Share Posted December 29, 2022 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/ Quote Link to comment
lynx Posted February 2, 2023 Share Posted February 2, 2023 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. function: https://github.com/lynxlynxlynx/gemrb-mods/blob/master/cleric-of-eilistraee/functions.tpa example call: https://github.com/lynxlynxlynx/gemrb-mods/blob/master/cleric-of-eilistraee/cleric-of-eilistraee.tp2#L33 Thanks go to CamDawg for the idea and debugging help. Quote Link to comment
jmerry Posted April 19, 2023 Share Posted April 19, 2023 (edited) 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 June 6, 2023 by jmerry Added a note Quote Link to comment
Graion Dilach Posted June 3, 2023 Share Posted June 3, 2023 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 . 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.