Jump to content

Toss your semi-useful WeiDU macros here


Recommended Posts

For once, a function of mine that doesn't rely on 127 other functions. This one (written in the context of Argent77's 'Magic Shop of Vergadain' mod) edits the UI to let you turn store options off by script. Apart from the specific function, it demos a general way to pass information from scripts to the UI that others might find useful.

/*

This EE-only function (by DavidW) allows you to restrict which store options (Buy/Sell, Drink, Rest, etc) are
offered when a store is open, by adding commands to the script that summons the store. You use it like this:

(i) run the function and return the INT_VAR 'inactive_strref'

(ii) In the storekeeper's dlg file (or similar), jusst before the StartStore(xxx) command that you use to 
trigger the store, add commands like

SetToken("DW_SIGNAL_STORE_REST",%inactive_strref%)

The possible tokens are:
DW_SIGNAL_STORE_IDENTIFY
DW_SIGNAL_STORE_STEAL
DW_SIGNAL_STORE_DONATE
DW_SIGNAL_STORE_CURE
DW_SIGNAL_STORE_DRINK
DW_SIGNAL_STORE_REST

Any token set will inactivate the associated option.

Tokens remain set until the next time the store interface runs, so it's important only to set them just before
opening the store, else they may hang around and mess up the next store.

Anyone is welcome to borrow this; just credit me in your readme.

v1 (9/24/23): initial version
v2 (9/25/23): should handle stealing properly

*/

