Jump to content

Toss your semi-useful WeiDU macros here


Recommended Posts

Oh, this is great! Any chance for a 'shawdowed' option (i.e. like shades or shadow monsters in iwdee)?

Eh, didn't expect such a response to a relatively simple function. Is nobody interested in my erase-journal-o-matic for BGT? :-)

 

And yeah, writing variants should not be too hard, if I'd know what makes a monster a shade or shadow. Here's a variant that makes the monster illusionary.

 

 

 

DEFINE_PATCH_FUNCTION make_illusion
  INT_VAR   power_level   = "-1"
BEGIN
  WRITE_LONG 0x0010 THIS | BIT1      // No corpse
  WRITE_LONG 0x0014 0                // XP
  WRITE_LONG 0x001c 0                // Gold
  WRITE_BYTE 0x0270 IDS_OF_SYMBOL("ea" "neutral")
  WRITE_BYTE 0x0275 IDS_OF_SYMBOL("gender" "illusionary")

  // Add unstealable&undroppable flags to carried items
  GET_OFFSET_ARRAY itm_array CRE_V10_ITEMS
  PHP_EACH itm_array AS int => itm_offset
  BEGIN
    WRITE_LONG (itm_offset + 0x0010) (THIS | (BIT1 | BIT3))
  END

  // Handle power level if specified
  PATCH_IF (power_level >= 0)
  BEGIN
    WRITE_LONG  0x0018 power_level
  END
END

 

 

Edited by Angel
Link to comment

 

Another small one, turn a creature into a summoned creature.

This is GOLD. Gonna make good use of this.

 

Anyone know of a macro that is the obverse of SNPRINT - instead of retaining the first x characters of a string, I want to remove the first x characters and keep the rest. (Basically I want to automate switching out the modder prefix for a different one.)

 

In a patch context, this will take the file name sppr101/spwi101 and set the new_res variable to abcd101:

  INNER_PATCH_SAVE new_res ~%SOURCE_RES%~ BEGIN
    REPLACE_TEXTUALLY ~^[Ss][Pp][WwPp][IiRr]~ ~abcd~
  END

In an action context, it's the same syntax except with OUTER_INNER_PATCH_SAVE.

Link to comment

 

Oh, this is great! Any chance for a 'shawdowed' option (i.e. like shades or shadow monsters in iwdee)?

Eh, didn't expect such a response to a relatively simple function. Is nobody interested in my erase-journal-o-matic for BGT? :-)

 

And yeah, writing variants should not be too hard, if I'd know what makes a monster a shade or shadow. Here's a variant that makes the monster illusionary.

 

 

 

DEFINE_PATCH_FUNCTION make_illusion
  INT_VAR   power_level   = 1
BEGIN
  WRITE_LONG 0x0010 THIS | BIT1      // No corpse
  WRITE_LONG 0x0014 0                // XP
  WRITE_LONG 0x0018 power_level
  WRITE_LONG 0x001c 0                // Gold
  WRITE_BYTE 0x0270 IDS_OF_SYMBOL("ea" "neutral")

  WRITE_BYTE  0x0275 IDS_OF_SYMBOL("gender" "illusionary")

  // Add unstealable&undroppable flags to carried items
  GET_OFFSET_ARRAY itm_array CRE_V10_ITEMS
  PHP_EACH itm_array AS int => itm_offset
  BEGIN
    WRITE_LONG (itm_offset + 0x0010) (THIS | (BIT1 | BIT3))
  END
END

 

 

 

 

 

Oh, I love all of these! I just have a specific issue that this will help with. Basically, I have some bugs with a couple of summoning spells. I think the issue has something to do with imported animations. I'm just going to convert existing creatures, and this'll help. It's great timing.

 

I had to look up what made a creature 'shadow-like'. I think the only thing required is that the creature has TRANS60.ITM equipped (I'm pretty sure). That's easy enough for me to add, though. Thanks...

 

Edit: as for illusionary creatures, I'll probably use it just because it's right there!

Edited by Grammarsalad
Link to comment

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)

https://github.com/K4thos/IE-code-repository/blob/master/joinable_npc_array.tpa
Example usage:

LAM JOINABLE_NPC_ARRAY
ACTION_PHP_EACH JOINABLE_NPC_ARRAY AS cre => dv BEGIN
    PRINT ~%cre% => %dv%~ 
    COPY_EXISTING ~%cre%~ ~override~
    //your patching code
END
Link to comment

It hasn't been extensively tested yet, but here is a function library to do stuff with tilesets:

PS_Tileset

Note that if you want to add bitmap tiles to a PVRz-based tileset, you will need to run Tile2EE to convert it to a palette-based tileset first, then convert it back.

 

 

ps_tileset_lib ps_tileset_repair_header
Repairs a couple of errors in tileset headers:  missing header and wrong version number
ps_tileset_remove_tiles
Removes tiles from a tileset
ps_tileset_add_tiles
Adds or replaces tiles in a tileset
ps_tileset_create_tileset
Creates a tileset from a number of tiles
ps_tileset_export_tiles
Exports tiles from a tileset
ps_tileset_info
Gathers and reports basic or verbose information from a tileset
ps_tileset_replace_palette_entry
Replaces palette entries in a tile with a new color
ps_tileset_recolor_pixel
Recolors a pixel in a tile without changing the color of every pixel using the same palette entry
ps_zlib_compress
Z-lib compresses a file
ps_zlib_decompress
Z-lib decompresses a file
Edited by Sam.
Link to comment

This is a patch function that can be used to add multiple effects to items or spells without much effort. It uses a simple syntax with short keywords to keep effect definitions plain and simple. I'm currently using it to dynamically add many game dependent effects to spells.

The function code:

 

// Automates adding spell effects to items or spells, based on a code sequence.
// Code format:          "param1=value1,param2=value2;...second effect..."
// Supported parameters: op (Opcode), tgt (Target), tmg (Timing), pwr (Power), p1 (Parameter1), p2 (Parameter2), 
//                       rd (Resist/Dispel), dur (Duration), pro1 (Probability1), pro2 (Probability2), res (Resource), 
//                       dnum (Dice Number), dsize (Dice Size), stype (Save Type), sbonus (Save Bonus), spec (Special), ip (Insert Point)
//                       All parameters except "Opcode" are optional.
DEFINE_PATCH_FUNCTION a7_auto_apply_spl_effect
INT_VAR
  // default values if not specified in "effect_codes"
  def_target        = 0
  def_timing        = 0
  def_power         = 0
  def_parameter1    = 0
  def_parameter2    = 0
  def_resist_dispel = 0
  def_duration      = 0
  def_probability1  = 100
  def_probability2  = 0
  def_dicenumber    = 0
  def_dicesize      = 0
  def_savetype      = 0
  def_savebonus     = 0
  def_special       = 0
  def_insertpoint   = "-1"
STR_VAR
  // Supported functions: ADD_SPELL_EFFECT, ADD_SPELL_CFEFFECT, ADD_ITEM_EFFECT, ADD_ITEM_EQEFFECT and (limited) ADD_CRE_EFFECT
  function_name     = ~~
  // The code string with effect definitions
  effect_codes      = ~~
  def_resource      = ~~
