Jump to content

Toss your semi-useful WeiDU macros here


Recommended Posts

This is a small function that could come in handy to determine the string count in the dialog.tlk. It uses a constant (and small) number of iterations to determine the string count regardless of the actual dialog.tlk size.

Code:

Spoiler
/**
 * Determines the current number of available strings in the dialog.tlk.
 *
 * INT_VAR bits   A positive number that determines the string range to cover.
 *                Default is 24 which covers up to 2^24 = 16,777,216 string entries.
 * RET count      Returns the number of available string entries.
 */
DEFINE_DIMORPHIC_FUNCTION count_tlk_entries
INT_VAR bits = 24
RET count
BEGIN
  OUTER_SET strref = 1 << bits
  OUTER_SET less = 1
  OUTER_FOR (subrange = (1 << bits) / 2; subrange > 0; subrange >>= 1) BEGIN
    OUTER_SET strref = less ? (strref - subrange) : (strref + subrange)
    ACTION_GET_STRREF strref string
    OUTER_SET less = ~%string%~ STR_EQ ~<Invalid Strref %strref%>~
  END
  OUTER_SET count = strref + (NOT less)
END

// Usage:
LAF count_tlk_entries RET count END
PRINT ~Number of strings in dialog.tlk: %count%~

Update: The WeiDU command NEXT_STRREF seems to provide the same functionality as this function. I leave it here for educational purposes.

Edited by argent77
Update
Link to comment

@argent77: any reason not to just read the number from dialog.tlk? i.e.

DEFINE_DIMORPHIC_FUNCTION count_tlk_entries
RET count
BEGIN
	ACTION_IF VARIABLE_IS_SET EE_LANGUAGE BEGIN
		OUTER_SPRINT dialog_path "lang/%EE_LANGUAGE%/dialog.tlk"
	END ELSE BEGIN
		OUTER_SPRINT dialog_path "dialog.tlk"
	END
	COPY - "%dialog_path%" nowhere
		count=LONG_AT 0xa
END

It's plausible I'm missing something - I'm not superfamiliar with the dialog format, I mostly treat it as a black box.

EDIT: as Argent points out, this only works if you're not adding strings to dialog.tlk elsewhere in the same install.

Link to comment
19 minutes ago, DavidW said:

any reason not to just read the number from dialog.tlk?

That'll probably work too in most cases. However, it might fail with an error if the dialog.tlk size is >16 MB because of (32-bit) WeiDU restrictions. It may also miss strings added by the current installation process since I'm not sure whether WeiDU writes them immediately to disk or just caches them in memory until the installation is complete.

Link to comment

When I first introduced the EXTEND-O-MATIC way back in the third post, I had this to say:

On 6/24/2017 at 7:55 PM, CamDawg said:

The EXTEND-O-MATIC in particular could probably be used to knock out a new Spell-50 mod pretty easily.

Well, it wasn't quite as simple as that. I paired the EXTEND-O-MATIC (patent still pending) with a new function, the TRIM-O-MATIC (patent pending), and then unified them under an umbrella function, the LEVEL_SELECT-O-MATIC (patent pending). The EXTEND-O-MATIC has been further enhanced and now scale damage; more info below.

All three functions return the abil_delta variable which tracks how many abilities have been added or removed.

CD_LEVEL_SELECT-O-MATIC

The LEVEL_SELECT-O-MATIC will call the TRIM or EXTEND-O-MATIC depending on whether the spell needs to be cut down (e.g. you're capping a spell below its current maximum level) or expanded (you're increasing the existing level cap). As such, it takes all of the parameters of the TRIM and EXTEND-O-MATIC functions, which are covered in detail below. The only parameter that is unique to it specifically is min_abil, which is the number of abilities (aka spell headers) required for changes to be made.

CD_TRIM-O-MATIC

About as simple as it gets: it will delete all headers with a minimum level greater than what you specify in the level_cap variable.

CD_EXTEND-O-MATIC

This aims to extend a spell to the level specified in the level_cap variable by copying the highest existing level header and adjusting the duration and damage as specified.

step_size is used to determine how many levels should be between every header. If you have a spell that increases a duration of something like +1 round/4 levels, you'd need a new header every four levels, and would set step_size to 4 accordingly.