DEFINE_ACTION_FUNCTION ui_block_store_function RET inactive_strref
BEGIN

	// have we been installed already?
	
	ACTION_IF !(FILE_EXISTS_IN_GAME "m_dw_sgd.lua" && RESOURCE_CONTAINS "m_dw_sgd.lua" "dwStorePanelLookup") BEGIN

		// if not, install

		// define token strings
		
		// the numbers in this array are frame numbers (counting from 0) in GUISTBBC.bam
		// 2 is a placeholder value (the old 'steal' icon isn't used in the EE interface, I think)
		ACTION_DEFINE_ASSOCIATIVE_ARRAY signal_tokens BEGIN
			0=>DW_SIGNAL_STORE_BUYSELL
			1=>DW_SIGNAL_STORE_IDENTIFY
			2=>DW_SIGNAL_STORE_STEAL
			3=>DW_SIGNAL_STORE_DONATE
			4=>DW_SIGNAL_STORE_CURE
			5=>DW_SIGNAL_STORE_DRINK
			6=>DW_SIGNAL_STORE_REST
		END

		// put them in to dialog.tlk
		ACTION_PHP_EACH signal_tokens AS ind=>token BEGIN
			OUTER_SET $token_strref("%ind%")=RESOLVE_STR_REF("<%token%>")
		END

		// put the 'inactive' strref in too 
		
		OUTER_SET active_strref=RESOLVE_STR_REF("ACTIVE")
		OUTER_SET inactive_strref=RESOLVE_STR_REF("INACTIVE")

		// install the data
	<<<<<<<<.../stratagems-inline/m_dw_sgd.lua
	dwStorePanelLookup={}
	%data%
	>>>>>>>>
		
		OUTER_SPRINT data ""
		ACTION_PHP_EACH token_strref AS ind=>strref BEGIN
			OUTER_SPRINT data "%data%dwStorePanelLookup[%ind%]=%strref%%WNL%"
		END

		ACTION_IF !FILE_EXISTS_IN_GAME "m_dw_sgd.lua" BEGIN
			COPY ".../stratagems-inline/m_dw_sgd.lua" override EVALUATE_BUFFER
		END ELSE BEGIN
			APPEND "m_dw_sgd.lua" ".../stratagems-inline/m_dw_sgd.lua"
		END

		// install the functions

	<<<<<<<<.../stratagems-inline/m_dw_sgf.lua
	function dwStorePanelEnabled(num)
		storeInd=storeScreen:GetPanelButtonSequence(num)
		if dwStorePanelLookup[storeInd] then
			if (Infinity_FetchString(dwStorePanelLookup[storeInd]))=="INACTIVE" then
				return false
			end	
		end
		return storeScreen:GetPanelButtonEnabled(num)
	end

	function dwUpdateBuySellPanel()	
		if (Infinity_FetchString(dwStorePanelLookup[0]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateBuySellPanel()
		end
	end
	


	function dwUpdateIdentifyPanel()	
		if (Infinity_FetchString(dwStorePanelLookup[1]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateIdentifyPanel()
		end
	end
	
	function dwIsStealEnabled()
		if (Infinity_FetchString(dwStorePanelLookup[2]))=="INACTIVE" then
			return false
		end
		return storeScreen:IsStealEnabled()
	
	
	end

	function dwUpdateDonatePanel()	
		if (Infinity_FetchString(dwStorePanelLookup[3]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateDonatePanel()
		end
	end

	function dwUpdateBuySpellPanel()	
		if (Infinity_FetchString(dwStorePanelLookup[4]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateBuySpellPanel()
		end
	end

	function dwUpdateBuyDrinksPanel()	
		if (Infinity_FetchString(dwStorePanelLookup[5]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateBuyDrinksPanel()
		end
	end
		
	function dwUpdateRentRoomPanel()	
		if (Infinity_FetchString(dwStorePanelLookup[6]))=="INACTIVE" then
			dwStoreForcePanel()
		else
			storeScreen:UpdateRentRoomPanel()
		end
	end

	function dwStoreForcePanel()
		if dwStorePanelEnabled(0) then
			setStoreMainPanel(0)
		elseif dwStorePanelEnabled(1) then
			setStoreMainPanel(1)
		elseif dwStorePanelEnabled(2) then
			setStoreMainPanel(2)
		else
			setStoreMainPanel(3)
		end
		
			
	end

	function dwStoreClose() --- reset all tokens
		%data%
		storeScreen:OnMainDoneButtonClick()
	end
	>>>>>>>>
		
		OUTER_SPRINT data ""
		ACTION_PHP_EACH signal_tokens AS ind=>token BEGIN
			OUTER_SPRINT data ~%data%%TAB%C:Eval('SetToken("%token%",%active_strref%)')%WNL%~
		END
		
		ACTION_IF !FILE_EXISTS_IN_GAME "m_dw_sgf.lua" BEGIN
			COPY ".../stratagems-inline/m_dw_sgf.lua" OVERRIDE EVALUATE_BUFFER
		END ELSE BEGIN
			APPEND "m_dw_sgf.lua" ".../stratagems-inline/m_dw_sgf.lua" 
		END
		
		// inject into ui.menu (simple enough that we might as well just REPLACE_TEXTUALLY)

		COPY_EXISTING "ui.menu" override
			REPLACE_TEXTUALLY "storeScreen:GetPanelButtonEnabled(\([0-9]\))" "dwStorePanelEnabled(\1)"
			REPLACE_TEXTUALLY "storeScreen:UpdateRentRoomPanel()" "dwUpdateRentRoomPanel()"
			REPLACE_TEXTUALLY "storeScreen:UpdateDonatePanel()" "dwUpdateDonatePanel()"
			REPLACE_TEXTUALLY "storeScreen:IsStealEnabled()" "dwIsStealEnabled()"
			REPLACE_TEXTUALLY "storeScreen:UpdateIdentifyPanel()" "dwUpdateIdentifyPanel()"
			REPLACE_TEXTUALLY "storeScreen:UpdateBuySellPanel()" "dwUpdateBuySellPanel()"
			REPLACE_TEXTUALLY "storeScreen:UpdateBuySpellPanel()" "dwUpdateBuySpellPanel()"
			REPLACE_TEXTUALLY "storeScreen:UpdateBuyDrinksPanel()" "dwUpdateBuyDrinksPanel()"
			REPLACE_TEXTUALLY "storeScreen:OnMainDoneButtonClick()" "dwStoreClose()"


	END ELSE BEGIN
	
		// if so, just return the strref of 'INACTIVE'
		
		OUTER_SET inactive_strref=RESOLVE_STR_REF("INACTIVE")
	
	END
END

 

Link to comment

Here is a more sophisticated version of spell_to_innate. It makes sure all nested subspells (if any) are of type "innate" and can make the specified innate ability uninterruptible (while casting).

Spoiler
// Example usage

WITH_SCOPE BEGIN
	LAF "GET_UNIQUE_FILE_NAME" STR_VAR "extension" = "spl" RET "filename" END
	//
	LAF "MAKE_SPELL-LIKE_ABILITY"
	STR_VAR
		"source_spell" = "sppr450" // spell to clone
		"dest_spell" = EVAL "%filename%" // resource name of the cloned spell
	END
END
  
// Function definition
  
DEFINE_ACTION_FUNCTION "MAKE_SPELL-LIKE_ABILITY"
INT_VAR
	"level" = 0 // default: keep levels (in case "%source_spell%" scales with level), otherwise specify a level
	"location" = 2 // default: "Cast spell" button (F7)
	"uninterruptible" = 0 // boolean
STR_VAR
	"source_spell" = "" // spell to clone
	"dest_spell" = "" // resource name of the cloned spell
RET_ARRAY
	"make_spell-like_ability_reserved_hash" // this is only needed when the function calls itself (i.e., you can safely ignore it)
BEGIN
	// Initialize
	OUTER_SET "last_valid_level" = "-1"
	OUTER_SET "last_valid_target" = 1
	//
	COPY_EXISTING - "%source_spell%.spl" "override"
		PATCH_IF "%uninterruptible%" BEGIN
			READ_SLONG NAME1 "name"
			READ_LONG 0x18 "flags"
		END
		GET_OFFSET_ARRAY "abilityArray" SPL_V10_HEADERS
		PHP_EACH "abilityArray" AS "abilityIndex" => "abilityOffset" BEGIN
			PATCH_IF "%uninterruptible%" BEGIN
				SET "last_valid_target" = BYTE_AT ("%abilityOffset%" + 0xC)
			END
			// Return the last header whose "min_level" @ 0x10 is smaller or equal than "%level%"
			PATCH_IF (SHORT_AT ("%abilityOffset%" + 0x10) <= "%level%") BEGIN
				SET "last_valid_level" = SHORT_AT ("%abilityOffset%" + 0x10)
			END
		END
	BUT_ONLY_IF_IT_CHANGES
	// Clone the original spell
	ACTION_IF "%uninterruptible%" BEGIN
		LAF "GET_UNIQUE_FILE_NAME" STR_VAR "extension" = "spl" RET "shell_spell" = "filename" END
	END ELSE BEGIN
		OUTER_TEXT_SPRINT "shell_spell" "%dest_spell%"
	END
	COPY_EXISTING "%source_spell%.spl" "override/%shell_spell%.spl"
		// Header
		PATCH_IF "%uninterruptible%" BEGIN
			WRITE_LONG NAME1 "-1"
			WRITE_LONG 0x18 0 // flags
		END
		WRITE_SHORT 0x1C 4 // innate
		WRITE_LONG 0x1E 0 // exclusion flags
		// Extended header(s)
		PATCH_WITH_SCOPE BEGIN
			GET_OFFSET_ARRAY "abilityArray" SPL_V10_HEADERS
			PHP_EACH "abilityArray" AS "abilityIndex" => "abilityOffset" BEGIN
				PATCH_IF ("%last_valid_level%" != "-1") AND (SHORT_AT ("%abilityOffset%" + 0x10) != "%last_valid_level%") BEGIN
					WRITE_BYTE "%abilityOffset%" 0xFF // mark it for later deletion
				END ELSE BEGIN
					WRITE_SHORT ("%abilityOffset%" + 0x2) "%location%"
					WRITE_SHORT ("%abilityOffset%" + 0x10) ("%last_valid_level%" == "-1" ? THIS : 1)
				END
			END
			// Actual deletion
			LPF "DELETE_SPELL_HEADER" INT_VAR "header_type" = 0xFF END
		END
		// Feature block(s) -- make sure all op206/318/321/324/333 protect from / remove / cast the new resref
		LPF ~ALTER_EFFECT~ INT_VAR ~silent~ = 1 ~match_opcode~ = 206 STR_VAR ~match_resource~ = EVAL ~%SOURCE_RES%~ ~resource~ = EVAL ~%DEST_RES%~ END
		LPF ~ALTER_EFFECT~ INT_VAR ~silent~ = 1 ~match_opcode~ = 318 STR_VAR ~match_resource~ = EVAL ~%SOURCE_RES%~ ~resource~ = EVAL ~%DEST_RES%~ END
		LPF ~ALTER_EFFECT~ INT_VAR ~silent~ = 1 ~match_opcode~ = 321 STR_VAR ~match_resource~ = EVAL ~%SOURCE_RES%~ ~resource~ = EVAL ~%DEST_RES%~ END
		LPF ~ALTER_EFFECT~ INT_VAR ~silent~ = 1 ~match_opcode~ = 324 STR_VAR ~match_resource~ = EVAL ~%SOURCE_RES%~ ~resource~ = EVAL ~%DEST_RES%~ END
		LPF ~ALTER_EFFECT~ INT_VAR ~silent~ = 1 ~match_opcode~ = 333 STR_VAR ~match_resource~ = EVAL ~%SOURCE_RES%~ ~resource~ = EVAL ~%DEST_RES%~ END
		// Make sure any op146/148/326/333 cast an "innate" spell (if any)
		GET_OFFSET_ARRAY "abilityArray" SPL_V10_HEADERS
		PHP_EACH "abilityArray" AS "abilityIndex" => "abilityOffset" BEGIN
			GET_OFFSET_ARRAY2 "effectArray" "%abilityOffset%" SPL_V10_HEAD_EFFECTS
			PHP_EACH "effectArray" AS "effectIndex" => "effectOffset" BEGIN
				READ_ASCII ("%effectOffset%" + 0x14) "res"
				PATCH_MATCH SHORT_AT "%effectOffset%" WITH
					146 148 326 333 WHEN ("%res%" STRING_COMPARE_CASE "%DEST_RES%") BEGIN
						PATCH_IF !(VARIABLE_IS_SET $"make_spell-like_ability_reserved_hash"("%res%")) BEGIN
							LPF "GET_UNIQUE_FILE_NAME" STR_VAR "extension" = "spl" RET "filename" END
							WRITE_ASCIIE ("%effectOffset%" + 0x14) "%filename%" #8
							TEXT_SPRINT $"make_spell-like_ability_reserved_hash"("%res%") "%filename%"
							INNER_ACTION BEGIN
								LAF "MAKE_SPELL-LIKE_ABILITY" INT_VAR "level" "location" STR_VAR "source_spell" = EVAL "%res%" "dest_spell" = EVAL "%filename%" RET_ARRAY "make_spell-like_ability_reserved_hash" END
							END
						END ELSE BEGIN
							WRITE_ASCIIE ("%effectOffset%" + 0x14) $"make_spell-like_ability_reserved_hash"("%res%") #8
						END
					END
					DEFAULT
				END
			END
		END
	BUT_ONLY_IF_IT_CHANGES
	// Make a dummy SPL file casting the real SPL file if "%uninterruptible%"
	ACTION_IF "%uninterruptible%" BEGIN
		ACTION_MATCH "%last_valid_target%" WITH
			1 BEGIN // living actor
				OUTER_SET "opcode" = 146 // cast spell
				OUTER_SET "target" = 2 // projectile target
			END
			4 BEGIN // any point within range
				OUTER_SET "opcode" = 148 // cast spell at point
				OUTER_SET "target" = 1 // self
			END
			5 7 BEGIN // caster
				OUTER_SET "opcode" = 146 // cast spell
				OUTER_SET "target" = 1 // self
			END
			DEFAULT
				FAIL "MAKE_SPELL-LIKE_ABILITY: Should not happen"
		END
		COPY_EXISTING "%shell_spell%.spl" "override/%dest_spell%.spl"
			// Header
			WRITE_LONG NAME1 "%name%"
			WRITE_LONG 0x18 "%flags%"
			WRITE_SHORT 0x22 0 // casting animation
			WRITE_ASCII 0x10 "" #8 // casting sound
			// Extended header(s)
			PATCH_WITH_SCOPE BEGIN
				GET_OFFSET_ARRAY "abilityArray" SPL_V10_HEADERS
				PHP_EACH "abilityArray" AS "abilityIndex" => "abilityOffset" BEGIN
					PATCH_IF (SHORT_AT ("%abilityOffset%" + 0x10) != 1) BEGIN
						WRITE_BYTE "%abilityOffset%" 0xFF // mark it for later deletion
					END ELSE BEGIN
  						WRITE_SHORT ("%abilityOffset%" + 0x12) 0 // casting speed
  						WRITE_SHORT ("%abilityOffset%" + 0x26) IDS_OF_SYMBOL ("MISSILE" "None") // projectile
  					END
				END
				// Actual deletion
				LPF "DELETE_SPELL_HEADER" INT_VAR "header_type" = 0xFF END
			END
			// Casting feature effects
			LPF ~ADD_SPELL_CFEFFECT~ INT_VAR "opcode" = 101 "target" = 1 "parameter2" = 12 END // Immunity to effect => Effect: Damage (`casting_speed=0` might not be enough, hence a 0-duration immunity to damage...)
			// Feature block(s)
			LPF ~DELETE_SPELL_EFFECT~ INT_VAR ~opcode_to_delete~ = ~-1~ END // delete everything
			LPF ~ADD_SPELL_EFFECT~ INT_VAR "opcode" "target" "timing" = 1 STR_VAR "resource" = EVAL "%SOURCE_RES%" END // Cast spell / Cast spell at point
		BUT_ONLY_IF_IT_CHANGES
	END
END

 

 

Edited by Luke
Link to comment

I posted a similar thing else where, and so I'll post this here.
Here's a weapon proficiency indexing in the ADD_KIT command: 

//https://gibberlings3.github.io/iesdp/files/2da/2da_tob/weapprof.htm
               //BG1 ------      BG2                                             Useless
// ID            0 1 2 3 4 5 6 7 89 
        ~KITNAME 0 0 0 0 0 0 0 0 x x x x x x x x x x x x x x x x x x x x x x x x 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0~ //weapprof.2da 
//               L S B S B S A M B L S A T K S D W C S H F M Q C L S D S 2 S S 2 E - - - - - - - - - - - - - - - - - -
//               a m o p l p x i a o h x w a c a a l p a l a u r o h a l H S i W x - - - - - - - - - - - - - - - - - -
//               r a w e u i e s s n o e o t i g r u e l a c a o n o r i a h n e t - - - - - - - - - - - - - - - - - -
//               g l   a n k   s t g r   H a m g H b a b i e r s g t t n n e g a r - - - - - - - - - - - - - - - - - -
//               e l   r t e   i a S t   a n i e a   r e l   t s B B   g e l e p a - - - - - - - - - - - - - - - - - -
//the zeroes are the needed values or the game can remain at the character level up screen 

 

Link to comment

This library (dynArrayFunctionalities.tph) is  a byproduct of a small tool(actually nothing new) I am working on.

It is available for the following operating systems:

This library offers the possibility to create arrays whose elements can be overwritten without the need to create the same array again. It allows access and change of the elements in an array both inside and outside patching.

Detailed Information can be found here on GitHub. The wiki also contains a small and rudimentary benchmark. To be on the safe side, this should be checked before using the library.

I don't know if this is of any use to anyone but me, but you never know.

Just don't forget to mention me by adding me in a little thank you section.

EDIT:

 Allows pseudo-dyn arrays to set negative integer values.

This applies to the outer patch version (tested). The patch version is expected to work equally.

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

 

Linux version

Spoiler
// Never Forget: Comments can "lie", but code not so much ;P
//-----------------------------------------------------------//
// Contains self defined pseudo dynamic array functionalities//
//-----------------------------------------------------------//

// GLOBAL LIB VARIABLES(Don't change these)
// ----------------------------------------
OUTER_SPRINT 	~!_bufferValue~ ~~
OUTER_SPRINT 	~!_arraySlots~ 	~~



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy these variables to another location and change their values before calling the macro.
//			This macro can not be used in patching.
OUTER_SET 		~!_slotNumbers~ 	= 0
OUTER_SPRINT 	~!_setArrayName~	~!_NULL~
// Purpose: Creates a dynamic array and patch dynamic array
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// 	!_slotNumbers 	->	The desired number of elements that the array can hold
//	!_setArrayName 	-> 	The array name which is needed for further "DYN_ARRAY" function arguments
// ----------------------------------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CREATE 
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_slotNumbers~ 	= 0
	// STRING 			~!_setArrayName~ 	~!_NULL~
	// INTEGER			index				= 0
	// STRING			~!_arraySlots~ 		~~
	// ----------------------------------------------
	
	// Local variables which must not or can't be manipulated outside of macros
	LOCAL_SET 		index 			= 0
	OUTER_SPRINT 	~!_arraySlots~ 	~~
	
	// Checks if a proper array name is given	
	ACTION_IF (~%!_setArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if slot numbers are not out of bounds
	ACTION_IF (~%!_slotNumbers%~ <= 0) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE %!_setArrayName% -> The correct SlotNumbers are missing as an argument. Value must be greater than 0!~
	END

	// Creates empty inline files which are needed 
	// for buffering current values regarding dyn arrays and patch dyn arrays
	<<<<<<<<./weidu_external/workspace/!_temporaryArrayBuffer.tph
	>>>>>>>> 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph
	>>>>>>>>
	COPY ~./weidu_external/workspace/!_temporaryArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_setArrayName%Buffer.tph~
	COPY ~./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_setArrayName%Buffer.tph~

	// Creates an inline file which content is needed to define the dynamic array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryArray.tph
	DEFINE_ACTION_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	ACTION_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Creates an inline file which content is needed to define the dynamic patch array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArray.tph
	DEFINE_PATCH_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	PATCH_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Copies the content of the temporary arrays to dynamic named files so that they can be reused for another array
	COPY ~./weidu_external/workspace/!_temporaryArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~  EVALUATE_BUFFER
	COPY ~./weidu_external/workspace/!_temporaryPatchArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~  EVALUATE_BUFFER
	
	
	// Includes array element slots as variables into the array (%LNL% means Linux New Line)
	OUTER_FOR(index = 0; index < ~!_slotNumbers~; ++index) BEGIN	
		OUTER_SPRINT ~!_arraySlots~ ~%!_arraySlots%	"%%!_setArrayName%%index%%"%LNL%~	
	END
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~%!_arraySlots%~
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~%!_arraySlots%~
	
	// Closes dynamic array and makes it usable by WeiDU with 
	// e.g. INCLUDE/REINCLUDE (The tab space is for formatting)
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~	END END~	
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~	END END~	
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~
	
	// Resets macro variables
	OUTER_SET 		~!_slotNumbers~ 	= 0
	OUTER_SPRINT 	~!_setArrayName~	~!_NULL~

END// End of "DYN_ARRAY_CREATE"



// Purpose: Reads the buffer file for the specified array into a file.
//			This function mustn't be used outside this library.
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// position 	->	The array slot position/index in which the new value will be written
// UseArrayName ->	The required name of the array in which the new values will be written.
// bufferValue	->	The required string containing the buffered values
DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE
	INT_VAR
		position 	= "-1"
	STR_VAR
		UseArrayName= ~!_NULL~
		bufferValue = ~~
		functionType= ~!_NULL~
BEGIN
	ACTION_IF (~%functionType%~ STRING_EQUAL ~PATCH~) THEN
	BEGIN
		// This little line does not work inside of a patch macros, 
		// thus it is wrapped in this dimorphic function
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE ACTION_IF (~%functionType%~ STRING_EQUAL ~OUTER~) THEN
	BEGIN
	
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE
	BEGIN
	
		FAIL ~DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE %UseArrayName% -> The function type argument is missing or invalid. The allowed function types are PATCH and OUTER.~	
	END
		
END// End of "DYN_ARRAY_BUFFER_TO_FILE"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the these variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a patch version.
OUTER_SET 		~!_position~ =		"-1"
OUTER_SET 		~!_maxSlots~ =		"-1"
OUTER_SET		~!_collected~=		"-1"
OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets values in a dynamic array
// ------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ - 1
		
	// Checks if an array index is not given
	ACTION_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	ACTION_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ + 1 
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	ACTION_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	ACTION_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	ACTION_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		ACTION_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%LNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%LNL%~~~~~
		END
	// If value is not integer then it is string
	END ELSE 
	BEGIN		
		OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%LNL%~~~~~
	END
	
	// If all values are collected then write it into array
	ACTION_IF (~%!_collected%~ = 1) THEN
	BEGIN
		
		LAF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~OUTER~
		END
		
		// Writes into the dynamic array
		REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_useArrayName%Buffer.tph~
		LAM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		OUTER_SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		OUTER_SET 		~!_position~ =		"-1"
		OUTER_SET 		~!_maxSlots~ =		"-1"
		OUTER_SET		~!_collected~=		"-1"
		OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
		OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the commented SET variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a outer version.
//SET 		~!_position~ =		"-1"
//SET 		~!_maxSlots~ =		"-1"
//SET		~!_collected~=		"-1"
//SPRINT 	~!_useArrayName~ 	~!_NULL~
//SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets patch values in a dynamic array
// ------------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	SET ~!_maxSlots~ = ~!_maxSlots~ - 1
		
	// Checks if an array index is not given
	PATCH_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	PATCH_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		SET ~!_maxSlots~ = ~!_maxSlots~ + 1 
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	PATCH_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	PATCH_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	PATCH_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		PATCH_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%LNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%LNL%~~~~~	
		END		
	// If value is not integer then it is string
	END ELSE 
	BEGIN	
		SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%LNL%~~~~~
	END
	
	// If all values are collected then write it into array
	PATCH_IF (~%!_collected%~ = 1) THEN
	BEGIN
	
		LPF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~PATCH~
		END
		
		// Writes into the dynamic array
		PATCH_REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_useArrayName%Buffer.tph~
		LPM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		SET 	~!_position~ =		"-1"
		SET 	~!_maxSlots~ =		"-1"
		SET		~!_collected~=		"-1"
		SPRINT 	~!_useArrayName~ 	~!_NULL~
		SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the variable 1 to 1 to somewhere else where you need it 
// 			and change its value before before calling the macro.!!!
//			This macro can not be used in patching.
OUTER_SPRINT ~!_deleteArray~ ~!_NULL~
// Purpose: Deletes all created dynamic array files for patch and outer version
// ----------------------------------------------------------------------------
// Parameters:|
// ============
// !_deleteArray -> The array name for which the files will be deleted
// -------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP		
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
 	// STRING 			~!_deleteArray~ 	~!_NULL~
	// ----------------------------------------------
	
	// ---WARNING: Will output an error if local variables are not placed directly at the beginning of macros
	LOCAL_SPRINT outerBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT patchBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT outerArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_deleteArray%.tph~
	LOCAL_SPRINT patchArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_deleteArray%.tph~

	// The macro fails if no array name is given because it needs to know which array to delete the files for.
	ACTION_IF (~%!_deleteArray%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEAR_BUFFER -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
		
	// Deletes the buffer files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerBufferFilename%~) THEN
	BEGIN
		DELETE ~%outerBufferFilename%~
		PRINT ~Deleted %outerBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerBufferFilename%!~
	END
	
	ACTION_IF(FILE_EXISTS ~%patchBufferFilename%~) THEN
	BEGIN
		DELETE ~%patchBufferFilename%~
		PRINT ~Deleted %patchBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchBufferFilename%!~
	END
	
	// Deletes the definition files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerArrayFilename%~) THEN
	BEGIN
		DELETE ~%outerArrayFilename%~
		PRINT ~Deleted %outerArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerArrayFilename%!~
	END

	ACTION_IF(FILE_EXISTS ~%patchArrayFilename%~) THEN
	BEGIN
		DELETE ~%patchArrayFilename%~
		PRINT ~Deleted %patchArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchArrayFilename%!~
	END

END// End of "DYN_ARRAY_CLEANUP"

 

 

Mac version

Spoiler
// Never Forget: Comments can "lie", but code not so much ;P
//-----------------------------------------------------------//
// Contains self defined pseudo dynamic array functionalities//
//-----------------------------------------------------------//

// GLOBAL LIB VARIABLES(Don't change these)
// ----------------------------------------
OUTER_SPRINT 	~!_bufferValue~ ~~
OUTER_SPRINT 	~!_arraySlots~ 	~~



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy these variables to another location and change their values before calling the macro.
//			This macro can not be used in patching.
OUTER_SET 		~!_slotNumbers~ 	= 0
OUTER_SPRINT 	~!_setArrayName~	~!_NULL~
// Purpose: Creates a dynamic array and patch dynamic array
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// 	!_slotNumbers 	->	The desired number of elements that the array can hold
//	!_setArrayName 	-> 	The array name which is needed for further "DYN_ARRAY" function arguments
// ----------------------------------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CREATE 
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_slotNumbers~ 	= 0
	// STRING 			~!_setArrayName~ 	~!_NULL~
	// INTEGER			index				= 0
	// STRING			~!_arraySlots~ 		~~
	// ----------------------------------------------
	
	// Local variables which must not or can't be manipulated outside of macros
	LOCAL_SET 		index 			= 0
	OUTER_SPRINT 	~!_arraySlots~ 	~~
	
	// Checks if a proper array name is given	
	ACTION_IF (~%!_setArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if slot numbers are not out of bounds
	ACTION_IF (~%!_slotNumbers%~ <= 0) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE %!_setArrayName% -> The correct SlotNumbers are missing as an argument. Value must be greater than 0!~
	END

	// Creates empty inline files which are needed 
	// for buffering current values regarding dyn arrays and patch dyn arrays
	<<<<<<<<./weidu_external/workspace/!_temporaryArrayBuffer.tph
	>>>>>>>> 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph
	>>>>>>>>
	COPY ~./weidu_external/workspace/!_temporaryArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_setArrayName%Buffer.tph~
	COPY ~./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_setArrayName%Buffer.tph~

	// Creates an inline file which content is needed to define the dynamic array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryArray.tph
	DEFINE_ACTION_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	ACTION_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Creates an inline file which content is needed to define the dynamic patch array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArray.tph
	DEFINE_PATCH_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	PATCH_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Copies the content of the temporary arrays to dynamic named files so that they can be reused for another array
	COPY ~./weidu_external/workspace/!_temporaryArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~  EVALUATE_BUFFER
	COPY ~./weidu_external/workspace/!_temporaryPatchArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~  EVALUATE_BUFFER
	
	
	// Includes array element slots as variables into the array (%MNL% means Macintosh New Line)
	OUTER_FOR(index = 0; index < ~!_slotNumbers~; ++index) BEGIN	
		OUTER_SPRINT ~!_arraySlots~ ~%!_arraySlots%	"%%!_setArrayName%%index%%"%MNL%~	
	END
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~%!_arraySlots%~
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~%!_arraySlots%~
	
	// Closes dynamic array and makes it usable by WeiDU with 
	// e.g. INCLUDE/REINCLUDE (The tab space is for formatting)
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~	END END~	
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~	END END~	
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~
	
	// Resets macro variables
	OUTER_SET 		~!_slotNumbers~ 	= 0
	OUTER_SPRINT 	~!_setArrayName~	~!_NULL~

END// End of "DYN_ARRAY_CREATE"



// Purpose: Reads the buffer file for the specified array into a file.
//			This function mustn't be used outside this library.
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// position 	->	The array slot position/index in which the new value will be written
// UseArrayName ->	The required name of the array in which the new values will be written.
// bufferValue	->	The required string containing the buffered values
DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE
	INT_VAR
		position 	= "-1"
	STR_VAR
		UseArrayName= ~!_NULL~
		bufferValue = ~~
		functionType= ~!_NULL~
BEGIN
	ACTION_IF (~%functionType%~ STRING_EQUAL ~PATCH~) THEN
	BEGIN
		// This little line does not work inside of a patch macros, 
		// thus it is wrapped in this dimorphic function
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE ACTION_IF (~%functionType%~ STRING_EQUAL ~OUTER~) THEN
	BEGIN
	
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE
	BEGIN
	
		FAIL ~DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE %UseArrayName% -> The function type argument is missing or invalid. The allowed function types are PATCH and OUTER.~	
	END
		
END// End of "DYN_ARRAY_BUFFER_TO_FILE"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the these variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a patch version.
OUTER_SET 		~!_position~ =		"-1"
OUTER_SET 		~!_maxSlots~ =		"-1"
OUTER_SET		~!_collected~=		"-1"
OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets values in a dynamic array
// ------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ - 1
		
	// Checks if an array index is not given
	ACTION_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	ACTION_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ + 1 
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	ACTION_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	ACTION_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	ACTION_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		ACTION_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%MNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%MNL%~~~~~
		END
	// If value is not integer then it is string
	END ELSE 
	BEGIN		
		OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%MNL%~~~~~
	END
	
	// If all values are collected then write it into array
	ACTION_IF (~%!_collected%~ = 1) THEN
	BEGIN
		
		LAF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~OUTER~
		END
		
		// Writes into the dynamic array
		REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_useArrayName%Buffer.tph~
		LAM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		OUTER_SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		OUTER_SET 		~!_position~ =		"-1"
		OUTER_SET 		~!_maxSlots~ =		"-1"
		OUTER_SET		~!_collected~=		"-1"
		OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
		OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the commented SET variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a outer version.
//SET 		~!_position~ =		"-1"
//SET 		~!_maxSlots~ =		"-1"
//SET		~!_collected~=		"-1"
//SPRINT 	~!_useArrayName~ 	~!_NULL~
//SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets patch values in a dynamic array
// ------------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	SET ~!_maxSlots~ = ~!_maxSlots~ - 1
		
	// Checks if an array index is not given
	PATCH_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	PATCH_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		SET ~!_maxSlots~ = ~!_maxSlots~ + 1 
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	PATCH_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	PATCH_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	PATCH_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		PATCH_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%MNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%MNL%~~~~~	
		END		
	// If value is not integer then it is string
	END ELSE 
	BEGIN	
		SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%MNL%~~~~~
	END
	
	// If all values are collected then write it into array
	PATCH_IF (~%!_collected%~ = 1) THEN
	BEGIN
	
		LPF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~PATCH~
		END
		
		// Writes into the dynamic array
		PATCH_REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_useArrayName%Buffer.tph~
		LPM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		SET 	~!_position~ =		"-1"
		SET 	~!_maxSlots~ =		"-1"
		SET		~!_collected~=		"-1"
		SPRINT 	~!_useArrayName~ 	~!_NULL~
		SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the variable 1 to 1 to somewhere else where you need it 
// 			and change its value before before calling the macro.!!!
//			This macro can not be used in patching.
OUTER_SPRINT ~!_deleteArray~ ~!_NULL~
// Purpose: Deletes all created dynamic array files for patch and outer version
// ----------------------------------------------------------------------------
// Parameters:|
// ============
// !_deleteArray -> The array name for which the files will be deleted
// -------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP		
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
 	// STRING 			~!_deleteArray~ 	~!_NULL~
	// ----------------------------------------------
	
	// ---WARNING: Will output an error if local variables are not placed directly at the beginning of macros
	LOCAL_SPRINT outerBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT patchBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT outerArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_deleteArray%.tph~
	LOCAL_SPRINT patchArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_deleteArray%.tph~

	// The macro fails if no array name is given because it needs to know which array to delete the files for.
	ACTION_IF (~%!_deleteArray%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEAR_BUFFER -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
		
	// Deletes the buffer files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerBufferFilename%~) THEN
	BEGIN
		DELETE ~%outerBufferFilename%~
		PRINT ~Deleted %outerBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerBufferFilename%!~
	END
	
	ACTION_IF(FILE_EXISTS ~%patchBufferFilename%~) THEN
	BEGIN
		DELETE ~%patchBufferFilename%~
		PRINT ~Deleted %patchBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchBufferFilename%!~
	END
	
	// Deletes the definition files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerArrayFilename%~) THEN
	BEGIN
		DELETE ~%outerArrayFilename%~
		PRINT ~Deleted %outerArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerArrayFilename%!~
	END

	ACTION_IF(FILE_EXISTS ~%patchArrayFilename%~) THEN
	BEGIN
		DELETE ~%patchArrayFilename%~
		PRINT ~Deleted %patchArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchArrayFilename%!~
	END

END// End of "DYN_ARRAY_CLEANUP"

 

 

Windows version:

Spoiler
// Never Forget: Comments can "lie", but code not so much ;P
//-----------------------------------------------------------//
// Contains self defined pseudo dynamic array functionalities//
//-----------------------------------------------------------//

// GLOBAL LIB VARIABLES(Don't change these)
// ----------------------------------------
OUTER_SPRINT 	~!_bufferValue~ ~~
OUTER_SPRINT 	~!_arraySlots~ 	~~



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy these variables to another location and change their values before calling the macro.
//			This macro can not be used in patching.
OUTER_SET 		~!_slotNumbers~ 	= 0
OUTER_SPRINT 	~!_setArrayName~	~!_NULL~
// Purpose: Creates a dynamic array and patch dynamic array
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// 	!_slotNumbers 	->	The desired number of elements that the array can hold
//	!_setArrayName 	-> 	The array name which is needed for further "DYN_ARRAY" function arguments
// ----------------------------------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CREATE 
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_slotNumbers~ 	= 0
	// STRING 			~!_setArrayName~ 	~!_NULL~
	// INTEGER			index				= 0
	// STRING			~!_arraySlots~ 		~~
	// ----------------------------------------------
	
	// Local variables which must not or can't be manipulated outside of macros
	LOCAL_SET 		index 			= 0
	OUTER_SPRINT 	~!_arraySlots~ 	~~
	
	// Checks if a proper array name is given	
	ACTION_IF (~%!_setArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if slot numbers are not out of bounds
	ACTION_IF (~%!_slotNumbers%~ <= 0) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CREATE %!_setArrayName% -> The correct SlotNumbers are missing as an argument. Value must be greater than 0!~
	END

	// Creates empty inline files which are needed 
	// for buffering current values regarding dyn arrays and patch dyn arrays
	<<<<<<<<./weidu_external/workspace/!_temporaryArrayBuffer.tph
	>>>>>>>> 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph
	>>>>>>>>
	COPY ~./weidu_external/workspace/!_temporaryArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_setArrayName%Buffer.tph~
	COPY ~./weidu_external/workspace/!_temporaryPatchArrayBuffer.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_setArrayName%Buffer.tph~

	// Creates an inline file which content is needed to define the dynamic array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryArray.tph
	DEFINE_ACTION_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	ACTION_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Creates an inline file which content is needed to define the dynamic patch array. 
	<<<<<<<<./weidu_external/workspace/!_temporaryPatchArray.tph
	DEFINE_PATCH_MACRO ~!_%!_setArrayName%~ 
	BEGIN
	PATCH_DEFINE_ARRAY ~%!_setArrayName%~ BEGIN >>>>>>>>
	
	// Copies the content of the temporary arrays to dynamic named files so that they can be reused for another array
	COPY ~./weidu_external/workspace/!_temporaryArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~  EVALUATE_BUFFER
	COPY ~./weidu_external/workspace/!_temporaryPatchArray.tph~ ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~  EVALUATE_BUFFER
	
	
	// Includes array element slots as variables into the array (%WNL% means Windows New Line)
	OUTER_FOR(index = 0; index < ~!_slotNumbers~; ++index) BEGIN	
		OUTER_SPRINT ~!_arraySlots~ ~%!_arraySlots%	"%%!_setArrayName%%index%%"%WNL%~	
	END
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~%!_arraySlots%~
	APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~%!_arraySlots%~
	
	// Closes dynamic array and makes it usable by WeiDU with 
	// e.g. INCLUDE/REINCLUDE (The tab space is for formatting)
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~ ~	END END~	
	APPEND_OUTER 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~ ~	END END~	
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_setArrayName%.tph~
	REINCLUDE 		~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_setArrayName%.tph~
	
	// Resets macro variables
	OUTER_SET 		~!_slotNumbers~ 	= 0
	OUTER_SPRINT 	~!_setArrayName~	~!_NULL~

END// End of "DYN_ARRAY_CREATE"



// Purpose: Reads the buffer file for the specified array into a file.
//			This function mustn't be used outside this library.
// ----------------------------------------------------------------------------------------------
// Parameters:|
// ============
// position 	->	The array slot position/index in which the new value will be written
// UseArrayName ->	The required name of the array in which the new values will be written.
// bufferValue	->	The required string containing the buffered values
DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE
	INT_VAR
		position 	= "-1"
	STR_VAR
		UseArrayName= ~!_NULL~
		bufferValue = ~~
		functionType= ~!_NULL~
BEGIN
	ACTION_IF (~%functionType%~ STRING_EQUAL ~PATCH~) THEN
	BEGIN
		// This little line does not work inside of a patch macros, 
		// thus it is wrapped in this dimorphic function
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE ACTION_IF (~%functionType%~ STRING_EQUAL ~OUTER~) THEN
	BEGIN
	
		APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%UseArrayName%Buffer.tph~ ~%bufferValue%~
	END ELSE
	BEGIN
	
		FAIL ~DEFINE_DIMORPHIC_FUNCTION DYN_ARRAY_BUFFER_TO_FILE %UseArrayName% -> The function type argument is missing or invalid. The allowed function types are PATCH and OUTER.~	
	END
		
END// End of "DYN_ARRAY_BUFFER_TO_FILE"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the these variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a patch version.
OUTER_SET 		~!_position~ =		"-1"
OUTER_SET 		~!_maxSlots~ =		"-1"
OUTER_SET		~!_collected~=		"-1"
OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets values in a dynamic array
// ------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ - 1
		
	// Checks if an array index is not given
	ACTION_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	ACTION_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		OUTER_SET ~!_maxSlots~ = ~%!_maxSlots%~ + 1 
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	ACTION_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	ACTION_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	ACTION_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		ACTION_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%WNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%WNL%~~~~~
		END
	// If value is not integer then it is string
	END ELSE 
	BEGIN		
		OUTER_SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% OUTER_SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%WNL%~~~~~
	END
	
	// If all values are collected then write it into array
	ACTION_IF (~%!_collected%~ = 1) THEN
	BEGIN
		
		LAF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~OUTER~
		END
		
		// Writes into the dynamic array
		REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_useArrayName%Buffer.tph~
		LAM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		OUTER_SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		OUTER_SET 		~!_position~ =		"-1"
		OUTER_SET 		~!_maxSlots~ =		"-1"
		OUTER_SET		~!_collected~=		"-1"
		OUTER_SPRINT 	~!_useArrayName~ 	~!_NULL~
		OUTER_SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the commented SET variables to another location and change their values before calling the macro.
//			It is important to note that this macro has a outer version.
//SET 		~!_position~ =		"-1"
//SET 		~!_maxSlots~ =		"-1"
//SET		~!_collected~=		"-1"
//SPRINT 	~!_useArrayName~ 	~!_NULL~
//SPRINT 	~!_arrayValue~		~!_NULL~
// Purpose: Replaces/sets patch values in a dynamic array
// ------------------------------------------------------
// Parameters:|
// ============
// !_position		-> 	The array slot position/index in which the new value will be written
// !_maxSlots		->	The highest value the argument for position/index is allowed to be.
//					The maximum is in general the max number of elments the array can contain
// !_collected		->	A flag that indicates whether or not the collection process is complete.
//						0: Collection is in progress.
//						1: Collection is complete.
// !_useArrayName	->	The array name in which the array value is written
// !_arrayValue		->	The value that is planned to be written to the array.
DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
	// INTEGER 			~!_position~ =		"-1"
	// INTEGER 			~!_maxSlots~ =		"-1"
	// INTEGER			~!_collected~=		"-1"
 	// STRING 			~!_useArrayName~ 	~!_NULL~
 	// INTEGER/STRING 	~!_arrayValue~		~!_NULL~
	// STRING			~!_bufferValue~		~~
	// ----------------------------------------------
	
	// Corrects number of elements to slot index
	SET ~!_maxSlots~ = ~!_maxSlots~ - 1
		
	// Checks if an array index is not given
	PATCH_IF (~%!_position%~ <= "-1") THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct position as an argument is missing. The position value must be greater than -1!~
	END
	// Checks if a highest array slot number is not given or if it makes any sense
	PATCH_IF ( (~%!_maxSlots%~ <= "-1") OR (~%!_maxSlots%~ < ~%!_position%~) ) THEN 
	BEGIN
		// Prepares value for error output
		SET ~!_maxSlots~ = ~!_maxSlots~ + 1 
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The correct maxSlots as an argument is missing. The maxSlots value is %!_maxSlots%, but must be greater than -1 and greater %!_position%!~
	END
	// Checks if an array name is not given
	PATCH_IF (~%!_useArrayName%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
	// Checks if an array index is not given
	PATCH_IF (~%!_collected%~ <= "-1" OR ~%!_collected%~ >= 2) THEN 
	BEGIN
		PATCH_FAIL ~DEFINE_PATCH_MACRO DYN_ARRAY_SET_VALUES %!_useArrayName% -> The argument for "!_collected" is %!_collected%. Allowed values are 0(for still collecting) and 1(value collection is done) !~
	END
	
	// Checks if the array value is an integer and calls the appropriate function
	// to prepare the abstract array element information.
	PATCH_IF (IS_AN_INT ~%!_arrayValue%~) THEN 
	BEGIN
		PATCH_IF (~%!_arrayValue%~ > "-1") THEN 
		BEGIN
			// Positive values including 0
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = %!_arrayValue%%WNL%~~~~~
		END	ELSE 
		BEGIN
			// Negative values
			SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SET ~%!_useArrayName%%!_position%~ = "%!_arrayValue%"%WNL%~~~~~	
		END		
	// If value is not integer then it is string
	END ELSE 
	BEGIN	
		SPRINT ~!_bufferValue~ ~~~~~%!_bufferValue% SPRINT ~%!_useArrayName%%!_position%~ ~%!_arrayValue%~%WNL%~~~~~
	END
	
	// If all values are collected then write it into array
	PATCH_IF (~%!_collected%~ = 1) THEN
	BEGIN
	
		LPF DYN_ARRAY_BUFFER_TO_FILE
			INT_VAR
				position 	= ~%!_position%~
			STR_VAR
				UseArrayName= EVAL~%!_useArrayName%~
				bufferValue = EVAL~%!_bufferValue%~
				functionType= EVAL~PATCH~
		END
		
		// Writes into the dynamic array
		PATCH_REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_useArrayName%Buffer.tph~
		LPM ~!_%!_useArrayName%~
		
		// Resets global lib variable
		SPRINT ~!_bufferValue~ ~~	
		// Resets macro variables
		SET 	~!_position~ =		"-1"
		SET 	~!_maxSlots~ =		"-1"
		SET		~!_collected~=		"-1"
		SPRINT 	~!_useArrayName~ 	~!_NULL~
		SPRINT 	~!_arrayValue~		~!_NULL~
	END
	
END// End of "DYN_ARRAY_SET_VALUES"



// ---DEFAULT/INIT ARGUMENTS(Don't change these!)
// USAGE: 	Copy the variable 1 to 1 to somewhere else where you need it 
// 			and change its value before before calling the macro.!!!
//			This macro can not be used in patching.
OUTER_SPRINT ~!_deleteArray~ ~!_NULL~
// Purpose: Deletes all created dynamic array files for patch and outer version
// ----------------------------------------------------------------------------
// Parameters:|
// ============
// !_deleteArray -> The array name for which the files will be deleted
// -------------------------------------------------------------------
DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP		
BEGIN
	// ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
	// ----------------------------------------------
 	// STRING 			~!_deleteArray~ 	~!_NULL~
	// ----------------------------------------------
	
	// ---WARNING: Will output an error if local variables are not placed directly at the beginning of macros
	LOCAL_SPRINT outerBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT patchBufferFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/values/!_patch%!_deleteArray%Buffer.tph~
	LOCAL_SPRINT outerArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_%!_deleteArray%.tph~
	LOCAL_SPRINT patchArrayFilename 	~./weidu_external/%MOD_FOLDER%/temp/arrays/!_patch%!_deleteArray%.tph~

	// The macro fails if no array name is given because it needs to know which array to delete the files for.
	ACTION_IF (~%!_deleteArray%~ STRING_EQUAL ~!_NULL~) THEN 
	BEGIN
		FAIL ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEAR_BUFFER -> The correct array name as an argument is missing. The array name !_NULL is forbidden.~
	END
		
	// Deletes the buffer files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerBufferFilename%~) THEN
	BEGIN
		DELETE ~%outerBufferFilename%~
		PRINT ~Deleted %outerBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerBufferFilename%!~
	END
	
	ACTION_IF(FILE_EXISTS ~%patchBufferFilename%~) THEN
	BEGIN
		DELETE ~%patchBufferFilename%~
		PRINT ~Deleted %patchBufferFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchBufferFilename%!~
	END
	
	// Deletes the definition files for the specified array.
	ACTION_IF(FILE_EXISTS ~%outerArrayFilename%~) THEN
	BEGIN
		DELETE ~%outerArrayFilename%~
		PRINT ~Deleted %outerArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %outerArrayFilename%!~
	END

	ACTION_IF(FILE_EXISTS ~%patchArrayFilename%~) THEN
	BEGIN
		DELETE ~%patchArrayFilename%~
		PRINT ~Deleted %patchArrayFilename% belonging to %!_deleteArray% dynamic array~
	END ELSE BEGIN
		WARN ~DEFINE_ACTION_MACRO DYN_ARRAY_CLEANUP -> Couldn't find and delete %patchArrayFilename%!~
	END

END// End of "DYN_ARRAY_CLEANUP"

 

 

Edited by Incrementis
Informing about changes and future updates
Link to comment

Here is yet another "spell-2-innate" type of function, which is based on Subtledoctor's original function, while the op146/148 target matching portion is based on Luke's MAKE_SPELL-LIKE_ABILITY.

Spoiler
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Borrowed and adapted from Subtledoctor's 5E Spellcasting: create wrapper spell from any existing payload spell //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

DEFINE_ACTION_FUNCTION PAYLOAD_SPELL_TO_WRAPPER_SPELL
  INT_VAR
    wrapper_type = 4 // default to innate (0 - special, 1 - wizard, 2 - priest, 3 - psionic, 4 - innate, 5 - bard song)
    wrapper_location = 4 // default to ability (0 - none, 1 - weapon, 2 - spell, 3 - item, 4 - ability)
    op148_fix = 0 // default to no fix (0 - no fix, 1 - fix spell range for op148)
  STR_VAR
    payload_spell = ~~
    wrapper_spell = ~~
    wrapper_name = ~~ // default to empty (payload spell name)
    wrapper_desc = ~~ // default to empty (payload spell description)
  RET
    debug_msg
BEGIN
  COPY_EXISTING ~%payload_spell%.spl~ ~override/%wrapper_spell%.spl~
    READ_STRREF NAME1 payload_name
    PATCH_IF (STRING_LENGTH ~%wrapper_name%~ > 0) BEGIN
      SAY NAME1 ~%wrapper_name%~
    END
    READ_STRREF NAME1 wrapper_name
    PATCH_IF (STRING_LENGTH ~%wrapper_desc%~ > 0) BEGIN
      SAY UNIDENTIFIED_DESC ~%wrapper_desc%~
    END
    WRITE_SHORT 0x1c wrapper_type
    PATCH_IF wrapper_type = 4 BEGIN
      WRITE_BYTE 0x27 0 // no sectype for innates
    END
    LPF ALTER_SPELL_HEADER INT_VAR location = wrapper_location END
    READ_LONG 0x64 abil_offset
    READ_SHORT 0x68 abil_number
    READ_BYTE (abil_offset + 0x0c) abil_target
    READ_SHORT (abil_offset + 0x0e) abil_range
    WHILE (abil_number > 1) BEGIN
      SET abil_number = (abil_number - 1)
      READ_SHORT (abil_offset + 0x10 + (0x28 * abil_number)) abil_minlv
      LPF DELETE_SPELL_HEADER INT_VAR header_type = "-1" min_level = abil_minlv END // delete all but the 1st ability header
    END
    PATCH_IF wrapper_type = 4 BEGIN
      WRITE_SHORT (abil_offset + 0x12) 0 // instant casting (speed) for innates
    END
    WRITE_SHORT (abil_offset + 0x26) 1 // set projectile to none
    LPF DELETE_EFFECT END // delete all existing spell extended effects
    PATCH_MATCH abil_target WITH
      4 BEGIN // any point within range
        LPF ADD_SPELL_EFFECT INT_VAR opcode = 148 target = 1 parameter2 = 1 timing = 1 STR_VAR resource = EVAL ~%payload_spell%~ END // cast spell at point: payload spell
        PATCH_IF (op148_fix = 1) BEGIN
          PATCH_IF (abil_range < 35) BEGIN
            PATCH_IF (abil_range > 4) BEGIN
              LPF ALTER_SPELL_HEADER INT_VAR range = (abil_range - 3) END
            END
            PATCH_IF (abil_range < 5) BEGIN
              LPF ALTER_SPELL_HEADER INT_VAR target = 5 END
            END
          END
        END
      END
      1 BEGIN // living actor
        LPF ADD_SPELL_EFFECT INT_VAR opcode = 146 target = 2 parameter2 = 1 timing = 1 STR_VAR resource = EVAL ~%payload_spell%~ END // caste spell at creature: payload spell
      END
      5 7 BEGIN // caster
        LPF ADD_SPELL_EFFECT INT_VAR opcode = 146 target = 1 parameter2 = 1 timing = 1 STR_VAR resource = EVAL ~%payload_spell%~ END // caste spell at creature: payload spell
      END
      DEFAULT
        PATCH_FAIL ~PAYLOAD_SPELL_TO_WRAPPER_SPELL: ability target matching failure~
    END
  BUT_ONLY
  OUTER_TEXT_SPRINT debug_msg ~Wrapper spell %wrapper_spell% (%wrapper_name%) of type %wrapper_type% created at location %wrapper_location% to launch payload spell %payload_spell% (%payload_name%).~
END

 The function would make a "wrapper spell" that would in turn launch a "payload spell", regardless if it's uninterruptible or not.

For the wrapper spell, one can further define spell type, spell location, spell name, and spell description. Otherwise, it would retain the payload spell's most information intact (sectype would be changed to none and casting speed to zero in case of innate type), have only one ability header without any projectile, and have only one extended effect to launch the payload spell (via 146/148 depending on the payload spell's targeting).

There is an additional boolean variable to enable Subtledoctor's original fix to op148's targeting. I could not reproduce the intended issue in game myself, so I made it default to no-fix.

Sample usage:

Spoiler
LAF PAYLOAD_SPELL_TO_WRAPPER_SPELL
  INT_VAR wrapper_type = 4
          wrapper_location = 4
  STR_VAR payload_spell = EVAL ~sppr731~ // fire elemental transformation
          wrapper_spell = EVAL ~spdr701~
          wrapper_name = EVAL ~optional unique wrapper name~
          wrapper_desc = EVAL ~optional unique wrapper description~
  RET debug_msg
END
PRINT ~%debug_msg%~

Thanks go to @subtledoctor and @jmerry for their patience and help.

Link to comment

Inspired by a case in which some other mod screwed up and named a file with the wrong extension, leading to a component in my mod choking on that file, here's a near-universal sanity check function for IE binary formats. This checks the signature (first eight bytes of the file) against its extension, and checks whether the file is large enough for its type. If everything matches, return true (1). Otherwise, return false (0).

Many mods use sanity checks on size, but I'm not aware of any that also check for extension/signature matching. Go through all .SPL files, run CLONE_EFFECT to do the component's thing, fail because that one .SPL file is actually an EFF. Oops. A size check alone wouldn't have caught this, as an EFF's size (0x110) is more than twice the size of a minimal SPL (0x72). This function, however, would work.

Documentation blurb:

Spoiler
// This is a PATCH function. file_sanity_check runs on almost any IE binary format and tests whether
// the file has the proper type signature and is an appropriate size. The function will return 1 if
// the file extension matches its signature and the file is sufficiently large, as determined by the
// info array defined below. If the signature and extension don't match, the file is too small, or
// the file type isn't one of those checked against, the function will return zero.
//
// This function assumes that the SOURCE_SIZE and SOURCE_EXT variables defined by the COPY operations
// are valid for the current file. After all, if you're using this function, it should be the first
// thing you do after opening a file.
//
// The function takes no arguments, and returns the integer "valid" as either 0 or 1.

 

Full code, suitable for saving as a tpa file:

Spoiler
// This is a PATCH function. file_sanity_check runs on almost any IE binary format and tests whether
// the file has the proper type signature and is an appropriate size. The function will return 1 if
// the file extension matches its signature and the file is sufficiently large, as determined by the
// info array defined below. If the signature and extension don't match, the file is too small, or
// the file type isn't one of those checked against, the function will return zero.
//
// This function assumes that the SOURCE_SIZE and SOURCE_EXT variables defined by the COPY operations
// are valid for the current file. After all, if you're using this function, it should be the first
// thing you do after opening a file.
//
// The function takes no arguments, and returns the integer "valid" as either 0 or 1.

DEFINE_PATCH_FUNCTION file_sanity_check RET valid BEGIN

DEFINE_ASSOCIATIVE_ARRAY filetype_info BEGIN
// Extension, min size => signature in first eight bytes of file
    ~ARE~, ~0x11C~ => ~AREAV1.0~
    ~ARE~, ~0x12C~ => ~AREAV9.1~
    ~BAM~, ~0x18~  => ~BAM V1  ~
    ~BAM~, ~0x20~  => ~BAM V2  ~
    ~BIF~, ~0x14~  => ~BIFFV1  ~
    ~BIF~, ~0x14~  => ~BIF V1.0~
    ~BIF~, ~0xC~   => ~BIFCV1.0~
    ~CHR~, ~0x64~  => ~CHR V1.0~
    ~CHR~, ~0x64~  => ~CHR V1.2~
    ~CHR~, ~0x64~  => ~CHR V2.0~
    ~CHR~, ~0x224~ => ~CHR V2.2~
    ~CHR~, ~0x64~  => ~CHR V9.0~
    ~CHU~, ~0x14~  => ~CHU V1  ~
    ~CRE~, ~0x2D4~ => ~CRE V1.0~
    ~CRE~, ~0x378~ => ~CRE V1.2~
    ~CRE~, ~0x62D~ => ~CRE V2.2~
    ~CRE~, ~0x33C~ => ~CRE V9.0~
    ~DLG~, ~0x34~  => ~DLG V1.0~
    ~EFF~, ~0x110~ => ~EFF V2.0~
    ~GAM~, ~0xB8~  => ~GAMEV1.1~
    ~GAM~, ~0xB4~  => ~GAMEV2.0~
    ~GAM~, ~0xB4~  => ~GAMEV2.2~
    ~ITM~, ~0x72~  => ~ITM V1  ~
    ~ITM~, ~0x9A~  => ~ITM V1.1~
    ~ITM~, ~0x92~  => ~ITM V2.0~
    ~KEY~, ~0x18~  => ~KEY V1  ~
    ~MOS~, ~0x18~  => ~MOS V1  ~
    ~MOS~, ~0x18~  => ~MOS V2  ~
    ~PLT~, ~0x18~  => ~PLT V1  ~
    ~PRO~, ~0x100~ => ~PRO V1.0~
    ~SAV~, ~0x8~   => ~SAV V1.0~
    ~SPL~, ~0x72~  => ~SPL V1  ~
    ~SPL~, ~0x82~  => ~SPL V2.0~
    ~STO~, ~0x9C~  => ~STORV1.0~
    ~STO~, ~0x9C~  => ~STORV1.1~
    ~STO~, ~0xF0~  => ~STORV9.0~
    ~TIS~, ~0x18~  => ~TIS V1  ~
    ~TLK~, ~0x12~  => ~TLK V1  ~
    ~VVC~, ~0x1EC~ => ~VVC V1.0~
    ~WED~, ~0x20~  => ~WED V1.3~
    ~WFX~, ~0x108~ => ~WFX V1.0~
    ~WMP~, ~0x10~  => ~WMAPV1.0~
END

SET valid = 0
PATCH_IF (%SOURCE_SIZE% >= 8) BEGIN // We need to read the eight-byte signature
    READ_ASCII 0 signature
    PHP_EACH filetype_info AS info => type BEGIN
        PATCH_IF (~%read_signature%~ STR_EQ ~%type%~) BEGIN
            PATCH_IF (~%SOURCE_EXT%~ STR_EQ ~%info_0%~ AND %SOURCE_SIZE% >= %info_1%) BEGIN
                SET valid = 1
            END
        END
    END
END

END

 

(Not tested. If there are any errors, please point them out so they can be corrected.)

Link to comment

Hello folks,

here's a macros I wrote myself for something else.

It creates a file in a defined location (NOTE: The default location is the game folder).

I have also written detailed documentation which can be found here on GitHub.

Use it as you see fit. No credits are required because the code is too trivial, but if you still want to give me credits, I'm not against it either.

Macros:

Spoiler

// Never Forget: Comments can "lie", but code not so much 😜
//--------------------------------------//
// Contains self defined file operations//
//--------------------------------------//


// ---DEFAULT/INIT ARGUMENTS(Don't change this!)
// USAGE:     Copy the variable 1 to 1 to somewhere else where you need it 
//             and change its value before before calling the macro.
//            This macro can not be used in patching.
OUTER_SPRINT ~!_filename~ ~!_NULL~
// Purpose: Creates a file on a defined location(NOTE: default location is the gamefolder)
// --------------------------------------------------------------------------------------
// Parameters:|
// ============
// !_filename -> The name of the file or the path including the file(e.g. "weidu_external/workspace/filename.tph")
DEFINE_ACTION_MACRO CREATE_FILE
BEGIN
    // ALL VARIABLES IN THIS MACRO AND DEFAULT VALUES
    // ----------------------------------------------
    // STRING ~!_filename~     ~!_NULL~
    // ----------------------------------------------

    // Checks if a proper file name is given
    ACTION_IF(~%!_filename%~ STRING_EQUAL ~!_NULL~) THEN
    BEGIN
        FAIL ~DEFINE_ACTION_MACRO CREATE_FILE -> The correct argument for filename is missing. The argument !_NULL is forbidden.~
    END

    // Defines file inline then creates and copies file to location
    <<<<<<<<./weidu_external/workspace/temporaryFile.tph
    >>>>>>>>
    COPY ~./weidu_external/workspace/temporaryFile.tph~ ~./%!_filename%~
    
    // Resets macro variable
    OUTER_SPRINT ~!_filename~ ~!_NULL~
    
END// End of "CREATE_FILE"

Example:

Spoiler
// This will create a file with an extension in a specific folder/path.
OUTER_SPRINT ~!_filename~ ~weidu_external/%MOD_FOLDER%/1.tph~
LAM CREATE_FILE

If you want to add content to the file:

Spoiler
// This will append content to the file
OUTER_SPRINT text ~HELLO WORLD!~
APPEND_OUTER ~./weidu_external/%MOD_FOLDER%/1.tph~ ~~~~~PRINT ~%text%~ ~~~~~	
// Includes file, so "HELLO WORLD" can be printed.
INCLUDE ~./weidu_external/%MOD_FOLDER%/1.tph~

 

Edited by Incrementis
Small fix, but nothing that breaks the usability of the previous code
Link to comment

Good evening everyone!

This may be my last post in this thread for a long period of time(because I like to work slowly and have real life things happen, and because I'm relatively new to IE modding).

Since I'm working on something else, it creates another library that I'm happy to share.

Here are two dimorphic functions:

  • HIGHEST_STR_REF_BY_COUNTING : Calculates the highest strref value in dialog.tlk by iteratively counting through the file.
  • RESOLVE_STR_REF_WITH_STRINGS : Like RESOLVE_STR_REF, but takes only string values to resolve text and soundfile names.

For more detailed information, see the documentation on GitHub.

 

Use HIGHEST_STR_REF_BY_COUNTING as you see fit. No credits are required because the code is too trivial, but if you still want to give me credits, I'm not against it either.

Use WeiDU's NEXT_STRREF instead to determine the highest strref!

⚠️ WARNING:

This function works correctly for the en_US installation of the Enhanced Edition games, as they only have a dialog.tlk file in the "lang" folder. For the other languages, e.g. de_DE(German) the "lang" game folder contains two dialog.tlk files. This causes the "HIGHEST_STR_REF_BY_COUNTING" function to return twice the strref value, which is an incorrect value.

 

RESOLVE_STR_REF_WITH_STRINGS took me a while to write, so just don't forget to mention me by adding me in a little thank you section.

 

RESOLVE_STR_REF_WITH_STRINGS:

Spoiler

// Purpose: like RESOLVE_STR_REF, 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 string reference number which is found
DEFINE_DIMORPHIC_FUNCTION RESOLVE_STR_REF_WITH_STRINGS
    STR_VAR
        text     = ~!_NULL~
        sound    = ~!_NULL~
    RET
        strRef
BEGIN
    // Check if valid arguments are given.
    ACTION_IF (~%text%~ STRING_EQUAL ~!_NULL~) THEN 
    BEGIN
        FAIL ~DEFINE_DIMORPHIC_FUNCTION RESOLVE_STR_REF_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 RESOLVE_STR_REF_WITH_STRINGS -> The correct argument for sound is missing. The argument !_NULL is forbidden.~
    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 RESOLVE_STR_REF_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 RESOLVE_STR_REF.
    <<<<<<<<./weidu_external/workspace/!_TemporaryResolveStrRef.tph
    OUTER_SET strRef = RESOLVE_STR_REF(~%text%~ [%sound%])
    >>>>>>>>
    COPY ~./weidu_external/workspace/!_TemporaryResolveStrRef.tph~ ~./weidu_external/%MOD_FOLDER%/temp/values/!_TemporaryResolveStrRef.tph~ EVALUATE_BUFFER
    REINCLUDE ~./weidu_external/%MOD_FOLDER%/temp/values/!_TemporaryResolveStrRef.tph~
    
END// End of "RESOLVE_STR_REF_WITH_STRINGS"

💀HIGHEST_STR_REF_BY_COUNTING: Deprecated

Spoiler


// Purpose: Calculates the highest strref value in dialog.tlk
//            by iteratively counting through the file.
// -----------------------------------------------------------
// Parameters:|
// ============
// strRef -> The highest strref value in dialog.tlk
DEFINE_DIMORPHIC_FUNCTION HIGHEST_STR_REF_BY_COUNTING
    RET
        strRef
BEGIN
    OUTER_SET strRef = 0
    // Loops over dialog.tlk
    ALTER_TLK BEGIN    
        SET strRef = strRef+1
    END
    // Subtracting 1 from strRef to account for the fact that the last iteration 
    // of the loop increments strRef even though it is not a valid strref value.
    OUTER_SET strRef = strRef - 1
    
END// End of "HIGHEST_STR_REF_BY_COUNTING"

💀Example for HIGHEST_STR_REF_BY_COUNTING: Deprecated

Spoiler

BACKUP ~./weidu_external/dialogTLKtest/backup~
AUTHOR ~YOUR NAME~
VERSION ~v0.0.0~

// --------------
// Initialization
// --------------
ALWAYS 
    // LIBRARY INCLUDES(Don't change these)
    // ------------------------------------
    INCLUDE ~%MOD_FOLDER%/lib/dialogTlkFunctionalities.tph~
    
END// End of "ALWAYS"


// -----------
// Start Tests
// -----------
BEGIN ~TLK-Testing~

// -- Gets Highest stringref value(dialog.tlk)
LAF HIGHEST_STR_REF_BY_COUNTING 
    RET
        max = strRef
END
// Prints maximum number of strrefs in dialog.tlk
PRINT ~%max%~

// Example
OUTER_SET min         = max - 1000
OUTER_SET strref     = min
ALTER_TLK_RANGE min max BEGIN
    // Gets sound and text from string reference
    GET_STRREF         strref     refText
    GET_STRREF_S     strref    refSound
    
    // Print the text and mapped soundfile of the strref in dialog.tlk
    PATCH_PRINT ~%strref%: %refText% [%refSound%]~
    
    SET strref = strref+1
END

 

Edited by Incrementis
Fixing smileys in code!
Link to comment
2 minutes ago, Incrementis said:

Use HIGHEST_STR_REF_BY_COUNTING as you see fit. No credits are required because the code is too trivial, but if you still want to give me credits, I'm not against it either.

And yet, it's still not as trivial as it should be. You see, if you look at the tlk format ... the number of strref entries is right there in the header. 4 bytes, offset 0xA. Just read that, and you're done. Much faster than iterating through the file, too.

Link to comment
4 minutes ago, jmerry said:

And yet, it's still not as trivial as it should be. You see, if you look at the tlk format ... the number of strref entries is right there in the header. 4 bytes, offset 0xA. Just read that, and you're done. Much faster than iterating through the file, too.

Thanks for the alternative.

My idea is not to use bits and bytes to code things as much as possible, but that's me.

EDIT:

Regarding performance. It should be fast enough.

Edited by Incrementis
Forgot to mention something
Link to comment
On 8/24/2017 at 2:00 AM, K4thos said:

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

For anyone interested in using this beyond the EEs, I've adapted this for Tweaks Anthology to cover all games. oBG, oBG2, and the EEs all have a working pdialog so they're tied into the existing scheme. I use a static NPC list for oPsT and IWD2, if NPCs from IWD2EE are installed. I also log the non-joinables (as DV=0), since I have some components that target them specifically. I pulled it out of the macro and just INCLUDE as needed.

Here's the main library, and some example uses:

Link to comment

This is partly just for amusement, but I can see possible places it would be useful in complicated mods: functionality to create arrays with global scope even inside a function. Example of use:

INCLUDE "%MOD_FOLDER%/lib_globalize.tph"

DEFINE_ACTION_FUNCTION fn BEGIN
	ACTION_CLEAR_ARRAY test_array
	ACTION_DEFINE_ASSOCIATIVE_ARRAY test_array BEGIN
		minsc=>awesome
		anomen=>annoying
		aerie=>wet
		sarevok=>treacherous
	END
	LAF globalize_array_save STR_VAR array=test_array END
END

DEFINE_ACTION_FUNCTION fn2 BEGIN
	ACTION_CLEAR_ARRAY test_array
	ACTION_DEFINE_ASSOCIATIVE_ARRAY test_array BEGIN
		viconia=>hot
		korgan=>psychopath
		edwin=>trap-fodder
	END
	LAF globalize_array_save INT_VAR append=1 STR_VAR array=test_array END
END


LAF fn END

LAF globalize_array_load STR_VAR array=test_array RET_ARRAY test_array=array END

ACTION_PHP_EACH test_array AS k=>v BEGIN
	PRINT "%k%: %v%"
END

LAF fn2 END

LAF globalize_array_load STR_VAR array=test_array RET_ARRAY test_array=array END

ACTION_PHP_EACH test_array AS k=>v BEGIN
	PRINT "%k%: %v%"
END

Library attached; see if you can guess how it works before looking! (Hint: it doesn't just involve reading in a textfile each time, though I'm unclear whether that would be quicker.)

lib_globalize.tph

Link to comment

Good morning everyone,

I found some time to work on something, but even though it's not finished yet, it's created two libraries that I'm happy to share (Yes, I'm very slow, but hobbies are fun and rushing isn't).

Here are four dimorphic functions:

  • ERR_EVALUATE_FILE_EXISTANCE: Raises an error if the specified file exists and prevents further installation
  • ERR_EVALUATE_GAME_EXISTANCE: Raises an error if the specified WeiDU game short is detected and prevents further installation
  • WARN_EVALUATE_FILE_EXISTANCE: Raises a warning if the specified file exists
  • WARN_EVALUATE_GAME_EXISTANCE: Raises a warning if the specified WeiDU game short is detected

For more detailed information, see the documentation on GitHub.

  • prefix "ERR" in "ERR_<function name>" indicates that this is an error function
  • prefix "WARN" in "WARN_<function name>" indicates that this is a warning function

No credits are required as sanity checks in mods help to better understand the code and mods and create a healthier environment so that everyone benefits.

errors.tph

Spoiler
// Never Forget: Comments can "lie", but code not so much ;P
//----------------------------------------//
// Contains custom defined error functions//
//----------------------------------------//

// Purpose: Raises an error if the specified file exists and prevents further installation
// ---------------------------------------------------------------------------------------
// Parameters:|
// ============
// file     -> The name of the file to check for existence
// message    -> The error message to display upon file existence
DEFINE_DIMORPHIC_FUNCTION ~ERR_EVALUATE_FILE_EXISTANCE~
    STR_VAR
        file     = ~~
        message = ~~
BEGIN
    ACTION_IF (FILE_EXISTS ~%file%~) THEN 
    BEGIN
        FAIL ~%message%~
    END
END // End of "ERR_EVALUATE_FILE_EXISTANCE"


// Purpose: Raises an error if the specified WeiDU game short is detected and prevents further installation
// --------------------------------------------------------------------------------------------------------
// Parameters:|
// ============
// game     -> The WeiDU game short identifier to check for installation
// message    -> The error message to display if the specified game is detected
DEFINE_DIMORPHIC_FUNCTION ~ERR_EVALUATE_GAME_EXISTANCE~
    STR_VAR
        game     = ~~
        message = ~~
BEGIN
    ACTION_IF (GAME_IS ~%game%~) THEN 
    BEGIN
        FAIL ~%message%~
    END
END // End of "ERR_EVALUATE_GAME_EXISTANCE"

 

warnings.tph

Spoiler
// Never Forget: Comments can "lie", but code not so much ;P
//------------------------------------------//
// Contains custom defined warning functions//
//------------------------------------------//

// Purpose: Raises a warning if the specified file exists
// ------------------------------------------------------
// Parameters:|
// ============
// file     -> The name of the file to check for existence
// message    -> The warning message to display upon file existence
DEFINE_DIMORPHIC_FUNCTION ~WARN_EVALUATE_FILE_EXISTANCE~
    STR_VAR
        file     = ~~
        message = ~~
BEGIN
    ACTION_IF (FILE_EXISTS ~%file%~) THEN 
    BEGIN
        PRINT ~--------~
        PRINT ~WARNING:~
        PRINT ~--------~
        PRINT ~%message%~
    END
END // End of "WARN_EVALUATE_FILE_EXISTANCE"


// Purpose: Raises a warning if the specified WeiDU game short is detected
// -----------------------------------------------------------------------
// Parameters:|
// ============
// game     -> The WeiDU game short identifier to check for installation
// message    -> The warning message to display if the specified game is detected
DEFINE_DIMORPHIC_FUNCTION ~WARN_EVALUATE_GAME_EXISTANCE~
    STR_VAR
        game     = ~~
        message = ~~
BEGIN
    ACTION_IF (GAME_IS ~%game%~) THEN 
    BEGIN
        PRINT ~--------~
        PRINT ~WARNING:~
        PRINT ~--------~
        PRINT ~%message%~
    END
END // End of "WARN_EVALUATE_GAME_EXISTANCE"

 

Possible use for error:

Spoiler
BEGIN ~Games incompatible~

// Creates an array with game shortcuts which are incompatible with mod (produces no error for BG:EE, see comment in array)
ACTION_DEFINE_ARRAY games_incompatible
BEGIN 
    ~bg2~
     ~tob~
    ~iwd2~
    ~pst~
    ~bg1~
    ~totsc~
    ~iwd1~
    ~how~
    ~totlm~
    ~tutu~
    ~tutu_totsc~
    ~bgt~
    ~ca~
    ~iwd_in_bg2~
    //~bgee~
    ~bg2ee~
    ~eet~
    ~iwdee~
    ~pstee~ 
END


// OUTSIDE OF PATCHING
// -------------------
OUTER_SPRINT error ~Mod is incompatible with game! The Installation is aborted.~

// Checks all game shortcuts
OUTER_FOR (index = 0; VARIABLE_IS_SET $games_incompatible(~%index%~); ++index) BEGIN

    // Gets specific game shortcut
    OUTER_TEXT_SPRINT game $games_incompatible(~%index%~)

    // Calls error 
    LAF ERR_EVALUATE_GAME_EXISTANCE 
        STR_VAR 
            game     = EVAL ~%game%~
            message = EVAL ~%error%~
        END
END


 

Possible use for warning:

Spoiler
// Creates an array with game shortcuts whose compatibilty with the mod is uncertain (produces no warning for BG:EE, see comment in array)
ACTION_DEFINE_ARRAY games_insecure_compatibility
BEGIN 
    ~bg2~
    ~tob~
    ~iwd2~
    ~pst~
    ~bg1~
    ~totsc~
    ~iwd1~
    ~how~
    ~totlm~
    ~tutu~
    ~tutu_totsc~
    ~bgt~
    ~ca~
    ~iwd_in_bg2~
    //~bgee~
    ~bg2ee~
    ~eet~
    ~iwdee~
    ~pstee~
END

// OUTSIDE OF PATCHING
// -------------------
OUTER_SPRINT warning ~Insecure game compatibility detected! No guarantee for smooth functionality in case of successful installation. If you encounter an error after installation, this may be the cause.~

// Checks all game shortcuts
OUTER_FOR (index = 0; VARIABLE_IS_SET $games_insecure_compatibility(~%index%~); ++index) BEGIN

    // Gets specific game shortcut
    OUTER_TEXT_SPRINT game $games_insecure_compatibility(~%index%~)

    // Calls warning 
    LAF WARN_EVALUATE_GAME_EXISTANCE 
        STR_VAR 
            game     = EVAL ~%game%~
            message = EVAL ~%warning%~
        END
END

 

 

Link to comment
On 12/29/2022 at 1:45 AM, jmerry said:

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

 

A year later still... well, yes and no. The thing READ_2DA_ENTRIES_NOW spits out isn't a proper array: you can't PHP_EACH through it or return it from a function. (Effectively my function is a wrap for READ_2DA_ENTRIES_NOW that returns a proper array.)

Link to comment

Apropos this conversation, here's an SFO function which as it happens is self-contained. It attempts to diagnose whether a 2DA file is broken (or has incomplete lines, which technically is legal but annoys WEIDU), and then fix broken 2DA files by

(i) forcing the first line to be '2DA V1.0'

(ii) removing any entry beyond the first on the second line

(iii) truncating any entries that lie beyond the range defined by the columns on the third line

(iv) filling in any incomplete entries with the array's default character.

Returns 'legal', which is 1 or 0, and 'fixed_file', which is just a string containing the full contents of the file. Optional argument: 'apply_to_file' (can be 1 or 0, default is 1); if set to 1, the changes are actually carried out on the file being patched; if set to 0, they're discarded (but contained in the 'fixed_file' variable - this is because my use case is nondestructive read).

As with all my functions, this assumes AUTO_EVAL_STRINGS.

DEFINE_PATCH_FUNCTION 2da_fix
	INT_VAR apply_to_file=1
	RET legal
		fixed_file
BEGIN
	COUNT_2DA_COLS colcount
	COUNT_2DA_ROWS 1 rowcount_one
	COUNT_2DA_ROWS 2 rowcount_two
	COUNT_2DA_ROWS colcount rowcount_max
	COUNT_2DA_ROWS (colcount - 1) rowcount_max_minus_one
	PATCH_IF rowcount_one>=4 BEGIN
	PATCH_MATCH colcount WITH
	1 BEGIN // no salvageable 2da file has colcount=1
		PATCH_FAIL "2da_sanity_check: current file (probably %SOURCE_FILE%) is unfixable"
	END
	2 BEGIN // all rows should have 2 columns except the default and the column labels
		legal = (rowcount_one = rowcount_two + 2)
	END
	3 BEGIN // there should be one 1-column row (the default), two 2-column rows (the sig and the column labels) and the rest 3-column
		legal = (rowcount_one = rowcount_max + 3) && (rowcount_two = rowcount_max + 2)
	END
	DEFAULT // there should be one 1-column row, 1 2-column row, 1 (N-1)-column row, and the rest N columns
		legal = (rowcount_one = rowcount_max + 3) && (rowcount_two = rowcount_max +2) && (rowcount_max_minus_one = rowcount_max + 1)
	END
	END ELSE BEGIN
		legal=1  // we can't fix problems in empty 2DAs
	END
	PATCH_IF legal BEGIN
		READ_ASCII 0x0 fixed_file (BUFFER_LENGTH)
	END ELSE BEGIN
		// get the contents
		READ_ASCII 0x0 data (BUFFER_LENGTH)
		// break into lines
		INNER_PATCH "%data%" BEGIN
			CLEAR_ARRAY lines
			count=0
			REPLACE_EVALUATE "^\([^%WNL%%MNL%%LNL%]+\)" BEGIN
				SPRINT $lines("%count%") "%MATCH1%"
				++count
			END	
			""
		END
		// fix first line if necessary
		SPRINT $lines(0) "2DA V1.0"
		// get default character and fix if needed
		SPRINT default_line $lines(1)
		INNER_PATCH "%default_line%" BEGIN
			REPLACE_EVALUATE "^\([^ %TAB%]+\)" BEGIN
				SPRINT default_entry "%MATCH1%"
			END
			""
		END
		SPRINT $lines(1) "%default_entry%"
		// get the column count
		SPRINT column_line $lines(2)
		INNER_PATCH " %column_line%" BEGIN // extra space just in case
			count=0
			REPLACE_EVALUATE "[ %TAB%]+[^ %TAB%]+" BEGIN
				++count
			END
			""
		END
		real_colcount=count
		// fix the remaining lines
		PHP_EACH lines AS line_num=>line BEGIN
			PATCH_IF line_num>2 BEGIN
				SPRINT line_new ""
				INNER_PATCH " %line%" BEGIN // extra space just in case
					count=0
					REPLACE_EVALUATE "[ %TAB%]+\([^ %TAB%]+\)" BEGIN
						PATCH_IF count<=real_colcount BEGIN
							SPRINT line_new "%line_new%%MATCH1% "
						END
						++count						
					END
					""
				END		
				FOR (n=count;n<=real_colcount;++n) BEGIN
					SPRINT line_new "%line_new%%default_entry% "
				END
				SPRINT $lines("%line_num%") "%line_new%"
			END
		END
		// make the file
		SPRINT fixed_file ""
		PHP_EACH lines AS line_num=>line BEGIN
			SPRINT fixed_file "%fixed_file%%line%%WNL%"
		END
		// apply fix if required
		PATCH_IF apply_to_file BEGIN
			DELETE_BYTES 0x0 BUFFER_LENGTH
			INSERT_BYTES 0x0 STRING_LENGTH "%fixed_file%"
			WRITE_ASCII 0x0 "%fixed_file%"
			PRETTY_PRINT_2DA
			REPLACE_TEXTUALLY "2DA[ %TAB%]+V1\.0" "2DA V1.0"
		END
	END
END

 

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