BEGIN
  PATCH_IF (NOT ~%function_name%~ STR_EQ ~~) BEGIN
    // parsing effect entries
    SET entries = 0
    SET strlen = STRING_LENGTH ~%effect_codes%~
    INNER_PATCH ~%effect_codes%~ BEGIN
      SET curOfs = 0
      WHILE (curOfs < strlen) BEGIN
        SET ofs = INDEX_BUFFER(~;~ curOfs)
        PATCH_IF (ofs < 0) BEGIN SET ofs = strlen END
        READ_ASCII curOfs entry (ofs - curOfs)
        TEXT_SPRINT EVAL ~entries_%entries%~ ~%entry%~
        SET entries += 1
        SET curOfs = ofs + 1
      END
    END

    // parsing effect parameters
    SET effects = 0
    FOR (idx = 0; idx < entries; ++idx) BEGIN
      TEXT_SPRINT entry EVAL ~%entries_%idx%%~
      SET strlen = STRING_LENGTH ~%entry%~
      INNER_PATCH ~%entry%~ BEGIN
        SET curOfs = 0
        WHILE (curOfs < strlen) BEGIN
          SET ofs = INDEX_BUFFER(~,~ curOfs)
          PATCH_IF (ofs < 0) BEGIN SET ofs = strlen END
          READ_ASCII curOfs param (ofs - curOfs)
          INNER_PATCH ~%param%~ BEGIN
            SET ofs2 = INDEX_BUFFER(~=~)
            PATCH_IF (ofs2 > 0) BEGIN
              READ_ASCII 0 v1 (ofs2)
              READ_ASCII (ofs2+1) v2 (BUFFER_LENGTH - ofs2 - 1)
              TEXT_SPRINT EVAL ~effects_%idx%_%v1%~ ~%v2%~
            END
          END
          SET curOfs = ofs + 1
        END
      END
    END
    SET effects = entries

    // adding effects
    FOR (idx = 0; idx < effects; ++idx) BEGIN
      SET opcode = (VARIABLE_IS_SET EVAL ~effects_%idx%_op~) ? EVAL ~effects_%idx%_op~ : ~-1~
      SET target = (VARIABLE_IS_SET EVAL ~effects_%idx%_tgt~) ? EVAL ~effects_%idx%_tgt~ : def_target
      SET timing = (VARIABLE_IS_SET EVAL ~effects_%idx%_tmg~) ? EVAL ~effects_%idx%_tmg~ : def_timing
      SET power = (VARIABLE_IS_SET EVAL ~effects_%idx%_pwr~) ? EVAL ~effects_%idx%_pwr~ : def_power
      SET param1 = (VARIABLE_IS_SET EVAL ~effects_%idx%_p1~) ? EVAL ~effects_%idx%_p1~ : def_parameter1
      SET param2 = (VARIABLE_IS_SET EVAL ~effects_%idx%_p2~) ? EVAL ~effects_%idx%_p2~ : def_parameter2
      SET resist_dispel = (VARIABLE_IS_SET EVAL ~effects_%idx%_rd~) ? EVAL ~effects_%idx%_rd~ : def_resist_dispel
      SET duration = (VARIABLE_IS_SET EVAL ~effects_%idx%_dur~) ? EVAL ~effects_%idx%_dur~ : def_duration
      SET prob1 = (VARIABLE_IS_SET EVAL ~effects_%idx%_pro1~) ? EVAL ~effects_%idx%_pro1~ : def_probability1
      SET prob2 = (VARIABLE_IS_SET EVAL ~effects_%idx%_pro2~) ? EVAL ~effects_%idx%_pro2~ : def_probability2
      SET dicenumber = (VARIABLE_IS_SET EVAL ~effects_%idx%_dnum~) ? EVAL ~effects_%idx%_dnum~ : def_dicenumber
      SET dicesize = (VARIABLE_IS_SET EVAL ~effects_%idx%_dsize~) ? EVAL ~effects_%idx%_dsize~ : def_dicesize
      SET savetype = (VARIABLE_IS_SET EVAL ~effects_%idx%_stype~) ? EVAL ~effects_%idx%_stype~ : def_savetype
      SET savebonus = (VARIABLE_IS_SET EVAL ~effects_%idx%_sbonus~) ? EVAL ~effects_%idx%_sbonus~ : def_savebonus
      SET special = (VARIABLE_IS_SET EVAL ~effects_%idx%_spec~) ? EVAL ~effects_%idx%_spec~ : def_special
      SET insertpoint = (VARIABLE_IS_SET EVAL ~effects_%idx%_ip~) ? EVAL ~effects_%idx%_ip~ : def_insertpoint
      PATCH_IF (VARIABLE_IS_SET EVAL ~effects_%idx%_res~) BEGIN
        TEXT_SPRINT resource EVAL ~%effects_%idx%_res%~
      END ELSE BEGIN
        TEXT_SPRINT resource ~%def_resource%~
      END
      PATCH_IF (opcode >= 0) BEGIN
        LPF ~%function_name%~
        INT_VAR
          opcode        = opcode
          target        = target
          timing        = timing
          parameter1    = param1
          parameter2    = param2
          power         = power
          resist_dispel = resist_dispel
          duration      = duration
          probability1  = prob1
          probability2  = prob2
          dicenumber    = dicenumber
          dicesize      = dicesize
          savingthrow   = savetype
          savebonus     = savebonus
          special       = special
          insert_point  = insertpoint
        STR_VAR
          resource      = EVAL ~%resource%~
        END
      END
    END
  END