min_lev_alt is used if you're extending a spell with only one header. By convention the first header in a spell is always set to minimum level 1, even though it's generally the effects of a higher level, e.g. the first header of a Fireball spell is actually the level 5 effects since it does 5d6 damage. In this case you would use min_lev_alt to tell the function to ignore the minimum level and use this value instead. For multi-header abilities, keep this value at 0 so that it will read and use the level from the last header.

Durations can be extended in a number of ways. Durations below min_dur will be ignored; use this to prevent short cosmetic or audio effects from being extended to the full duration of the spell. Otherwise it will calculate durations based on on of two formulas:

  • If duration_special is set to 0, it will calculate the duration by multiplying step_dur with the level and then adding base_dur. So if a spell lasts 4 rounds + 1 round/level, you'd use base_dur = 24 step_dur = 6.
  • If duration_special is set to 1, it will simply add step_dur to every new header created. If a spell has a duration like +1 round/4 levels, you'd use duration_special = 1 step_size = 4 and step_dur = 6. If duration_special is set, base_dur is ignored.

Similarly, damage can now be extended in this new iteration of the function. The function does not change the size of dice rolled (if any), only the number of rolls and fixed damage.

  • If damage_special is set to 0, it will calculate damage based on the header's level. Damage rolls are level multiplied with damage_rolls plus base_dmg_rolls. Fixed damage is level multiplied with damage_fixed plus base_dmg_fixed.
  • If damage_special is set to 1 it will simply add dice rolls from damage_rolls and fixed damage from damage_fixed to every new header. The base damage variables are ignored.

save_for_half allows for four values to account for the various ways that the original games split uneven damage. For example, when Fireball is cast at level 5, it rolls 3d6 damage (for which there is no save) and 2d6 damage (which can be negated with a save). On other spells, the 'extra die' goes to the save damage. The function will simply look for the save-for-half bit and do the normal damage calculations if it's found, otherwise the following information applies:

  • If set to 1, it will split the damage such that 'extra' die rolls or fixed damage both go to the no-save damage (most spells use this, see example: Fireball).
  • If set to 2, it will split the damage such that 'extra' die rolls or fixed damage both go to the save damage (example: Chain Lightning).
  • If set to 3, extra die damage goes to the save damage but extra fixed damage goes to the no-save damage. Cone of Cold does 1d4+1 per level and alternates its die and fixed splits. At level 11 it does 11d4+11, but split as 5d4+6 (no save) and 6d4+5 (save).
  • If set to 4, extra die damage goes to the no-save damage but extra fixed damage goes to the save damage (no examples in the base games)

If a spell needs scaling outside of duration and damage you'll have to go in and make changes the old-fashioned way. Even some of spells that scale damage can't be handed, e.g. Magic Missile (achieves damage scaling via projectile changes) or Flame Arrow (achieve damage scaling via additional effects).

Examples

The best thing I can do is direct folks to Spell-50 (specifically the spell50_foo libraries), which is probably the most comprehensive and repeated use of these functions. Spell-50 sets the variable round to 7 for oIWD and 6 otherwise, and the round variable is used throughout the patches and is used as some of the function's default. I'll provide two examples which are not covered by Spell-50.

Let's say you're making a kit and you want it to have an innate Protection From Lightning ability. There are any number of functions in this thread of converting a divine/arcane spell to an innate, but  since the innate is given at level 1, you want the duration to scale properly from level one as well. So:

COPY_EXISTING ~sppr407.spl~ ~override/cdpr407.spl~
  LPM spell_to_innate // or your choice of macro/function
  LPF CD_TRIM-O-MATIC INT_VAR level_cap = 1 END // delete all but the level 1 header
  LPF ALTER_EFFECT INT_VAR match_duration = 210 duration = 30 END // correect level 1 durations
  LPF CD_EXTEND-O-MATIC INT_VAR level_cap = 20 base_dur = 0 step_dur = 30 END // now scale it back out to level 20 with 5 rounds/level

