CamDawg Posted June 25, 2017 Share Posted June 25, 2017 (edited) It seems like everyone has a few macros that are fairly useful, but not quite useful enough to go bug Wisp for inclusion in WeiDU itself. I'd like to suggest a mega-thread where we can all dump anything that might be useful to others. This first post will be updated descripts and links to the (hopefully thousands, millions of) macros that get posted below.Post 2, Ardanis - ADD_SPELL_HEADER, ADD_ITEM_HEADER - Used to add abilities/headers to spells and items CREATE_EFFECT - Create effects whole cloth CREATE_SPELL - Create spells update_item_descriptions_to_bgee - Updates vanilla item descriptions to EE format, i.e. stripping Usable By section, so that you don't have to provide two strings for vanilla and EE descriptions. Post 3, CamDawg - cd_extend_bg_area_script - Used to easily extend area scripts when writing a BG/BGEE/BGT/Tutu mod - use the updated version in post 53 spell_to_innate - Used to convert arcane or divine spells into innate abilties kit.ids fixer - Not really a macro, but needed to fix kit.ids for non-EE, non-Fixpacked BG2 games cd_equip_item - finds the specified item in the creature file and equips it to the appropriate slot cd_equip_weapon - finds the first valid weapon and equips it cd_equip_weapon_specific - equips the specified weapon cd_no_pickpocket - Moves an item that should not be pickpocketed into an inventory slot that is impossible to pickpocket from. CD_EXTEND-O-MATIC - Used to arbitrarily create new spell headers out to the specified minimum level. CD_MISSING_SPELL_HEADERS - Used to 'backfill' missing spell headers Post 4, Ardanis - tooltips - Adding new tooltips for the items in a few quick strokes Post 5, Angel add_area_actor - A wrapper for fj_area_struct that simplifies even further the process of adding an actor to an area. remove_all_area_actors - Removes all actors from an area; useful for repurposing an area. add_simple_trap - Another wrapper for fj_area_struct that allow for quickly creating a trap in an area. add_area_actors_from_2da - Uses a table to mass-add actors to an area. Post 6, Angel add_energy_drain - Adds level drain to items or spells in engines that support energy drain, and pseudo-energy drain for those that don't add_magic_missile_immunity - Adds magic missile immunity to an item or spell Post 8, Angel prevent_spell_effect_stacking - Prevents a spell from stacking with itself Post 9, subtledoctor add_hla, remove_hla, replce_hla - Macros for non-destructively editing HLA tables Post 10, Angel replace_cre_script - find and replace a creature script Post 11, k4thos ADD_MAP_ICONS_EE - BAM v2 worldmap icon file patching (for EE games that use PVRZ based map icons) ADD_WORLDMAP_TBL - Adds multiple areas and links to the worldmap and saves using BP-BGT Worldmap TBL files convention Post 12, Angel make_magical - Add magical flag, lore, and enchantment level to item Post 16, argent77 TO_HEX_NUMBER - A decimal to hexadecimal number converter FIND_FREE_ANIM_SLOT - This function attempts to find a free creature animation slot in a given range Post 27, Gwendolyne GW_FIND_DLG_RESPONSE_STRING - a homemade function used to extend existing dlg Post 35, K4thos 2DA_MISSING_COLS - Patch and action function that fixes missing column entries in 2da files. Post 36, Angel make_summon - turn a creature into a summoned creature Post 39, Angel erase_journal_entries_on_bg2_transition - Finds and scripts deletion of BG journal entries during BGT's transition to BG2 Post 46, Angel make_illusion - a variant of make_summon (post 36) that makes the monster illusionary Post 49, K4thos JOINABLE_NPC_ARRAY - Action macro that generates JOINABLE_NPC_ARRAY table which can be used to patch joinable NPC CRE files (more reliable method than checking CRE BIO offset) Post 50, Sam. ps_toolset - An entire suite of functions for manipulating and patching tilesets Post 51, argent77 a7_auto_apply_spl_effect - a patch function that can be used to add multiple effects to items or spells without much effort Post 52, CamDawg CD_DOUBLE_DAMAGE - Combines separate damage opcodes to utilize EE's save-for-half damage flag Post 53, CamDawg cd_extend_bg_area_script - Used to easily extend area scripts when writing a BG/BGEE/BGT/Tutu mod, now supporting tra files Post 56, Aquadrizzt qd_multiclass - allows you to assign class features to multiclass kits in the 2.0+ engine Post 61, c4_Angel C4_FIND_STAT_SLOT is an action function, checks all creature/item/spell files in game which have effect using opcode #318 (vanilla game) or opcode #328 (EE game), also values in STATS.IDS or SPLSTATE.IDS, find an unused one from 501 to 32767, and append a line with an user defined "identifier"(requred). Return "available_stat" for further usage. C4_ADD_UNIQUE_MARK is a patch function, calls C4_FIND_STAT_SLOT, and uses function CLONE_EFFECT to add an effect to creature/item/spell file with an user defined "identifier"(also requred), other parameters of the "match_" part are same with CLONE_EFFECT. Post 68, CamDawg cd_new_summon_table (EE) - an action function, will add a new reference for you and then return the value for use in your spells cd_new_portrait_icon (EE) - another action function, will find the next available entry and add your strref and bam file to the list. Post 69, argent77 SORT_ARRAY - This macro can be used to sort arrays of numbers or strings in ascending or descending order. Post 72, K4thos REPLACE_MULTILINE - Patch function that replaces set or all occurrences of the given regexp pattern in the file with the given string. Post 73, CamDawg cd_clone_spell - Used to clone spells, updating any self-referencing protections on both the source and cloned spells. Can optionally convert spells to innates in the process. Post 74, Luke ALTER_STORE_ITEM - Alters properties of all store items matching the resource name specified by "match_resref". ALTER_CRE_ITEM - Alters properties of all inventory items matching the resource name specified by "match_resref". ALTER_AREA_ITEM - Alters properties of all area items matching the resource name specified by "match_resref". Post from subtledoctor I savagely mutilated some function (CLONE_EFFECT I think) to create a monster function that patches 206/318/321/324 effects... it's over here: Post from K4thos ADD_SPELL_EX - Action function similar to WeiDU ADD_SPELL (adding a spell of type and level into spell.ids, in the first empty slot possible), but more flexible Also my older REPLACE_MULTILINE function now supports regexp variables in output string, which works in a similar way to REPLACE_EVALUATE (see the 200 MATCHi variables explanation in weidu documentation.) Post from K4thos APPEND_TO_IDS - Action function for appending into IDS file at the first free index slot within given index range. Post from K4thos COMPILE_LUA - Action function that lets you compile BAF and D files containing Bubb's EEex Lua functions (EEex_Lua, EEex_LuaTrigger) Post from Grammarsalad ALTER_SPELL_TYPE - This one is like spell_to_innate, but allows for a few more options (including changing spell types from any kind to any other kind). Post from K4thos EXECUTE_LUA - Action and patch functions used for executing Lua code and retrieving the outcome back into WeiDU Post from subtledoctor fix_kitlist_missing_ids - A function to specifically address missing IDS values in kitlist.2da Post from argent77 TIS_RES_TO_PVRZ, TIS_RES_TO_PVRZ, UPDATE_PVRZ_INDICES, INSTALL_PVRZ, FIND_FREE_PVRZ_INDEX, FIND_FREE_PVRZ_INDEX - This is a set of functions that can be used to safely install pvrz-based tilesets without the danger of overwriting pvrz files from other mods or the game itself. It's an expanded version of the functionality already present in current WeiDU, so you can also install BAM V2 and MOS V2 files with it as well. Post from Weigo SRmap_modding_tool - I wrote a small tool and function to alter the searchmap bitmap files. Post from Weigo check_kit_conflict - I've made a drop-in-ready .tpa file containing two functions which will check whether the next kit to be installed will overlap with the IDS value of the Abjurer, Conjurer, or Diviner mage specialists Post from K4thos GENERATE_FILELIST_ARRAY - Action function for generating associative array listing all files inside specified path and all its subdirectories. Post from tipun FC_EDIT_AREA_DOOR, FC_EDIT_AREA_REGION, FC_EDIT_AREA_CONTAINER - They generally correspond to the standard weidu functions, but allow polygons to be modified. And the search for the desired structure occurs using a large number of parameters. Post from jastey GET_RESPONSE_STRREFS - This is for "finding the correct original game reply option number of a certain state number if knowing the stringref number of that reply option" Post from argent77 GET_DLG_RESPONSES - Following up on jastey's description of the GET_RESPONSE_STRREFS function above, this is a more powerful version that returns not only response strrefs but also the remaining response elements such as flags, journal entries, triggers, actions and the next dialog state. Post from CamDawg cd_add_random_treasure - I've written a function to add rows to the random treasure table Edited September 10, 2021 by CamDawg Fixed links, caught up to page 10-ish Quote Link to comment
Ardanis Posted June 25, 2017 Share Posted June 25, 2017 (edited) ADD_SPELL_HEADER ADD_ITEM_HEADER CREATE_EFFECT Also CREATE_SPELL, but it's probably not a thing many would care about. Parameters should be self-explanatory. Can also copy an existing one. I had this for items as well, but I don't think I've got the code anywhere in close vicinity. EDIT Here it is https://github.com/Gibberlings3/ItemRevisions/blob/master/item_rev/lib/macros.tpa DEFINE_PATCH_FUNCTION ~ADD_SPELL_HEADER~ INT_VAR type=1 location=4 target=1 target_count=0 range=0 required_level=1 speed=0 projectile=1 copy_header=0 insert_point=~-1~ STR_VAR icon=~~ RET insert_point BEGIN LPF ~FJ_SPL_ITM_REINDEX~ END hs=0x28 READ_LONG 0x64 ho READ_SHORT 0x68 hc READ_LONG 0x6a eo insert_point = (insert_point>hc || insert_point<0) ? hc : insert_point copy_header = (copy_header<0) ? 0 : copy_header PATCH_IF copy_header>hc BEGIN PATCH_WARN ~Unable to copy %copy_header%th header, %SOURCE_FILE% contains only %hc% headers!~ END ELSE BEGIN INSERT_BYTES ho+insert_point*hs hs hc+=1 eo+=hs PATCH_IF copy_header BEGIN READ_SHORT ho+(copy_header - 1)*hs+0x1e ec READ_SHORT ho+(copy_header - 1)*hs+0x20 ei READ_ASCII eo+ei*0x30 effs (ec*0x30) READ_ASCII ho+(copy_header - 1)*hs copy (hs) WRITE_ASCIIE ho+insert_point*hs ~%copy%~ (hs) END WRITE_SHORT 0x68 hc WRITE_LONG 0x6a eo READ_SHORT 0x70 ei // technically, it is a counter FOR (i=ho;i<ho+hc*hs;i+=hs) BEGIN READ_SHORT i+0x1e ec WRITE_SHORT i+0x20 ei ei+=ec END PATCH_IF copy_header BEGIN READ_SHORT ho+insert_point*hs+0x1e ec READ_SHORT ho+insert_point*hs+0x20 ei INSERT_BYTES eo+ei*0x30 ec*0x30 WRITE_ASCIIE eo+ei*0x30 ~%effs%~ (ec*0x30) END ELSE BEGIN off=ho+insert_point*hs WRITE_BYTE off type WRITE_BYTE off+0x2 location WRITE_ASCIIE off+0x4 ~%icon%~ (8) WRITE_BYTE off+0xc target WRITE_BYTE off+0xd target_count WRITE_SHORT off+0xe range WRITE_SHORT off+0x10 required_level WRITE_LONG off+0x12 speed WRITE_SHORT off+0x26 projectile END END END update_item_descriptions_to_bgee Updates vanilla item descriptions to EE format, i.e. stripping Usable By section, so that you don't have to provide two strings for vanilla and EE descriptions. https://github.com/Gibberlings3/ItemRevisions/blob/master/item_rev/lib/usability_description.tpa#L77 // make sure to add these two to your TRA files, adjust the numbers if needed //OUTER_SPRINT usab @900000 // ~Usable[ %tab%]+[Bb]y[ %tab%]*:~ //OUTER_SPRINT unus @900001 // ~\(Not[ %tab%]+\|Un\)[Uu]sable[ %tab%]+[Bb]y[ %tab%]*:~ OUTER_SPRINT usab ~Usable[ %tab%]+[Bb]y[ %tab%]*:~ OUTER_SPRINT unus ~\(Not[ %tab%]+\|Un\)[Uu]sable[ %tab%]+[Bb]y[ %tab%]*:~ DEFINE_PATCH_FUNCTION ~update_item_descriptions_to_bgee~ BEGIN PATCH_IF (ENGINE_IS ~bgee bg2ee~) BEGIN FOR (index = 0x54 ; index >= 0x50 ; index -= 4) BEGIN // loop through descriptions READ_LONG index strref PATCH_IF (strref < 2147483646 && strref >= 0) BEGIN // verify description is valid READ_STRREF index description INNER_PATCH_SAVE new_desc ~%description%~ BEGIN REPLACE_TEXTUALLY ~\(\([%LNL%%MNL%%WNL%][ %TAB%]*\(%usab%\|%unus%\)[ %TAB%]*\)\(\([%LNL%%MNL%%WNL%].*\)*\)?\)~ ~~ END SAY_EVALUATED index ~%new_desc%~ END END END END Example usage 1, by item: COPY_EXISTING ~mymod/myitem.itm~ override SAY_DESC ~Whatever~ LPF update_item_descriptions_to_bgee END Example usage 2, batch update (caution, only really useful if you've got a simple content mod and don't do any level 80 patching: // run this after all items have been copied into the game, i.e. near the end of tp2 ACTION_IF (ENGINE_IS ~bgee bg2ee~) BEGIN ACTION_FOR_EACH directory IN items itm any_other_subfolder_that_contains_installable_items BEGIN ACTION_BASH_FOR ~MyModFolder/%directory%~ ~.*\.itm~ BEGIN // get a list of .itm files in the mod directory ACTION_IF FILE_EXISTS_IN_GAME ~%BASH_FOR_FILE%~ BEGIN // if those files have been copied into override COPY_EXISTING ~%BASH_FOR_FILE%~ override // load those files from override, i.e. with descriptions already SAY'ed LPF update_item_descriptions_to_bgee END // patch the description BUT_ONLY END END END END Edited June 25, 2017 by Ardanis Quote Link to comment
CamDawg Posted June 25, 2017 Author Share Posted June 25, 2017 This one I wrote from frustration of trying to track down which areas have area scripts assigned, what they are, etc. when trying to write a mod for BG/BGEE/BGT/Tutu. DEFINE_ACTION_FUNCTION cd_extend_bg_area_script INT_VAR extend_top = 0 STR_VAR area = "" script = "" BEGIN // make sure we have area scripts assigned COPY_EXISTING ~%area%.are~ ~override~ READ_ASCII 0x94 a_script PATCH_IF ("%script%" STRING_COMPARE_CASE ~~ = 0) BEGIN // if blank PATCH_IF GAME_IS ~tutu tutu_totsc~ BEGIN // if Tutu WRITE_ASCIIE 0x95 ~%SOURCE_RES%~ #7 WRITE_ASCII 0x94 ~_ar~ END ELSE BEGIN // bgt WRITE_ASCIIE 0x94 ~%SOURCE_RES%~ #8 END READ_ASCII 0x94 a_script END BUT_ONLY ACTION_IF extend_top = 1 THEN BEGIN EXTEND_TOP ~%a_script%.bcs~ ~%script%.baf~ EVALUATE_BUFFER END ELSE BEGIN EXTEND_BOTTOM ~%a_script%.bcs~ ~%script%.baf~ EVALUATE_BUFFER END END Invoked like so LAF cd_extend_bg_area_script STR_VAR area = EVAL "%Ulcaster%" script = ~SUPERSECRETMOD/baf/et_3900~ END This is using the excellent g3_xx_cpmvars libraries to set Ulcaster to the correct area. This will basically look in the area file, find the area script, assign one if none is assigned, and then EXTEND_TOP or _BOTTOM.The classic kit.ids fixer; include this to fix kit.ids in an un-Fixpacked or non-EE game: ///// \\\\\ ///// kit.ids fixer \\\\\ ///// \\\\\ COPY_EXISTING ~kit.ids~ ~override~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(TRUECLASS[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4000 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BERSERKER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4001 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(WIZARDSLAYER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4002 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(KENSAI[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4003 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(CAVALIER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4004 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(INQUISITOR[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4005 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(UNDEADHUNTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4006 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ABJURER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0040 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_CONJURER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0080 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_DIVINER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0100 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ENCHANTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0200 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ILLUSIONIST[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0400 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_INVOKER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0800 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_NECROMANCER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x1000 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_TRANSMUTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x2000 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_GENERALIST[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4000 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(FERALAN[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4007 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(STALKER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4008 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BEASTMASTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4009 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(ASSASIN[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400A \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BOUNTYHUNTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400B \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SWASHBUCKLER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400C \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BLADE[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400D \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(JESTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400E \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SKALD[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x400F \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODTALOS[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4013 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODHELM[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4014 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODLATHANDER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4015 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(TOTEMIC[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4010 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SHAPESHIFTER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4011 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BEASTFRIEND[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x4012 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BARBARIAN[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x40000000 \1~ REPLACE_TEXTUALLY ~^.+[ %TAB%]\(WILDMAGE[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x80000000 \1~ BUT_ONLY APPEND ~kit.ids~ ~0x4000 TRUECLASS~ UNLESS ~^.+[ %TAB%]TRUECLASS[ %TAB%%LNL%%MNL%%WNL%]+~ APPEND ~kit.ids~ ~0x40000000 BARBARIAN~ UNLESS ~^.+[ %TAB%]BARBARIAN[ %TAB%%LNL%%MNL%%WNL%]+~ APPEND ~kit.ids~ ~0x80000000 WILDMAGE~ UNLESS ~^.+[ %TAB%]WILDMAGE[ %TAB%%LNL%%MNL%%WNL%]+~ ACTION_FOR_EACH crefile IN _imoen1 _imoen2 _imoen4 _imoen6 anomen10 anomen12 anomen6 anomen7 anomen8 anomen9 haer10 haer11 haer13 haer15 haer19 imoen1 imoen10 imoen15 imoen2 imoen211 imoen213 imoen4 imoen61 korgan11 korgan12 korgan15 korgan8 korgan9 mazzy11 mazzy12 mazzy15 mazzy8 mazzy9 BEGIN ACTION_IF FILE_EXISTS_IN_GAME ~%crefile%.cre~ BEGIN COPY_EXISTING ~%crefile%.cre~ ~override~ READ_LONG 0x0244 KIT_VALUE PATCH_IF (KIT_VALUE = 0x00) BEGIN WRITE_LONG 0x0244 0x40000000 END ELSE PATCH_IF (KIT_VALUE = 0x20) BEGIN WRITE_LONG 0x0244 0x400d0000 END ELSE PATCH_IF (KIT_VALUE = 0x010000) BEGIN WRITE_LONG 0x0244 0x40010000 END BUT_ONLY END END I wrote a lot for Fixpack for fixing creature inventories: DEFINE_PATCH_FUNCTION cd_equip_item // defines what we're going to check INT_VAR move = 0 gpuse = 0 STR_VAR item = "same" slot = "helmet" BEGIN SET fruitbats = "-1" PATCH_IF ("%slot%" STRING_COMPARE_CASE "helmet" = 0) BEGIN SET start = 0 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "armor" = 0) BEGIN SET start = 1 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "shield" = 0) BEGIN SET start = 2 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "gloves" = 0) BEGIN SET start = 3 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "rings" = 0) BEGIN SET start = 4 SET fruitbats = 5 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "amulet" = 0) BEGIN SET start = 6 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "jewelry" = 0) BEGIN SET start = 4 SET fruitbats = 6 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "belt" = 0) BEGIN SET start = 7 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "boots" = 0) BEGIN SET start = 8 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "weapon" = 0) BEGIN SET start = 9 SET fruitbats = 12 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "quiver" = 0) BEGIN SET start = 13 SET fruitbats = 15 END ELSE PATCH_IF ("%slot%" STRING_COMPARE_CASE "quickslot" = 0) BEGIN SET start = 18 SET fruitbats = 20 END ELSE BEGIN // inventory PATCH_IF gpuse = 0 BEGIN // creatures with gpuse scripts will try to use items in the first inventory slots SET start = 21 END ELSE BEGIN SET start = 23 END SET fruitbats = 0 END PATCH_IF fruitbats < 0 BEGIN SET fruitbats = start END READ_LONG 0x2b8 slot_off ELSE 0 READ_LONG 0x2bc itm_off ELSE 0 READ_LONG 0x2c0 itm_num ELSE 0 FOR (index = 0 ; index < itm_num ; ++index) BEGIN READ_ASCII (itm_off + (0x14 * index)) test_item PATCH_IF ("%item%" STRING_COMPARE_CASE "%test_item%" = 0) BEGIN SET proceed = 1 FOR (index2 = 0 ; index2 < 36 ; ++index2) BEGIN // first make a loop to make sure it's not assigned somwhere already READ_SHORT (slot_off + (index2 * 0x02)) ref PATCH_IF ref = index BEGIN PATCH_IF move = 1 BEGIN WRITE_SHORT (slot_off + (index2 * 0x02)) 0xffff END ELSE BEGIN SET proceed = 0 END END END PATCH_IF proceed BEGIN FOR (index2 = start ; index2 < 36 ; ++index2) BEGIN // first make a loop to make sure it's not assigned somwhere already READ_SHORT (slot_off + (index2 * 0x02)) ref PATCH_IF (ref = 0xffff) BEGIN // if null reference in targeted slot WRITE_SHORT (slot_off + (index2 * 0x02)) index // adds reference to item SET index2 = 36 // kills loop END PATCH_IF (index2 = fruitbats) BEGIN // if end of possible equipment slots, skip ahead to inventory PATCH_IF gpuse = 0 BEGIN // creatures with gpuse scripts will try to use items in the first inventory slots SET index2 = 20 // otherwise go to inventory slots END ELSE BEGIN SET index2 = 22 // otherwise go to inventory slots END END END END END END END // run this on creatures with invalid selected weapons; it'll check the weapon slots and update the equipped weapon as needed DEFINE_PATCH_FUNCTION cd_equip_weapon BEGIN READ_LONG 0x2b8 slot_off ELSE 0 READ_LONG 0x2c0 itm_num ELSE 0 WRITE_SHORT (slot_off + 0x4c) 0xffff // null equipped weapon - either patch below will enter a valid value, or no valid value exists FOR (index = 0 ; index < 4 ; ++index) BEGIN // search through weapon slots READ_SHORT (slot_off + 0x12 + (index * 0x02)) ref PATCH_IF ((ref != 0xffff) AND (ref < itm_num)) BEGIN // if valid reference in weapon slots // PATCH_IF (ref != 0xffff) BEGIN // if valid reference in weapon slots WRITE_SHORT (slot_off + 0x4c) index // equipped weapon SET "index" = 4 // kills loop and prevents next patch_if END END END // run this on creatures with invalid selected weapons; it'll check the weapon slots and update the equipped weapon as needed DEFINE_PATCH_FUNCTION cd_equip_weapon_specific // defines what we're going to check STR_VAR item = "" BEGIN READ_LONG 0x2b8 slot_off READ_LONG 0x2bc itm_off FOR (index = 0; index < 4; ++index) BEGIN // cycles through weapon slots READ_SHORT (slot_off + 0x12 + (index * 0x02)) slot_num PATCH_IF (slot_num < 37) BEGIN READ_ASCII (itm_off + (slot_num * 0x14)) weapon PATCH_IF ("%item%" STRING_COMPARE_CASE "%weapon%" = 0) BEGIN // if long sword WRITE_SHORT (slot_off + 0x4c) index // sets weapon slot to selected weapon SET index = 4 END END END END // this function moves an item in a creture file into a slot where it can't be pickocketed // also used to move items out of helmet slot for non-legit crit protection DEFINE_PATCH_FUNCTION cd_no_pickpocket // defines what we're going to check STR_VAR item = "" BEGIN READ_LONG 0x2b8 slot_off ELSE 0 READ_LONG 0x2bc itm_off ELSE 0 READ_LONG 0x2c0 itm_num ELSE 0 SET added = 0 FOR (index = 0 ; index < itm_num ; ++index) BEGIN READ_ASCII (itm_off + (0x14 * index)) file_item PATCH_IF ("%item%" STRING_COMPARE_CASE "%file_item%" = 0) BEGIN FOR (index2 = 0 ; index2 < 36 ; ++index2) BEGIN // search through item slots; first pass finds and/or removes READ_SHORT (slot_off + (0x02 * index2)) ref PATCH_IF (ref = index) BEGIN // if item in question PATCH_IF ((index2 = 1) OR (index2 = 3) OR ((index2 > 6) AND (index2 < 18))) BEGIN // if legit slot (1, 3, 7-17) SET added = 1 END ELSE BEGIN WRITE_SHORT (slot_off + (0x02 * index2)) 0xffff // nulls reference END END END PATCH_IF added = 0 BEGIN // if not in a legit slot to begin with FOR (index2 = 1 ; index2 < 36 ; ++index2) BEGIN // search through item slots (skipping helmet at 0); first pass finds and/or removes PATCH_IF (index2 = 2) BEGIN SET index2 = 3 END // skip shield slot PATCH_IF (index2 = 4) BEGIN SET index2 = 7 END // skip rings & amulet PATCH_IF (index2 = 18) BEGIN SET index2 = 21 END // skip quick slots READ_SHORT (slot_off + (0x02 * index2)) ref PATCH_IF (ref = 0xffff) BEGIN // if empty, legit slot WRITE_SHORT (slot_off + (0x02 * index2)) index // adds reference SET index = itm_num // kill loops SET index2 = 36 // kill loops END END END END END END cd_equip_item simple finds the specified item in the creature file and equips it to the appropriate slot. cd_equip_weapon finds the first valid weapon and equips it; cd_equip_weapon_specific does the same but only with the item you specify. cd_no_pickpocket move an item that should not be pickpocketed into an inventory slot that is impossible to pickpocket from. Fixpack also had to do a lot of fixes for spells with missing headers. Sometimes these were headers at the end of the spell (e.g. spell had headers through level 18, but was missing 19 and 20), other times they were missing low-level headers (e.g. spell had headers for 1, 10, 11, etc. but also needed 8 and 9). Enter CD_EXTEND-O-MATIC (patent pending) for the former case, and CD_MISSING_SPELL_HEADERS for the latter: ///// \\\\\ ///// CD_EXTEND-O-MATIC, patent pending \\\\\ ///// \\\\\ // this function creates headers and does basic duration extensions to level 30; written for inwd-in-bg2 ages ago DEFINE_PATCH_FUNCTION CD_EXTEND-O-MATIC INT_VAR base_dur = 0 // constant to add to all durations step_dur = 6 // how much duration to add to each consecutive header step_size = 1 // how many levels between headers level_cap = 20 // stop extending at level min_dur = 4 // ignore effects with durations less than this (e.g. cosmetics) dur_special = 0 // just add step_dur to existing effects (ignore base_dur) min_lev_alt = 0 // if extending from level 1, use this value as its minimum level instead (e.g. fifth slevel spell with only one header, use 9 here) BEGIN READ_LONG 0x64 abil_off READ_SHORT 0x68 abil_num READ_LONG 0x6a fx_off READ_SHORT (abil_off + 0x10 + (0x28 * (abil_num - 1))) min_lev // read level of last ability PATCH_IF ((min_lev = 1) AND (min_lev_alt != 0)) BEGIN SET min_lev = min_lev_alt END FOR (index = min_lev + step_size ; index < (level_cap + 1) ; index = index + step_size) BEGIN READ_ASCII (abil_off + (0x28 * (abil_num - 1))) abil (0x28) // read entire ability READ_SHORT (abil_off + 0x1e + (0x28 * (abil_num - 1))) abil_fx_num READ_SHORT (abil_off + 0x20 + (0x28 * (abil_num - 1))) abil_fx_idx READ_ASCII (fx_off + (0x30 * abil_fx_idx)) effects (abil_fx_num * 0x30) // read entire fx block INSERT_BYTES (fx_off + (0x30 * (abil_fx_idx + abil_fx_num))) (abil_fx_num * 0x30) // insert bytes for new ability WRITE_ASCIIE (fx_off + (0x30 * (abil_fx_idx + abil_fx_num))) "%effects%" // write in effects block FOR (index2 = 0 ; index2 < abil_fx_num ; ++index2) BEGIN READ_LONG (fx_off + 0x0e + (0x30 * (abil_fx_idx + abil_fx_num + index2))) duration PATCH_IF (duration > min_dur) BEGIN // exclude instant/cosmetic efects PATCH_IF dur_special = 1 BEGIN SET new_dur = duration + step_dur END ELSE BEGIN SET new_dur = (base_dur + (index * step_dur)) END WRITE_LONG (fx_off + 0x0e + (0x30 * (abil_fx_idx + abil_fx_num + index2))) new_dur // adjust durations END END INSERT_BYTES (abil_off + (0x28 * abil_num)) 0x28 // insert new ability WRITE_ASCIIE (abil_off + (0x28 * abil_num)) "%abil%" WRITE_SHORT (abil_off + 0x10 + (0x28 * abil_num)) index WRITE_SHORT (abil_off + 0x20 + (0x28 * abil_num)) (abil_fx_idx + abil_fx_num) SET abil_num += 1 SET fx_off += 0x28 END WRITE_SHORT 0x68 abil_num WRITE_LONG 0x6a fx_off END ///// \\\\\ ///// CD_MISSING_SPELL_HEADERS \\\\\ ///// \\\\\ // this function is useful for filling in missing spell headers between existing headers // you'll pretty much have to run a series of ALTER_EFFECTs to fix the inserted effects, though DEFINE_PATCH_FUNCTION CD_MISSING_SPELL_HEADERS INT_VAR first_missing = 0 // first missing level last_missing = 0 // last missing level BEGIN // sanity check PATCH_IF ((first_missing != 0) AND (last_missing != 0) AND (first_missing <= last_missing)) BEGIN // create headers first, then fix effects READ_LONG 0x64 abil_off READ_SHORT 0x68 abil_num READ_LONG 0x6a fx_off CLEAR_ARRAY cd_levels FOR (index = first_missing ; index < (last_missing + 1) ; ++index) BEGIN DEFINE_ASSOCIATIVE_ARRAY cd_levels BEGIN ~%index%~ => 0 END END SET new_abil = 0 FOR (index = 0 ; index < abil_num ; ++index) BEGIN READ_SHORT (abil_off + 0x10 + (0x28 * index)) min_lev PATCH_IF (min_lev < first_missing) BEGIN // last header before gap, keep overwriting as needed READ_SHORT (abil_off + 0x1e + (0x28 * index)) abil_fx_num READ_SHORT (abil_off + 0x20 + (0x28 * index)) abil_fx_idx READ_ASCII (abil_off + (0x28 * index)) abil_clone (0x28) READ_ASCII (fx_off + (0x30 * (abil_fx_idx))) fx_clone (0x30 * abil_fx_num) SET new_fx = abil_fx_num SET start_fx = (abil_fx_idx + abil_fx_num) SET insert = (abil_off + (0x28 * (index + 1))) END ELSE PATCH_IF (min_lev >= first_missing) AND (min_lev <= last_missing) BEGIN DEFINE_ASSOCIATIVE_ARRAY cd_levels BEGIN "%min_lev%" => 1 END END END PATCH_PHP_EACH cd_levels AS level => exist BEGIN PATCH_IF (exist = 0) BEGIN INSERT_BYTES ((fx_off + (0x28 * new_abil)) + (0x30 * (start_fx * (new_abil + 1)))) (0x30 * new_fx) WRITE_ASCIIE ((fx_off + (0x28 * new_abil)) + (0x30 * (start_fx * (new_abil + 1)))) "%fx_clone%" INSERT_BYTES (insert + (0x28 * new_abil)) 0x28 WRITE_ASCIIE (insert + (0x28 * new_abil)) "%abil_clone%" WRITE_SHORT (insert + 0x10 + (0x28 * new_abil)) level WRITE_SHORT (insert + 0x1e + (0x28 * new_abil)) new_fx WRITE_SHORT (insert + 0x20 + (0x28 * new_abil)) (start_fx * (new_abil + 1)) SET new_abil += 1 END END PATCH_IF (new_abil > 0) BEGIN SET abil_num += new_abil WRITE_SHORT 0x68 abil_num WRITE_LONG 0x6a (fx_off + (0x28 * new_abil)) FOR (index = 0 ; index < abil_num ; ++index) BEGIN READ_SHORT (abil_off + 0x10 + (0x28 * index)) min_lev PATCH_IF (min_lev > last_missing) BEGIN // if after new inserted effects WRITE_SHORT (abil_off + 0x20 + (0x28 * index)) (THIS + (new_abil * new_fx)) END END END END END These will do rudimentary extensions; generally they'll nail it if the only thing that changes between headers is duration. For anything else, I usually had to follow up with a loop of ALTER_EFFECT. Fixpack has numerous examples of both in action. The EXTEND-O-MATIC in particular could probably be used to knock out a new Spell-50 mod pretty easily. And finally, an old, old friend: spell_to_innate. // change regular spell to innate DEFINE_PATCH_MACRO ~spell_to_innate~ BEGIN READ_LONG 0x64 abil_off ELSE 0 READ_SHORT 0x68 abil_num ELSE 0 READ_ASCII (abil_off + 0x04) "bam" (8) // reads the bam filename from ability WRITE_SHORT 0x1c 4 // sets spell type to innate (4) WRITE_LONG 0x34 1 // sets spell level to 1 to avoid scripting issues WRITE_ASCIIE 0x3a "%bam%" #8 // writes the bam filename from abilities to spell icon FOR (index = 0 ; index < abil_num ; ++index) BEGIN WRITE_SHORT (abil_off + 0x02 + (0x28 * index)) 4 // changes ability icon location to innate (4) END END This is useful for converting arcane/divine spells into innate abilities, particularly for kits. Quote Link to comment
Ardanis Posted June 25, 2017 Share Posted June 25, 2017 Adding new tooltips for the items in a few quick strokes. Usage example is inside the link https://github.com/Gibberlings3/ItemRevisions/blob/master/item_rev/lib/tooltip_macro.tpa Quote Link to comment
Angel Posted June 26, 2017 Share Posted June 26, 2017 Add a new actor to an area. This is a simple wrapper around fj_area_struct that saves having to type a bunch of variables. DEFINE_PATCH_FUNCTION add_area_actor BEGIN PATCH_IF FILE_EXISTS_IN_GAME "%cre_resref%.cre" BEGIN INNER_PATCH_FILE "%cre_resref%.cre" BEGIN READ_STRREF 0x0008 cre_name END END ELSE BEGIN PATCH_FAIL "Adding non-existent actor %cre_resref% to area!" END LAUNCH_PATCH_FUNCTION fj_are_structure INT_VAR fj_loc_x = x_position fj_loc_y = y_position fj_dest_x = x_position fj_dest_y = y_position fj_orientation = orientation STR_VAR fj_structure_type = "actor" fj_name = "%cre_name%" fj_cre_resref = "%cre_resref%" END END Clear all actors from an area. Useful if you want to give a generic house in BG1 some new purpose. DEFINE_PATCH_MACRO remove_all_area_actors BEGIN FOR (i = SHORT_AT 0x0058; i > 0; --i) BEGIN LAUNCH_PATCH_FUNCTION fj_are_structure INT_VAR fj_delete_mode = i - 1 STR_VAR fj_structure_type = "actor" END END END Add a simple trap to an area. Care should be taken that the trap form a long thin rectangle, or thieves may not be able to get close enough to disarm it. Another wrappper around fj_area_struct to save typing because I'm lazy. :-) DEFINE_PATCH_FUNCTION add_simple_trap INT_VAR trap_detect = 10 trap_remove = 10 STR_VAR trap_script = "gtar" BEGIN PATCH_IF ll_x < ul_x BEGIN SET min_x = ll_x END ELSE BEGIN SET min_x = ul_x END PATCH_IF lr_x > ur_x BEGIN SET max_x = lr_x END ELSE BEGIN SET max_x = ur_x END PATCH_IF ul_y < ur_y BEGIN SET min_y = ul_y END ELSE BEGIN SET min_y = ur_y END PATCH_IF ll_y > lr_y BEGIN SET max_y = ll_y END ELSE BEGIN SET max_y = lr_y END LAUNCH_PATCH_FUNCTION fj_are_structure INT_VAR fj_type = 0 // Trap fj_box_left = min_x fj_box_top = min_y fj_box_right = max_x fj_box_bottom = max_y fj_trap_active = 1 fj_loc_x = (min_x + max_x) / 2 fj_loc_y = (min_y + max_y) / 2 fj_alt_x = (min_x + max_x) / 2 fj_alt_y = (min_y + max_y) / 2 fj_trap_detect = trap_detect fj_trap_remove = trap_remove fj_flags = BIT3 fj_vertex_0 = ul_x + (ul_y << 16) fj_vertex_1 = ur_x + (ur_y << 16) fj_vertex_2 = lr_x + (lr_y << 16) fj_vertex_3 = ll_x + (ll_y << 16) STR_VAR fj_structure_type = "region" fj_name = "%trap_name%" fj_reg_script = "%trap_script%" END END Batch-add new actors to an area from a table. Table takes the form <resref> <xpos> <ypos> <orientation>. Uses add_area_actor(). DEFINE_PATCH_FUNCTION add_area_actors_from_2da STR_VAR path_to_2da = "none" BEGIN PATCH_IF FILE_EXISTS "%path_to_2da%" BEGIN INNER_PATCH_FILE "%path_to_2da%" BEGIN COUNT_2DA_COLS cols COUNT_2DA_ROWS cols rows READ_2DA_ENTRIES_NOW __actor_data cols END FOR (i = 0; i < rows; ++i) BEGIN READ_2DA_ENTRY_FORMER __actor_data i 0 cre_resref READ_2DA_ENTRY_FORMER __actor_data i 1 x_position READ_2DA_ENTRY_FORMER __actor_data i 2 y_position READ_2DA_ENTRY_FORMER __actor_data i 3 orientation LAUNCH_PATCH_FUNCTION add_area_actor INT_VAR x_position y_position orientation STR_VAR cre_resref END END END ELSE BEGIN PATCH_FAIL "add_area_actors_from_2da called with invalid path %path_to_2da%!" END END Quote Link to comment
Angel Posted June 26, 2017 Share Posted June 26, 2017 (edited) My version of the "extend-o-matic", written for two personal mods, one that makes healing spells scale with level and the other to make Flame Blade behave as the 3E version. I'll probably switch to CamDawg's since it is superior. This one only works with spells that have a single header and does not update them in any way. EDIT: Forget mine, CamDawg's version is superior in every way. It even has a much cooler name. ^^ Add a level-drain effect to an item or spell, and patch in a simulate for games that don't support opcode 216 (vanilla BG and IWD). The simulate lowers THAC0 by 2 and max. HP by 5 for 8 hours. Written for a personal mod to make Wraith Spiders more like their PnP version. EDIT: Turns out that in BG2, the level drain icon is set by the engine when opcode 216 is used. So, don't set it ourselves as it won't be removed. DEFINE_PATCH_FUNCTION add_energy_drain INT_VAR levels_drained = 1 BEGIN READ_ASCII 0x0000 signature (4) PATCH_MATCH "%signature%" WITH "ITM " WHEN ENGINE_IS "bg1 totsc iwd1 how totlm" BEGIN LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 18 // HP: Maximum HP Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 5) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 54 // Stat: THAC0 Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 2) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 142 // Graphics: Display Special Effect Icon target = 2 // Pre-target duration = 2400 parameter2 = 53 END END "ITM " BEGIN LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 216 // Spell Effect: Level Drain target = 2 // Pre-target timing = 1 // Permanent parameter1 = levels_drained END END "SPL " WHEN ENGINE_IS "bg1 totsc iwd1 how totlm" BEGIN LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 18 // HP: Maximum HP Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 5) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 54 // Stat: THAC0 Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 2) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 142 // Graphics: Display Special Effect Icon target = 2 // Pre-target duration = 2400 parameter2 = 53 END END "SPL " BEGIN LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR type = 1 // Melee opcode = 216 // Spell Effect: Level Drain target = 2 // Pre-target timing = 1 // Permanent parameter1 = levels_drained END END DEFAULT PATCH_FAIL "add_energy_drain() used on incompatible file!" END END Add magic missile immunity to an item or spell. Has to be given match_opcode to determine which effect header will be cloned.First, have this in your ALWAYS block somewhere. This finds 'Magic Missile'-like spells in BG1, BG2 and IWD1.('Improved Magic Missile' is from a personal mod of mine, it's a third level version that shoots up to 11 missiles. Yes, the magic missile projectile actually goes that high in the BG and IWD engines. ^^). // List of spells to be blocked by the Shield spell, like Magic Missile, Mordenkainen's Force Missiles, etc. ACTION_FOR_EACH spell_name IN "WIZARD_MAGIC_MISSILE" "WIZARD_IMPROVED_MAGIC_MISSILE" "WIZARD_MORDENKAINENS_FORCE_MISSILES" "GORION_MAGIC_MISSILE" "BEHOLDER_MAGIC_MISSILE" "TRAP_MAGIC_MISSILE" "TRAP_MAGIC_MISSILE_5" BEGIN ACTION_IF IDS_OF_SYMBOL ("spell" "%spell_name%") >= 0 BEGIN LAUNCH_ACTION_FUNCTION RES_NUM_OF_SPELL_NAME STR_VAR spell_name RET spell_num spell_res END PRINT "Found %spell_name% as %spell_res%" ACTION_DEFINE_ASSOCIATIVE_ARRAY magic_missiles BEGIN "%spell_name%" => "%spell_res%" END END END And the actual function: DEFINE_PATCH_FUNCTION add_magic_missile_immunity INT_VAR match_opcode = 0 // Stat:AC against specific BEGIN PATCH_FOR_EACH projectile IN 36 67 68 69 70 71 72 73 74 75 76 77 BEGIN LAUNCH_PATCH_FUNCTION CLONE_EFFECT INT_VAR match_opcode multi_match = 1 // Just once, please silent = 1 // Don't care about warnings opcode = 83 // Protection: From Projectile parameter1 = 0 // Irrelevant parameter2 = projectile STR_VAR insert = "last" END END PATCH_IF NOT ENGINE_IS "bg1 totsc" BEGIN PHP_EACH magic_missiles AS spell_name => spell_res BEGIN LAUNCH_PATCH_FUNCTION CLONE_EFFECT INT_VAR match_opcode multi_match = 1 // Just once, please silent = 1 // Don't care about warnings opcode = 206 // Spell: Protection From Spell parameter1 = 0 parameter2 = 0 STR_VAR insert = "last" resource = "%spell_res%" END END END END Prevent a spell from stacking with itself. Does not work with vanilla BG1 since it lacks opcode 206, but it does work with IWD1. Has to be given a match_opcode to find a suitable header to clone. EDIT: Use the improved version below, it plays nicer with IWD1 and EE games. Edited July 19, 2017 by Angel Quote Link to comment
CamDawg Posted June 26, 2017 Author Share Posted June 26, 2017 My version of the "extend-o-matic", written for two personal mods, one that makes healing spells scale with level and the other to make Flame Blade behave as the 3E version. I'll probably switch to CamDawg's since it is superior. This one only works with spells that have a single header and does not update them in any way. Yeah, something similar was the prompting for me to make the post. I thought my BG area script extender was quite clever, shared it, and realized that others had already done it long ago and I had wasted time reinventing the wheel. Prevent a spell from stacking with itself. Does not work with vanilla BG1 since it lacks opcode 206, but it does work with IWD1. Has to be given a match_opcode to find a suitable header to clone. I'd like to offer two refinements here, if I may. Both IWD1 and the EEs have superior ways of handling spell stacking, namely the 'Remove effects by resource' opcode (IIRC 254 for IWD, and 321 for EEs). Put it first in the effects stack (timing mode 0, duration 0) and you'll have a vastly superior stacking solution for the engines that support it. Second, I'd set the default match_opcode to 142--Fixpack patches 20+ spells to not self-stack, and 142 works for all but three of them. Quote Link to comment
Angel Posted June 26, 2017 Share Posted June 26, 2017 (edited) Prevent a spell from stacking with itself. Does not work with vanilla BG1 since it lacks opcode 206, but it does work with IWD1. Has to be given a match_opcode to find a suitable header to clone. I'd like to offer two refinements here, if I may. Both IWD1 and the EEs have superior ways of handling spell stacking, namely the 'Remove effects by resource' opcode (IIRC 254 for IWD, and 321 for EEs). Put it first in the effects stack (timing mode 0, duration 0) and you'll have a vastly superior stacking solution for the engines that support it. Second, I'd set the default match_opcode to 142--Fixpack patches 20+ spells to not self-stack, and 142 works for all but three of them. Heh, you certainly may! I've only recently begun to extend my meddlings to IWD1, so I have yet to learn all the subtleties of it. (Not that I claim to know them for BG, mind you!) As for the EE games, I have yet to touch them at all. It's on my to-do list, just hesitant to spend more money on games I already own. But knowing myself, I'll probably give in sooner or later. :-) I checked IESDP and it looks like you got the codes exactly right, so here's the adapted version: DEFINE_PATCH_FUNCTION prevent_spell_effect_stacking INT_VAR match_opcode = 142 BEGIN PATCH_IF ENGINE_IS "iwd1 how totlm" BEGIN LAUNCH_PATCH_FUNCTION CLONE_EFFECT INT_VAR match_opcode multi_match = 1 silent = 1 // Don't care about warnings opcode = 254 // Remove effects by resource timing = 0 // Duration duration = 0 parameter1 = 0 parameter2 = 0 STR_VAR insert = "first" resource = "%SOURCE_RES%" END END PATCH_IF ENGINE_IS "bgee bg2ee" BEGIN LAUNCH_PATCH_FUNCTION CLONE_EFFECT INT_VAR match_opcode multi_match = 1 silent = 1 // Don't care about warnings opcode = 321 // Remove effects by resource timing = 0 // Duration duration = 0 parameter1 = 0 parameter2 = 0 STR_VAR insert = "first" resource = "%SOURCE_RES%" END END PATCH_IF ENGINE_IS "bg2 tob" BEGIN LAUNCH_PATCH_FUNCTION CLONE_EFFECT INT_VAR match_opcode multi_match = 1 silent = 1 // Don't care about warnings opcode = 206 // Spell Effect: Immunity Spell parameter1 = RESOLVE_STR_REF ("Multiple castings of this spell have no effect.") parameter2 = 0 // Default (IWD1) STR_VAR insert = "last" resource = "%SOURCE_RES%" END END END Edited June 26, 2017 by Angel Quote Link to comment
subtledoctor Posted June 28, 2017 Share Posted June 28, 2017 (edited) Not sure if this actually counts, but I made a set of functions to create and customize HLA tables in a non-destructive manner: https://github.com/UnearthedArcana/Modify_HLAs DEFINE_ACTION_FUNCTION add_hla STR_VAR kit_name = ~~ 2da_row = ~1~ ability = ~*~ icon = ~*~ strref = ~*~ min_lev = ~1~ max_level = ~99~ num_allowed = ~*~ prerequisite = ~*~ excluded_by = ~*~ alignment_restrict = ~*~ BEGIN COPY_EXISTING ~luabbr.2da~ ~override~ COUNT_2DA_COLS l_cols // amount of columns READ_2DA_ENTRIES_NOW l_rows l_cols // read all file into memory FOR (l_row = 1; l_row < l_rows; ++l_row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER l_rows l_row 0 ~l_kit~ // read column value PATCH_IF ~%l_kit%~ STRING_EQUAL_CASE ~%kit_name%~ BEGIN SET lu_row = %l_row% READ_2DA_ENTRY_FORMER l_rows lu_row 1 ~l_table~ // read column value END END BUT_ONLY ACTION_IF FILE_EXISTS_IN_GAME ~lu%l_table%.2da~ BEGIN COPY_EXISTING ~lu%l_table%.2da~ ~override/lud5_%lu_row%.2da~ COUNT_2DA_COLS cols // amount of columns COUNT_2DA_ROWS cols rows // amount of rows READ_2DA_ENTRIES_NOW file cols // read all file into memory first_empty_row = rows // default value to amount of rows in order to skip removal if the table is full FOR (i = 0; i < file; ++i) BEGIN // iterate over rows SET empty_col_count = 0 // amount of empty columns in the row FOR (j = 0; j < cols; ++j) BEGIN // iterate over columns in the row READ_2DA_ENTRY_FORMER file i j col_value // read column value PATCH_IF "%col_value%" STRING_EQUAL "*" BEGIN // asterisk symbolizes empty column empty_col_count += 1 END END PATCH_IF "%empty_col_count%" = ("%cols%" - 1) BEGIN // first column in every row is its number, that's why (cols - 1) first_empty_row = i // remember the first empty row i = file // skip iterating over the rest of the rows END END INSERT_2DA_ROW ("%first_empty_row%") %cols% ~%2da_row% %ability% %icon% %strref% %min_lev% %max_level% %num_allowed% %prerequisite% %excluded_by% %alignment_restrict%~ PRETTY_PRINT_2DA ACTION_IF NOT (~%l_table%~ STRING_EQUAL_CASE ~d5_%lu_row%~) BEGIN COPY_EXISTING ~LUABBR.2DA~ ~override~ SET_2DA_ENTRY %lu_row% 1 2 ~d5_%lu_row%~ END END END DEFINE_ACTION_FUNCTION remove_hla STR_VAR kit_name = ~~ remove_ability = ~*~ BEGIN COPY_EXISTING ~luabbr.2da~ ~override~ COUNT_2DA_COLS l_cols // amount of columns READ_2DA_ENTRIES_NOW l_rows l_cols // read all file into memory FOR (l_row = 1; l_row < l_rows; ++l_row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER l_rows l_row 0 ~l_kit~ // read column value PATCH_IF ~%l_kit%~ STRING_EQUAL_CASE ~%kit_name%~ BEGIN SET lu_row = %l_row% READ_2DA_ENTRY_FORMER l_rows lu_row 1 ~l_table~ // read column value END END BUT_ONLY ACTION_IF FILE_EXISTS_IN_GAME ~lu%l_table%.2da~ BEGIN COPY_EXISTING ~lu%l_table%.2da~ ~override/lud5_%lu_row%.2da~ COUNT_2DA_COLS cols // amount of columns COUNT_2DA_ROWS cols rows // amount of rows READ_2DA_ENTRIES_NOW file cols // read all file into memory SET num_deleted = 0 FOR (i = 0; i < file; ++i) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER file i 1 col_value // read column value PATCH_IF "%col_value%" STRING_EQUAL_CASE "%remove_ability%" BEGIN // match .spl to be removed REMOVE_2DA_ROW (i - num_deleted) cols // kill the row SET num_deleted += 1 END END ACTION_IF NOT (~%l_table%~ STRING_EQUAL_CASE ~d5_%lu_row%~) BEGIN COPY_EXISTING ~LUABBR.2DA~ ~override~ SET_2DA_ENTRY %lu_row% 1 2 ~d5_%lu_row%~ END END END DEFINE_ACTION_FUNCTION replace_hla STR_VAR kit_name = ~~ remove_ability = ~*~ 2da_row = ~1~ ability = ~*~ icon = ~*~ strref = ~*~ min_lev = ~1~ max_level = ~99~ num_allowed = ~*~ prerequisite = ~*~ excluded_by = ~*~ alignment_restrict = ~*~ BEGIN COPY_EXISTING ~luabbr.2da~ ~override~ COUNT_2DA_COLS l_cols // amount of columns READ_2DA_ENTRIES_NOW l_rows l_cols // read all file into memory FOR (l_row = 1; l_row < l_rows; ++l_row) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER l_rows l_row 0 ~l_kit~ // read column value PATCH_IF ~%l_kit%~ STRING_EQUAL_CASE ~%kit_name%~ BEGIN SET lu_row = %l_row% READ_2DA_ENTRY_FORMER l_rows lu_row 1 ~l_table~ // read column value END END BUT_ONLY ACTION_IF FILE_EXISTS_IN_GAME ~lu%l_table%.2da~ BEGIN COPY_EXISTING ~lu%l_table%.2da~ ~override/lud5_%lu_row%.2da~ COUNT_2DA_COLS cols // amount of columns COUNT_2DA_ROWS cols rows // amount of rows READ_2DA_ENTRIES_NOW file cols // read all file into memory SET num_deleted = 0 FOR (i = 0; i < file; ++i) BEGIN // iterate over rows READ_2DA_ENTRY_FORMER file i 1 col_value // read column value PATCH_IF "%col_value%" STRING_EQUAL_CASE "%remove_ability%" BEGIN // match .spl to be removed REMOVE_2DA_ROW (i - num_deleted) cols // kill the row SET num_deleted += 1 END END PATCH_IF NOT num_deleted = 0 BEGIN FOR (i = 0; i < file; ++i) BEGIN // iterate over rows SET empty_col_count = 0 // amount of empty columns in the row FOR (j = 0; j < cols; ++j) BEGIN // iterate over columns in the row READ_2DA_ENTRY_FORMER file i j col_value // read column value PATCH_IF "%col_value%" STRING_EQUAL "*" BEGIN // asterisk symbolizes empty column empty_col_count += 1 END END PATCH_IF "%empty_col_count%" = ("%cols%" - 1) BEGIN // first column in every row is its number, that's why (cols - 1) first_empty_row = i // remember the first empty row i = file // skip iterating over the rest of the rows END END INSERT_2DA_ROW ("%first_empty_row%" - 1) %cols% ~%2da_row% %ability% %icon% %strref% %min_lev% %max_level% %num_allowed% %prerequisite% %excluded_by% %alignment_restrict%~ END PRETTY_PRINT_2DA ACTION_IF NOT (~%l_table%~ STRING_EQUAL_CASE ~d5_%lu_row%~) BEGIN COPY_EXISTING ~LUABBR.2DA~ ~override~ SET_2DA_ENTRY %lu_row% 1 2 ~d5_%lu_row%~ END END END Use is fairly simple, you just need to know the kit name of the kit you want to modify (the name in kitlist.2da), and the variables that you want to fill into the table: LAF remove_hla STR_VAR kit_name = ~RANGER~ remove_ability = ~GA_SPCL922~ END LAF add_hla STR_VAR kit_name = ~RANGER~ ability = ~GA_SPCL923~ num_allowed = ~20~ END LAF replace_hla STR_VAR kit_name = ~RANGER~ remove_ability = ~GA_SPCL900~ ability = ~GA_SPCL922~ num_allowed = ~1~ excluded_by = ~GA_SPCL922~ END The variables simply correspond to the column headings in the LUxyz.2da HLA tables... plus the added "remove_ability" variable in the remove_hla and replace_hla functions. NOTE: this will insert my personal modding prefix into the player's game, regardless of which mod/modder actually uses these functions. I think that is unavoidable; for different mods to work together with these functions the tables assigned to any given kit must not conflict with another game file (thus involving a modder prefix), and they should agree on a uniform system for assigning new, unique table names for each modified kit (thus, the same modder prefix). So it's not vanity, just convenience. The functions assign any modfied kit's HLAs to a table name constructed from "LU" plus "d5" plus the row number of the kit in LUABBR.2da. Edited June 28, 2017 by subtledoctor Quote Link to comment
Angel Posted June 29, 2017 Share Posted June 29, 2017 (edited) replace_cre_script, find and replace a creature script Written out of frustration when trying to give ankhegs and winter wolves different scripts for their special attacks and finding that almost every single version of the critters had it in a different slot. DEFINE_PATCH_FUNCTION replace_cre_script INT_VAR check_override = 1 check_class = 1 check_race = 1 check_general = 1 check_default = 1 STR_VAR old_script = "" new_script = "" RET found BEGIN SET found = 0 PATCH_IF check_override BEGIN READ_ASCII SCRIPT_OVERRIDE script PATCH_IF ("%script%" STRING_EQUAL_CASE "%old_script%") BEGIN WRITE_EVALUATED_ASCII SCRIPT_OVERRIDE "%new_script%" SET found = 1 END END PATCH_IF check_class BEGIN READ_ASCII SCRIPT_CLASS script PATCH_IF ("%script%" STRING_EQUAL_CASE "%old_script%") BEGIN WRITE_EVALUATED_ASCII SCRIPT_CLASS "%new_script%" SET found = 1 END END PATCH_IF check_race BEGIN READ_ASCII SCRIPT_RACE script PATCH_IF ("%script%" STRING_EQUAL_CASE "%old_script%") BEGIN WRITE_EVALUATED_ASCII SCRIPT_RACE "%new_script%" SET found = 1 END END PATCH_IF check_general BEGIN READ_ASCII SCRIPT_GENERAL script PATCH_IF ("%script%" STRING_EQUAL_CASE "%old_script%") BEGIN WRITE_EVALUATED_ASCII SCRIPT_GENERAL "%new_script%" SET found = 1 END END PATCH_IF check_default BEGIN READ_ASCII SCRIPT_DEFAULT script PATCH_IF ("%script%" STRING_EQUAL_CASE "%old_script%") BEGIN WRITE_EVALUATED_ASCII SCRIPT_DEFAULT "%new_script%" SET found = 1 END END END Edited July 16, 2017 by Angel Quote Link to comment
K4thos Posted July 6, 2017 Share Posted July 6, 2017 1. Code for patching BAM v2 worldmap icons without overwriting. I've tried to keep variable names similar to mike's code for BAM v1 patching in case someone would like to merge both functions into 1 that supports both formats: https://github.com/K4thos/IE-code-repository/blob/master/add_map_icons_ee.tpa The code can be easily adjusted to patch any BAM v2 file. 2. WMP file patching using BP-BGT Worldmap style TBL files (useful when you need to add more than 1 area or many links with custom travel times). The code can also patch classic and EE saves, remap icons (in case you use add_map_icons_ee.tpa too) and do other minor things (disabled by default) https://github.com/K4thos/IE-code-repository/blob/master/add_worldmap_tbl.tpa Earlier versions of these functions have been implemented in Lava's mods that add new areas to worldmap, so they can be used as a reference. Quote Link to comment
Angel Posted July 16, 2017 Share Posted July 16, 2017 (edited) Small one to quickly flag an item as magical. Written mostly because I keep forgetting what the offsets in the item file are. :-) DEFINE_PATCH_FUNCTION make_magical INT_VAR enchantment = "-1" lore = "-1" BEGIN WRITE_LONG 0x0018 (THIS | BIT6) PATCH_IF enchantment >= 0 BEGIN WRITE_LONG 0x0060 enchantment END PATCH_IF lore >= 0 BEGIN WRITE_SHORT 0x0042 lore END END Used like this, to make a magical version of the generic b1-4 "paw" that counts as a +1 weapon for things to hit: COPY_EXISTING "b1-4.itm" "override/b1-4m1.itm" LAUNCH_PATCH_FUNCTION make_magical INT_VAR enchantment = 1 END Note that this by itself does not set to-hit and damage bonuses, use the built-in ALTER_ITEM_HEADER for that. Edited July 16, 2017 by Angel Quote Link to comment
Grammarsalad Posted July 16, 2017 Share Posted July 16, 2017 ... EDIT: Turns out that in BG2, the level drain icon is set by the engine when opcode 216 is used. So, don't set it ourselves as it won't be removed. EFINE_PATCH_FUNCTION add_energy_drain INT_VAR levels_drained = 1 BEGIN READ_ASCII 0x0000 signature (4) PATCH_MATCH "%signature%" WITH "ITM " WHEN ENGINE_IS "bg1 totsc iwd1 how totlm" BEGIN LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 18 // HP: Maximum HP Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 5) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 54 // Stat: THAC0 Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 2) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 142 // Graphics: Display Special Effect Icon target = 2 // Pre-target duration = 2400 parameter2 = 53 END END "ITM " BEGIN LAUNCH_PATCH_FUNCTION ADD_ITEM_EFFECT INT_VAR type = 1 // Melee opcode = 216 // Spell Effect: Level Drain target = 2 // Pre-target timing = 1 // Permanent parameter1 = levels_drained END END "SPL " WHEN ENGINE_IS "bg1 totsc iwd1 how totlm" BEGIN LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 18 // HP: Maximum HP Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 5) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 54 // Stat: THAC0 Modifier target = 2 // Pre-target duration = 2400 parameter1 = ((0 - 2) * levels_drained) END LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR opcode = 142 // Graphics: Display Special Effect Icon target = 2 // Pre-target duration = 2400 parameter2 = 53 END END "SPL " BEGIN LAUNCH_PATCH_FUNCTION ADD_SPELL_EFFECT INT_VAR type = 1 // Melee opcode = 216 // Spell Effect: Level Drain target = 2 // Pre-target timing = 1 // Permanent parameter1 = levels_drained END END DEFAULT PATCH_FAIL "add_energy_drain() used on incompatible file!" END END ... Just a slight copy/paste typo. "EFINE_PATCH_FUNCTION add_energy_drain" needs to be changed to "DEFINE_PATCH_FUNCTION add_energy_drain" Quote Link to comment
Angel Posted July 19, 2017 Share Posted July 19, 2017 Just a slight copy/paste typo. "EFINE_PATCH_FUNCTION add_energy_drain" needs to be changed to "DEFINE_PATCH_FUNCTION add_energy_drain" Heh, probably a typo when I was editing. Fixed. Quote Link to comment
Jarno Mikkola Posted July 19, 2017 Share Posted July 19, 2017 Heh, probably a typo when I was editing. Fixed.Yeah, you typically won't get typos with Copy/Paste_edit scennarios, and typically in those one could also use the replace that's in Notepad to get rid of the edits problems too... but maybe that's just me. Hmm, you could just use ~add energy drain~ instead of add_energy_drain ... ahh, whell. 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.