END

 


Example (adds fully configured effects "Display String" and "Play Visual Effect" to a spell):

COPY ~%MOD_FOLDER%/spells/myspell.spl~ ~override~
  LPF a7_auto_apply_spl_effect
  INT_VAR
    // some predefined parameters for all effects
    def_target        = 2   // Preset target
    def_savetype      = 1   // Save vs. Spell
    def_savebonus     = "-2"
  STR_VAR
    function_name = ~ADD_SPELL_EFFECT~
    effect_codes  = ~op=139,tmg=1,p1=8203;op=215,tmg=1,dur=2,p2=1,res=ICARMOR~
  END
Link to comment

Background: if you look at a spell with save-for-half-damage like fireball, you'll note that at the seventh-level fireball has two damage opcodes--one does 4d6 fire damage with no save, and second one does 3d6 fire damage but can be negated outright with a save. The upshot is 7d6 damage that is not-quite-halved to 4d6 on a successful save. More egregious examples of poorly-split damage exist, e.g. Storm of Vengeance splits its listed 1d6s as 2d3s; Burning Hands splits its 1d3 as 2d2. EE adds a new flag to the damage opcode (bit 8 in the special field) that allows the engine to do this calculation without having to force two damage opcodes and allowing true save-for-half damage.

 

We're finally getting around to using this in the EEs. CD_DOUBLE_DAMAGE is a macro designed to find these double damage opcodes in items and spells and combine them into one using this new flag. It's not a particularly smart algorithm, as it will find the first damage opcode matching its parameters on a given ability and then try to merge it with any other matching damage opcode that is within a die roll and within one point of the static damage field. You could add spells or items in your mod with the traditional damage splits and then run this macro on them if an EE is detected.

 

Fireball is easy because it just has the pair of damage opcodes on every header. Something like Earthquake, with its three pairs of damage over time, would require this to be run three times

 

 

DEFINE_PATCH_FUNCTION CD_DOUBLE_DAMAGE

// defines what we're going to check
INT_VAR header = "-1"
header_type = "-1"
m_power = "-1"
m_type = "-1"
m_damtype = "-1"
m_timing = "-1"
m_dispel = "-1"
m_duration = "-1"
m_prob1 = "-1"
m_prob2 = "-1"
m_dicesize = "-1"
m_flags = "-1"
debug = 0
BEGIN