Another example would be to fix bugs with wild surges. If a level 5 wild mage casts Fireball, but gets a 'caster level reduced by 2' wild surge, then the Fireball should do 3d6 damage. However, since the spell lacks real headers for levels 1-4, it will always do a minimum of 5d6 damage. Easy enough:

COPY_EXISTING ~spwi304.spl~ ~override~
  LPF CD_LEVEL_SELECT-O-MATIC INT_VAR level_cap = 1 END // delete all but the level 1 header
  LPF ALTER_EFFECT INT_VAR silent = 1 match_opcode = 12 match_dicenumber = 3 dicenumber = 1 END // original, no-save damage
  LPF ALTER_EFFECT INT_VAR silent = 1 match_opcode = 12 match_dicenumber = 2 dicenumber = 0 END // original, save damage
  LPF ALTER_EFFECT INT_VAR silent = 1 match_opcode = 12 match_dicenumber = 5 dicenumber = 1 END // EEs
  LPF CD_LEVEL_SELECT-O-MATIC INT_VAR level_cap = 10 base_dur = 0 step_dur = 0 damage_rolls = 1 save_for_half = 1 END // scale it back out to 10d6, save for half
  BUT_ONLY

Note that the ALTER_EFFECT has to be tailored to the EEs unitary damage opcode or the original's dual opcodes, but otherwise the patch is identical.

Note that in the second I'm using the CD_LEVEL_SELECT-O-MATIC function to trim and then extend the spell, since it basically passes along the parameters to the respective functions. This is also the approach used in Spell-50.

Edited by CamDawg
Link to comment
2 hours ago, argent77 said:

That'll probably work too in most cases. However, it might fail with an error if the dialog.tlk size is >16 MB because of (32-bit) WeiDU restrictions. It may also miss strings added by the current installation process since I'm not sure whether WeiDU writes them immediately to disk or just caches them in memory until the installation is complete.

You're right, I've checked.

Link to comment
30 minutes ago, DavidW said:

Just include them yourself. There’s not really much advantage adding them to WEIDU, and Wisp is busy enough as it is without adding to his admin burden.

That's fair,

I don't really know what kind of work adding these macros entails, but I just figured it'd be easier for people to find these things down the road if they were built in. I'm always looking in the WeiDU documentation. I didn't even know that CamDawg had written these until I scrounged around in this thread. I guess that's on me.

On a different note, is Wisp pretty much the sole maintainer of WeiDU these days? Sure looks like it from the commits on Git. 

Edited by WanderingScholar
Link to comment
2 hours ago, WanderingScholar said:

Would you consider adding your wonderful O_MATIC spell functions as built in macros for WeiDU?

Not until I finally get those patents, daggummit!

In seriousness: unlike the CLONE/ALTER/DELETE_EFFECT series I don't think there's a broad enough use case for their inclusion, even if Wisp had the time. ALTER_HEADER, on the other hand, I could see a case for as I use it in a number of my mods.

Link to comment

Good evening everyone,

I'm still working on my little tool and in the process another function was added for the  dialogTlkFunctionalities.tph library.

I don't know if this will be useful to anyone other than myself, so I'll post it here and please decide for yourself.

The documentaion for the function can be found here.

The name of the function is "STR_SET_EVAL_WITH_STRINGS" and works like STRING_SET_EVALUATE, but takes only string values to resolve text and soundfile names.

 

IMPORTANT NOTE:

⚠️Please check the GitHub repository for future updates, as updating changes in many other places can be tedious and error-prone.

I won't continue to update the changes here unless it's something very critical or explicitly contacted to do so.

Since this feature modifies the entire dialog.tlk file at a specific location, it is highly recommended to use this feature responsibly. Otherwise, the text in the game may be corrupted.

 

Function:

Spoiler
// Purpose: like STR_SET_EVAL_WITH_STRINGS, but takes only string values to resolve text 
// 			and soundfile names.
// -------------------------------------------------------------------------------------
// Parameters:|
// ============
// text 	-> The text found in dialog.tlk
// sound	-> The name of the soundfile without ".wav" found in the dialog.tlk 
// strref	-> The stringref index value found in the dialog.tlk
DEFINE_DIMORPHIC_FUNCTION STR_SET_EVAL_WITH_STRINGS 
	INT_VAR
		strref	= 0
	STR_VAR
		text 	= ~!_NULL~
		sound	= ~!_NULL~
BEGIN
	// Check if valid text argument is given.
	ACTION_IF (~%text%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_DIMORPHIC_FUNCTION STR_SET_EVAL_WITH_STRINGS -> The correct argument for text is missing. The argument !_NULL is forbidden.~
	END
	// Checks if a valid voxSound is given
	ACTION_IF (~%sound%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_DIMORPHIC_FUNCTION STR_SET_EVAL_WITH_STRINGS -> The correct argument for sound is missing. The argument !_NULL is forbidden.~
	END
	// Checks if a valid strref is given
	ACTION_IF (~%strref%~ < 0) THEN 
	BEGIN
		FAIL ~DEFINE_DIMORPHIC_FUNCTION STR_SET_EVAL_WITH_STRINGS -> The correct argument for strref is missing. The value is %strref%, but it needs to be greater or equal than zero.~
	END
	// Checks if sound name has more than 8 characters
	ACTION_IF (STRING_LENGTH ~%sound%~ > 8) THEN
	BEGIN
		OUTER_SET numbers = STRING_LENGTH ~%sound%~
		FAIL ~DEFINE_DIMORPHIC_FUNCTION STR_SET_EVAL_WITH_STRINGS -> The argument '%sound%' for sound is %numbers% characters long. The maximum allowed length for this argument is 8 characters~
	END
	
	// Core part of this function. This part takes the string parameters  
	// and uses them in the same way as STRING_SET_EVALUATE.
	<<<<<<<<./weidu_external/workspace/!_TemporaryStringSetEvaluate.tph
	STRING_SET_EVALUATE %strref% ~%text%~ [%sound%]
	>>>>>>>>
	COPY ~./weidu_external/workspace/!_TemporaryStringSetEvaluate.tph~ ~./weidu_external/%MOD_FOLDER%/temp/values/!_TemporaryStringSetEvaluate.tph~ EVALUATE_BUFFER
	REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/values/!_TemporaryStringSetEvaluate.tph~

END// End of "STR_SET_EVAL_WITH_STRINGS"

 

 

Example(Repairing dialog.tlk entry): For this code to work, you will need the dialogTlkFunctionalities.tph

Spoiler
// -----------
// Information
// -----------
BACKUP ~weidu_external/dialogTLKtest/backup~ // %MOD_FOLDER% doesn't work here
AUTHOR ~Yes~
VERSION ~v0.0.0~


// --------------
// Initialization
// --------------
ALWAYS 
	// LIBRARY INCLUDES(Don't touch these)
	// -----------------------------------
	INCLUDE ~%MOD_FOLDER%/lib/dialogTlkFunctionalities.tph~

END// End of "ALWAYS"


// -----------
// Start Tests
// -----------
BEGIN ~TLK-Testing~
// Copy soundfile to language folder
COPY ~%MOD_FOLDER%/sound~ 	~lang/%LANGUAGE%/sounds~

// This will add to a specific strref the text "Become Ladder!" 
// and the sound file "!_DEMBb.wav"
LAF RESOLVE_STR_REF_WITH_STRINGS
	STR_VAR
		text = ~Become Ladder!~
		sound = ~!_DEMBb~
	RET
		refValue = strRef
END

// Prints strref which of text "Become Ladder!" and soundfile "!_DEMBb.wav"
PRINT ~BEFORE(refValue):%refValue%~


// Repairs the text in dalog.tlk
LAF STR_SET_EVAL_WITH_STRINGS
INT_VAR
	strref 	= refValue
STR_VAR
	text 	= ~Becoming Leader!~
	sound	= ~!_DEMBb~
END


// Checks the dialog.tlk whether there is an entry with the text “Becoming Leader!” and sound file e.g. !_DEMBb.wav
LAF RESOLVE_STR_REF_WITH_STRINGS
	STR_VAR
		text = ~Becoming Leader!~
		sound = ~!_DEMBb~
	RET
		refValue = strRef
END

// The value should match the first refValue, regardless of the text.
PRINT ~AFTER(refValue):%refValue%~

 

 

Edited by Incrementis
Fixed a comment
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...