SPRINT debug_message ~%SOURCE_FILE% report:~
READ_ASCII 0x00 sig (3)
SET abil_length = 0x28
PATCH_IF ("%sig%" STRING_COMPARE_CASE "ITM" = 0) BEGIN
SET abil_length = 0x38
END
READ_LONG 0x64 abil_off ELSE 0
READ_SHORT 0x68 abil_num ELSE 0
READ_LONG 0x6a fx_off ELSE 0
SET fx_delta = 0
FOR (index = 0 ; index WRITE_SHORT (abil_off + 0x20 + (abil_length * index)) (THIS + fx_delta)
READ_SHORT (abil_off + (abil_length * index)) o_header_type
PATCH_IF (((header = index) OR (header ((header_type = o_header_type) OR (header_type SET damage = 0
READ_SHORT (abil_off + 0x1e + (abil_length * index)) abil_fx_num
READ_SHORT (abil_off + 0x20 + (abil_length * index)) abil_fx_idx
FOR (index2 = 0 ; index2 READ_SHORT (fx_off + (0x30 * (abil_fx_idx + index2))) opcode
PATCH_IF (opcode = 12) BEGIN
PATCH_IF damage = 0 BEGIN
READ_BYTE (fx_off + 0x02 + (0x30 * (abil_fx_idx + index2))) o_power
READ_LONG (fx_off + 0x04 + (0x30 * (abil_fx_idx + index2))) o_amount
READ_SHORT (fx_off + 0x08 + (0x30 * (abil_fx_idx + index2))) o_type
READ_SHORT (fx_off + 0x0a + (0x30 * (abil_fx_idx + index2))) o_damtype
READ_BYTE (fx_off + 0x0c + (0x30 * (abil_fx_idx + index2))) o_timing
READ_BYTE (fx_off + 0x0d + (0x30 * (abil_fx_idx + index2))) o_dispel
READ_LONG (fx_off + 0x0e + (0x30 * (abil_fx_idx + index2))) o_duration
READ_BYTE (fx_off + 0x12 + (0x30 * (abil_fx_idx + index2))) o_prob1
READ_BYTE (fx_off + 0x13 + (0x30 * (abil_fx_idx + index2))) o_prob2
READ_LONG (fx_off + 0x1c + (0x30 * (abil_fx_idx + index2))) o_dicenum
READ_LONG (fx_off + 0x20 + (0x30 * (abil_fx_idx + index2))) o_dicesize
READ_LONG (fx_off + 0x24 + (0x30 * (abil_fx_idx + index2))) o_save
READ_LONG (fx_off + 0x28 + (0x30 * (abil_fx_idx + index2))) o_savebonus
READ_LONG (fx_off + 0x2c + (0x30 * (abil_fx_idx + index2))) o_flags
PATCH_IF (((o_power = m_power) OR (m_power ((o_type = m_type) OR (m_type ((o_damtype = m_damtype) OR (m_damtype ((o_timing = m_timing) OR (m_timing ((o_dispel = m_dispel) OR (m_dispel ((o_duration = m_duration) OR (m_duration ((o_prob1 = m_prob1) OR (m_prob1 ((o_prob2 = m_prob2) OR (m_prob2 ((o_dicesize = m_dicesize) OR (m_dicesize ((o_flags = m_flags) OR (m_flags SET o_index = index2
SET damage += 1 // matches
PATCH_IF (((o_save & BIT0) = 0) AND // no save vs. spell
((o_save & BIT1) = 0) AND // no save vs. breath
((o_save & BIT2) = 0) AND // no save vs. death
((o_save & BIT3) = 0) AND // no save vs. wand
((o_save & BIT4) = 0)) BEGIN // no save vs. polymorph
SET o_save_here = 0
END ELSE BEGIN
SET o_save_here = 1
END
END
END ELSE BEGIN // damage > 0, o_ vars set
READ_BYTE (fx_off + 0x02 + (0x30 * (abil_fx_idx + index2))) c_power
READ_LONG (fx_off + 0x04 + (0x30 * (abil_fx_idx + index2))) c_amount
READ_SHORT (fx_off + 0x08 + (0x30 * (abil_fx_idx + index2))) c_type
READ_SHORT (fx_off + 0x0a + (0x30 * (abil_fx_idx + index2))) c_damtype
READ_BYTE (fx_off + 0x0c + (0x30 * (abil_fx_idx + index2))) c_timing
READ_BYTE (fx_off + 0x0d + (0x30 * (abil_fx_idx + index2))) c_dispel
READ_LONG (fx_off + 0x0e + (0x30 * (abil_fx_idx + index2))) c_duration
READ_BYTE (fx_off + 0x12 + (0x30 * (abil_fx_idx + index2))) c_prob1
READ_BYTE (fx_off + 0x13 + (0x30 * (abil_fx_idx + index2))) c_prob2
READ_LONG (fx_off + 0x1c + (0x30 * (abil_fx_idx + index2))) c_dicenum
READ_LONG (fx_off + 0x20 + (0x30 * (abil_fx_idx + index2))) c_dicesize
READ_LONG (fx_off + 0x24 + (0x30 * (abil_fx_idx + index2))) c_save
READ_LONG (fx_off + 0x28 + (0x30 * (abil_fx_idx + index2))) c_savebonus
READ_LONG (fx_off + 0x2c + (0x30 * (abil_fx_idx + index2))) c_flags
PATCH_IF ((o_power = c_power) AND
(o_type = c_type) AND
(o_damtype = c_damtype) AND
(o_timing = c_timing) AND
(o_dispel = c_dispel) AND
(o_duration = c_duration) AND
(o_prob1 = c_prob1) AND
(o_prob2 = c_prob2) AND
(o_dicesize = c_dicesize) AND
(o_flags = c_flags)) BEGIN
PATCH_IF (((c_save & BIT0) = 0) AND // no save vs. spell
((c_save & BIT1) = 0) AND // no save vs. breath
((c_save & BIT2) = 0) AND // no save vs. death
((c_save & BIT3) = 0) AND // no save vs. wand
((c_save & BIT4) = 0)) BEGIN // no save vs. polymorph
SET c_save_here = 0
END ELSE BEGIN
SET c_save_here = 1
END
PATCH_IF ((o_amount = c_amount) OR (o_amount = (c_amount + 1)) OR (o_amount = (c_amount - 1))) BEGIN
PATCH_IF ((c_dicenum = c_dicenum) OR (c_dicenum = (c_dicenum + 1)) OR (c_dicenum = (c_dicenum - 1))) BEGIN
PATCH_IF (((c_save_here = 0) OR (o_save_here = 0)) AND (c_save_here + o_save_here != 0)) BEGIN // one, but not both saves must be zero
SPRINT debug_message ~%debug_message%\r\n == MATCH! on on %SOURCE_FILE%, header %index%, trying to combine damage~
PATCH_IF o_save_here = 0 BEGIN // no save vs. polymorph
SET n_save = c_save
SET n_savebonus = c_savebonus
END ELSE BEGIN
SET n_save = o_save
SET n_savebonus = o_savebonus
END
SET n_amount = o_amount + c_amount
SET n_dicenum = o_dicenum + c_dicenum
DELETE_BYTES (fx_off + (0x30 * (abil_fx_idx + index2))) 0x30 // delete second damage effect
SET index2 -= 1
SET fx_delta -= 1
SET abil_fx_num -= 1
// update damage #1 with new values
WRITE_LONG (fx_off + 0x04 + (0x30 * (abil_fx_idx + o_index))) n_amount // combined static damage
WRITE_LONG (fx_off + 0x1c + (0x30 * (abil_fx_idx + o_index))) n_dicenum // combined dice rolls
WRITE_LONG (fx_off + 0x24 + (0x30 * (abil_fx_idx + o_index))) n_save // save type
WRITE_LONG (fx_off + 0x28 + (0x30 * (abil_fx_idx + o_index))) n_savebonus // save bonus
WRITE_LONG (fx_off + 0x2c + (0x30 * (abil_fx_idx + o_index))) (THIS BOR BIT8) // add magic save-for-half flag
END ELSE BEGIN
SPRINT debug_message ~%debug_message%\r\n basic parameters of damage match, but saves vary on %SOURCE_FILE%, header %index%~
END
END ELSE BEGIN
SPRINT debug_message ~%debug_message%\r\n basic parameters of damage match, but number of dice rolls varies on %SOURCE_FILE%, header %index%~
END
END ELSE BEGIN
SPRINT debug_message ~%debug_message%\r\n basic parameters of damage match, but static damage varies on %SOURCE_FILE%, header %index%~
END
END ELSE BEGIN
SPRINT debug_message ~%debug_message%\r\n basic parameters of damage don't match on %SOURCE_FILE%, header %index%~
END
END // patch_if damage = 1
END // patch_if opcode = 12
END // end fx loop
PATCH_IF damage = 0 BEGIN
SPRINT debug_message ~%debug_message%\r\n no damage opcode matching search parameters found on %SOURCE_FILE%, header %index%~
END
WRITE_SHORT (abil_off + 0x1e + (abil_length * index)) abil_fx_num
END // end header type/number check
END // end abil loop
PATCH_IF debug BEGIN
INNER_PATCH_SAVE debug_message ~%debug_message%~ BEGIN
REPLACE_TEXTUALLY ~\\r\\n~ ~
~
END
PATCH_PRINT ~%debug_message%~
END

END

 

Link to comment
On 7/26/2017 at 6:25 AM, CamDawg said:

As presently written, you can't specify a tra file for it. I'll poke it a bit and make it work.

Long overdue, but here's cd_extend_bg_area_script that accepts tra files. Sample usage:

LAF cd_extend_bg_area_script STR_VAR area = ar1000 script = ~path/to/script~ tra_file = ~path/to/tra.tra~ END

Omit the .baf extension from the script parameter, but include .tra for tra_file. Poor planning on my part, but it works.

Spoiler
DEFINE_ACTION_FUNCTION cd_extend_bg_area_script
  INT_VAR extend_top = 0
  STR_VAR area       = ""
          script     = ""
          tra_file   = ""
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

<<<<<<<<./inlined-macro/cd_extend_bg_area_script.tpa
EXTEND_BOTTOM ~%a_script%.bcs~ ~%script%.baf~ EVALUATE_BUFFER USING ~%tra_file%~
>>>>>>>>

  ACTION_IF extend_top = 1 THEN BEGIN
    COPY ~./inlined-macro/cd_extend_bg_area_script.tpa~ ~./inlined-macro/cd_extend_bg_area_script.tpa~
      REPLACE_TEXTUALLY ~^EXTEND_BOTTOM~ ~EXTEND_TOP~
  END

  ACTION_IF ("%tra_file%" STRING_COMPARE_CASE "" = 0) THEN BEGIN
    COPY ~./inlined-macro/cd_extend_bg_area_script.tpa~ ~./inlined-macro/cd_extend_bg_area_script.tpa~
      REPLACE_TEXTUALLY ~ EVALUATE_BUFFER USING ~ ~ EVALUATE_BUFFER // USING ~
  END

  REINCLUDE ~./inlined-macro/cd_extend_bg_area_script.tpa~

END

 

Edited by CamDawg
borked formatting
Link to comment

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

 

 

I have a question whether these functions can be modified in such a way that I can either:

- remove an individual cre (actor) from an are-file

- replace an individual cre (actor) from an are-file

 

Currently I just modify the resource name (cre-file name) at the respective offset in the are-file. This works fine, but may be a bit fragile as some other modder may one day decide to change the same are-file in a way that changes the current offset.

 

Has anyone done this?

 

Background

 

In EET, I change the fixed appearance of a vanilla NPC by a scripted appearance where either the vanilla creature OR the continuous NPC from a previous campaign is triggered. In my upcoming mod, Corwin from SoD can join the PC already during the BG1 part as soon as Baldur's Gate City becomes available. I kind of extend the NPC forward...

Of course, when she re-joins in the SoD campaign, I use the same continuos creature. Bad thing in SoD coding is, that her appearance there is from the cre being part of an are-file. I have already changed that, so actually the problem is solved - just maybe not permanently, if some other modder touches a cruicial part of the respective are-file.

What I do at the moment is to go into the are-file actors part, go to actorXX and in the *character* field change the resource. (A rabbit appears instead of the NPC) The NPC is created by the area script now instead, thus allowing me to select based on some globals set/not set previously.

 

Edited by Roxanne
Link to comment

Figured I'd throw my multiclass macro up here too. It allows you to assign class features to multiclass kits in the 2.0+ engine, and does so with only a single line added to each trueclass clab. It's maintained on GitHub for those who want to download the library directly.

 

Actual macro code is available here (it's quite long). Syntax looks like this (and assumes that you have a few base files present somewhere in your mod structure):

 

 

LAF qd_multiclass

STR_VAR

kit_name = ~kitname~ //the internal name for your kit (e.g. QDMAGUS)

kit_clab = ~kitclab~ //the internal name of your kit's clab file, without the .2da extension

base_class = ~X~ // this can take 6 values: [F]ighter, [P]riest, [D]ruid, [R]anger, [M]age, [T]hief

mc_dir = ~your/file/structure~ // the directory containing the three files, as noted above

END

 

 

 

You can use it to make kitted multi-classes (Fighter/Wild Mage), multi-class kits (Bladesinger as a Fighter/Mage kit) or even multi-kit setups (Berserker/Priest of Talos), all with relatively painless syntax.

 

If anyone knows how to make weidu generate completely blank spells consistently, then this macro could be further refined to not need a link back to a directory.

Edited by Aquadrizzt
Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...