Autolykos
Regular
Common Warband ARray Processing
Disclaimer: This may or may not be news, but I couldn't find anything similar here or in any of the guides. OTOH, "functional", "script" and "parameter" are poor keywords for a search, so I might have just missed it.
After a bit of toying around with Warband scripts on the one hand and functional programming on the other, I discovered that scripts are perfectly valid parameters for other scripts, which enables a whole lot of really neat programming tricks. This seems to be unintentional - I have never seen it used that way in the vanilla scripts.
As a proof of concept, I wrote a bunch of scripts that make use of this feature to implement a few simple algorithms (for now, a natural mergesort and array shuffling, but more will follow):
WARNING: Wall of code
As an example, say you want to print a list of all Swadian lords, sorted by their current renown. You would do it this way:
with a simple helper script:
EDIT: Minor bugfixes, and new functions for reversing arrays and copying subranges.
EDIT2: Some cleanup, and fixed a major bug in warp_array_copy; also changed syntax to be more in line with instructions like str_copy_string_reg (first destination, then source)
- This seems to be true for Mount&Blade as well (except that I haven't found any bugs with it yet).Greenspun's tenth rule said:Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Disclaimer: This may or may not be news, but I couldn't find anything similar here or in any of the guides. OTOH, "functional", "script" and "parameter" are poor keywords for a search, so I might have just missed it.
After a bit of toying around with Warband scripts on the one hand and functional programming on the other, I discovered that scripts are perfectly valid parameters for other scripts, which enables a whole lot of really neat programming tricks. This seems to be unintentional - I have never seen it used that way in the vanilla scripts.
As a proof of concept, I wrote a bunch of scripts that make use of this feature to implement a few simple algorithms (for now, a natural mergesort and array shuffling, but more will follow):
WARNING: Wall of code
For this to work, create a new party called "warp_temp" and copy this code into module_scripts:
Code:
###################################
# Common Warband ARray Processing #
###################################
# Functional programming using Party Slots as arrays (or stacks)
# Parties are the only way to dynamically allocate memory in WB scripts
# Slots are already arrays of integers (or IDs), making them ideal for storage
# Slot zero is reserved for the number of elements
# Part I - Building Blocks
##########################
# script_warp_array_clear
# Clears an array by setting its length to zero
# Input: arg1 = array_id
# Output: nothing
("warp_array_clear",
[ (store_script_param, ":array_id", 1),
(party_set_slot, ":array_id", 0, 0),
]),
# script_warp_array_length
# Returns the length of an array
# Input: arg1 = array_id
# Output: reg0 = length
("warp_array_length",
[ (store_script_param, ":array_id", 1),
(party_get_slot, reg0, ":array_id", 0),
]),
# script_warp_array_init_value
# Fills the array with (arg2) repetitions of (arg3)
# Input: arg1 = array_id
# arg2 = length
# arg3 = value
# Output: nothing
("warp_array_init_value",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":length", 2),
(store_script_param, ":value", 3),
(party_set_slot, ":array_id", 0, ":length"),
(val_add, ":length", 1),
(try_for_range, ":i", 1, ":length"),
(party_set_slot, ":array_id", ":i", ":value"),
(try_end),
]),
# script_warp_array_init_range
# Fills the array with the numbers/IDs from (arg2) to (arg3)
# Input: arg1 = array_id
# arg2 = first
# arg2 = end
# Output: nothing
("warp_array_init_range",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":first", 2),
(store_script_param, ":end", 3),
(assign, ":slot", 0),
(try_for_range, ":i", ":first", ":end"),
(val_add, ":slot", 1),
(party_set_slot, ":array_id", ":slot", ":i"),
(try_end),
(party_set_slot, ":array_id", 0, ":slot"),
]),
# script_warp_array_init_random
# Fills the array with (arg2) random numbers in the range between (arg3) and (arg4)
# Input: arg1 = array_id
# arg2 = length
# arg3 = min_value
# arg4 = max_value
# Output: nothing
("warp_array_init_random",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":length", 2),
(store_script_param, ":min", 3),
(store_script_param, ":max", 4),
(party_set_slot, ":array_id", 0, ":length"),
(val_add, ":length", 1),
(try_for_range, ":i", 1, ":length"),
(store_random_in_range, ":value", ":min", ":max"),
(party_set_slot, ":array_id", ":i", ":value"),
(try_end),
]),
# script_warp_array_push
# Appends an element to an array
# Input: arg1 = array_id
# arg2 = element
# Output: nothing
("warp_array_push",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":element", 2),
(party_get_slot, ":length", ":array_id", 0),
(val_add, ":length", 1),
(party_set_slot, ":array_id", ":length", ":element"),
(party_set_slot, ":array_id", 0, ":length"),
]),
# script_cf_warp_array_pop
# Removes the last element and writes it to reg0
# Fails if the array is empty
# Input: arg1 = array_id
# Output: reg0 = popped element
("cf_warp_array_pop",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":length", ":array_id", 0),
(gt, ":length", 0),
(party_get_slot, reg0, ":array_id", ":length"),
(val_sub, ":length", 1),
(party_set_slot, ":array_id", 0, ":length"),
]),
# script_warp_array_remove_last
# Removes the last element if it exists
# Input: arg1 = array_id
# Output: nothing
("warp_array_remove_last",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":length", ":array_id", 0),
(val_sub, ":length", 1),
(val_max, ":length", 0),
(party_set_slot, ":array_id", 0, ":length"),
]),
# script_cf_warp_array_last
# Writes the last element to reg0
# Fails if the array is empty
# Input: arg1 = array_id
# Output: reg0 = last element
("cf_warp_array_last",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":length", ":array_id", 0),
(gt, ":length", 0),
(party_get_slot, reg0, ":array_id", ":length"),
]),
# script_cf_warp_array_set
# Sets the (arg2)th element to (arg3)
# Fails if the array is too small
# Input: arg1 = array_id
# arg2 = index
# arg3 = value
# Output: nothing
("cf_warp_array_set",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":index", 2),
(store_script_param, ":value", 3),
(gt, ":index", 0),
(store_sub, ":index-1", ":index", 1),
(party_slot_ge, ":array_id", 0, ":index-1"),
(try_begin),
(party_slot_eq, ":array_id", 0, ":index-1"),
(party_set_slot, ":array_id", 0, ":index"),
(try_end),
(party_set_slot, ":array_id", ":index", ":value"),
]),
# script_cf_warp_array_get
# Writes the (arg2)ths element to reg0
# Fails if the index is out of bounds
# Input: arg1 = array_id
# arg2 = index
# Output: reg0 = value
("cf_warp_array_get",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":index", 2),
(gt, ":index", 0),
(party_slot_ge, ":array_id", 0, ":index"),
(party_get_slot, reg0, ":array_id", ":index"),
]),
# script_warp_array_copy
# Copies array 1 from array 2
# Input: arg1 = dest_array
# arg2 = src_array
# Output: nothing
("warp_array_copy",
[ (store_script_param, ":dest_array", 1),
(store_script_param, ":src_array", 2),
(party_get_slot, ":length", ":src_array", 0),
(party_set_slot, ":dest_array", 0, ":length"),
(val_add, ":length", 1),
(try_for_range, ":i", 1, ":length"),
(party_get_slot, ":v", ":src_array", ":i"),
(party_set_slot, ":dest_array", ":i", ":v"),
(try_end),
]),
# script_cf_warp_array_copy_range
# Copies the subrange (arg2)..(arg3) from array 1 to array 2
# Input: arg1 = dest_array
# arg2 = src_array
# arg3 = first
# arg4 = end
#
# Output: nothing
("cf_warp_array_copy_range",
[ (store_script_param, ":dest_array", 1),
(store_script_param, ":src_array", 4),
(store_script_param, ":first", 2),
(store_script_param, ":end", 3),
(store_sub,":last",":end",1),
(party_slot_ge, ":src_array",0,":last"), # Is the source array long enough?
(assign, ":j", 0),
(try_for_range, ":i", ":first", ":end"),
(val_add,":j",1), # count the elements written
(party_get_slot, ":v", ":src_array", ":i"),
(party_set_slot, ":dest_array", ":j", ":v"),
(try_end),
(party_set_slot, ":dest_array", 0, ":j"),
]),
# script_warp_array_reverse
# Reverses the array in place
# Input: arg1 = array_id
# Output: nothing
("warp_array_reverse",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":last", ":array_id", 0),
(try_for_range, ":i", 1, ":last"),
(gt,":last",":i"), # otherwise, we're done
(party_get_slot, ":v1", ":array_id", ":i"),
(party_get_slot, ":v2", ":array_id", ":last"),
(party_set_slot, ":array_id", ":i", ":v2"),
(party_set_slot, ":array_id", ":last", ":v1"),
(val_sub,":last",1),
(try_end),
]),
# script_warp_array_filter
# Removes all elements that fail the check of cf_filter
# Input: arg1 = array_id
# arg2 = cf_filter
# Output: nothing
("warp_array_filter",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":cf_filter", 2),
(party_get_slot, ":end", ":array_id", 0),
(val_add, ":end", 1),
(assign, ":matches", 0),
(try_for_range, ":i", 1, ":end"),
(party_get_slot, ":ce", ":array_id", ":i"),
(call_script,":cf_filter",":ce"),
(val_add,":matches",1),
(gt,":i",":matches"),
(party_set_slot, ":array_id", ":matches", ":ce"),
(try_end),
(party_set_slot, ":array_id", 0, ":matches"),
]),
# script_warp_array_map
# Applies a mapping function (p1->reg0) to every element and writes the result in its place
# Elements are removed if the mapping function fails
# Input: arg1 = array_id
# arg2 = cf_map
# Output: nothing
("warp_array_map",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":cf_map", 2),
(party_get_slot, ":end", ":array_id", 0),
(val_add, ":end", 1),
(assign, ":matches", 0),
(try_for_range, ":i", 1, ":end"),
(party_get_slot, ":ce", ":array_id", ":i"),
(call_script,":cf_map",":ce"),
(val_add,":matches",1),
(party_set_slot, ":array_id", ":matches", reg0),
(try_end),
(party_set_slot, ":array_id", 0, ":matches"),
]),
# script_warp_array_unique
# Makes sure the array contains each element only once
# Input: arg1 = array_id
# Output: nothing
("warp_array_unique",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":end", ":array_id", 0),
(val_add, ":end", 1),
(assign, ":unique", 0),
(try_for_range, ":i", 1, ":end"),
(party_get_slot, ":ce", ":array_id", ":i"),
(assign,":found",0),
(try_for_range, ":j", 1, ":i"),
(party_slot_eq, ":array_id", ":j", ":ce"),
(assign,":found",1),
(assign,":j",":i"),
(try_end),
(eq,":found",0),
(val_add,":unique",1),
(gt,":i",":unique"),
(party_set_slot, ":array_id", ":unique", ":ce"),
(try_end),
(party_set_slot, ":array_id", 0, ":unique"),
]),
# script_warp_array_sort
# Sorts the array using natural merge sort and a function cf_order(a,b) that fails iff a,b is the wrong order
# Input: arg1 = array_id
# arg2 = cf_order
# Output: nothing
("warp_array_sort",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":cf_order", 2),
(party_get_slot, ":end", ":array_id", 0),
(val_add, ":end", 1),
(call_script,"script_warp_array_sort_range_aux",":array_id","p_warp_temp",1,":end",":cf_order"),
(try_begin),
(eq,reg0,"p_warp_temp"),
(try_for_range, ":i", 1, ":end"),
(party_get_slot, ":v", "p_warp_temp", ":i"),
(party_set_slot, ":array_id", ":i", ":v"),
(try_end),
(try_end),
]),
# script_warp_array_sort_range
# Sorts only a subrange of the array; otherwise same as "script_warp_array_sort"
# Input: arg1 = array_id
# arg2 = first
# arg3 = end
# arg4 = cf_order
# Output: nothing
("warp_array_sort_range",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":first", 2),
(store_script_param, ":end", 3),
(store_script_param, ":cf_order", 4),
(call_script,"script_warp_array_sort_range_aux",":array_id","p_warp_temp",":first",":end",":cf_order"),
(try_begin),
(eq,reg0,"p_warp_temp"),
(try_for_range, ":i", ":first", ":end"),
(party_get_slot, ":v", "p_warp_temp", ":i"),
(party_set_slot, ":array_id", ":i", ":v"),
(try_end),
(try_end),
]),
# Auxiliary function; recursively performs one round of mergesort from array_1 to array_2 and writes the sorted array_id to reg0 when done
("warp_array_sort_range_aux",
[ (store_script_param, ":array_1", 1),
(store_script_param, ":array_2", 2),
(store_script_param, ":first", 3),
(store_script_param, ":end", 4),
(store_script_param, ":cf_order", 5),
(assign,":p1",1),
(assign,":p2",-1),
(assign,":sorted",1),
(store_add, ":second", ":first", 1),
(store_sub, ":last", ":end", 1),
(try_for_range, ":i", ":second", ":last"),
(store_sub, ":i-1", ":i", 1),
(party_get_slot, ":val_a", ":array_1", ":i-1"),
(party_get_slot, ":val_b", ":array_1", ":i"),
(try_begin),
(call_script,":cf_order", ":val_a", ":val_b"),
(else_try), # Check failed -> wrong order!
(assign,":sorted",0),
(try_begin),
(le,":p2",0),
(assign,":p2",":i"),
(else_try),
(call_script,"script_warp_array_merge_range_aux",":array_1",":array_2",":p1",":p2",":i",":cf_order"),
(assign,":p1",":i"),
(assign,":p2",-1),
(try_end),
(try_end),
(try_end),
(try_begin), # Merge last two runs (if there are)
(gt,":p2",0),
(lt,":p2",":end"),
(call_script,"script_warp_array_merge_range_aux",":array_1",":array_2",":p1",":p2",":end",":cf_order"),
(else_try), # Or copy the last run (if necessary)
(lt,":p1",":end"),
(eq,":sorted",0), # otherwise, just return array_1
(try_for_range, ":i", ":p1", ":end"),
(party_get_slot, ":v", ":array_1", ":i"),
(party_set_slot, ":array_2", ":i", ":v"),
(try_end),
(try_end),
(try_begin),
(eq,":sorted",1), # Am I done yet?
(assign,reg0,":array_1"),
(else_try),
(call_script,"script_warp_array_sort_range_aux",":array_2",":array_1",":first",":end",":cf_order"),
(try_end),
(try_end),
]),
("warp_array_merge_range_aux",
[ (store_script_param, ":array_1", 1),
(store_script_param, ":array_2", 2),
(store_script_param, ":pos1", 3),
(store_script_param, ":pos2", 4),
(store_script_param, ":pos3", 5),
(store_script_param, ":cf_order", 6),
(assign,":c1",":pos1"),
(assign,":c2",":pos2"),
(try_for_range, ":i", ":pos1", ":pos3"),
(try_begin),
(eq,":c1",":pos2"),
(party_get_slot, ":v", ":array_1", ":c2"),
(party_set_slot, ":array_2", ":i", ":v"),
(val_add,":c2",1),
(else_try),
(eq,":c2",":pos3"),
(party_get_slot, ":v", ":array_1", ":c1"),
(party_set_slot, ":array_2", ":i", ":v"),
(val_add,":c1",1),
(else_try),
(party_get_slot, ":val_a", ":array_1", ":c1"),
(party_get_slot, ":val_b", ":array_1", ":c2"),
(try_begin),
(call_script,":cf_order", ":val_a", ":val_b"),
(party_get_slot, ":v", ":array_1", ":c1"),
(party_set_slot, ":array_2", ":i", ":v"),
(val_add,":c1",1),
(else_try),
(party_get_slot, ":v", ":array_1", ":c2"),
(party_set_slot, ":array_2", ":i", ":v"),
(val_add,":c2",1),
(try_end),
(try_end),
(try_end),
]),
# script_warp_array_shuffle
# Shuffles the array (puts all elements in random order)
# Input: arg1 = array_id
# Output: nothing
("warp_array_shuffle",
[ (store_script_param, ":array_id", 1),
(party_get_slot, ":end", ":array_id", 0),
(val_add, ":end", 1),
(call_script,"script_warp_array_shuffle_range",":array_id",1,":end"),
]),
# script_warp_array_shuffle_range
# Shuffles a subrange of the array
# Input: arg1 = array_id
# arg2 = first
# arg3 = end
# Output: nothing
("warp_array_shuffle_range",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":first", 2),
(store_script_param, ":end", 3),
(try_for_range, ":i", ":first", ":end"),
(store_random_in_range,":j",":i",":end"),
(neq,":i",":j"),
(party_get_slot, ":vi", ":array_id", ":i"),
(party_get_slot, ":vj", ":array_id", ":j"),
(party_set_slot, ":array_id", ":i", ":vj"),
(party_set_slot, ":array_id", ":j", ":vi"),
(try_end),
]),
# script_warp_print_array
# Writes an array's content to s3, using a map function and custom separators
# e.g. (1,4,6,9),"script_warp_roman","@ or ","@ or maybe" -> "I or IV or VI or maybe IX"
# Input: arg1 = array_id
# arg2 = script that writes the ID's name to s0 - MUST NOT TOUCH s1, s2 or s3!
# Set before calling:
# s1 = middle separator (usually comma or space)
# s2 = last separator (usually and)
# Output: s3 = List of strings returned by arg4
("warp_print_array",
[
(store_script_param, ":array_id", 1),
(store_script_param, ":script_name", 2),
(party_get_slot, ":array_last", ":array_id", 0),
(str_clear,s3),
(try_begin),
(eq,":array_last",1),
(party_get_slot, ":v", ":array_id", ":array_last"),
(call_script,":script_name",":v"),
(str_store_string,s3,"@{s0}"),
(else_try),
(ge,":array_last",2),
(party_get_slot, ":v", ":array_id", 1),
(call_script,":script_name",":v"),
(str_store_string,s3,"@{s0}"),
(try_for_range, ":i", 2, ":array_last"),
(party_get_slot, ":v", ":array_id", ":i"),
(call_script,":script_name",":v"),
(str_store_string,s3,"@{s3}{s1}{s0}"),
(try_end),
(party_get_slot, ":v", ":array_id", ":array_last"),
(call_script,":script_name",":v"),
(str_store_string,s3,"@{s3}{s2}{s0}"),
(try_end),
]),
# script_warp_print_array_comma
# Writes an array's content to s1, separated by commas and using a map function
# e.g. (1,4,6,9),"script_warp_roman" -> "I, IV, VI, IX"
# Input: arg1 = array_id
# arg2 = script that writes the ID's name to s0 - MUST NOT TOUCH s1!
# Output: s1 = List of strings returned by arg4
("warp_print_array_comma",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":script_name", 2),
(party_get_slot, ":array_last", ":array_id", 0),
(store_add,":array_end",":array_last",1),
(str_clear,s1),
(try_begin),
(eq,":array_last",1),
(party_get_slot, ":v", ":array_id", ":array_last"),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s0}"),
(else_try),
(ge,":array_last",2),
(party_get_slot, ":v", ":array_id", 1),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s0}"),
(try_for_range, ":i", 2, ":array_end"),
(party_get_slot, ":v", ":array_id", ":i"),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s1}, {s0}"),
(try_end),
(try_end),
]),
# script_warp_print_array_and
# Writes an array's content to s1, separated by commas, a final "and" and using a map function
# e.g. (1,4,6,9),"script_warp_roman" -> "I, IV, VI and IX"
# Input: arg1 = array_id
# arg2 = script that writes the ID's name to s0 - MUST NOT TOUCH s1!
# Output: s1 = List of strings returned by arg4
("warp_print_array_and",
[ (store_script_param, ":array_id", 1),
(store_script_param, ":script_name", 2),
(party_get_slot, ":array_last", ":array_id", 0),
(str_clear,s1),
(try_begin),
(eq,":array_last",1),
(party_get_slot, ":v", ":array_id", ":array_last"),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s0}"),
(else_try),
(ge,":array_last",2),
(party_get_slot, ":v", ":array_id", 1),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s0}"),
(try_for_range, ":i", 2, ":array_last"),
(party_get_slot, ":v", ":array_id", ":i"),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s1}, {s0}"),
(try_end),
(party_get_slot, ":v", ":array_id", ":array_last"),
(call_script,":script_name",":v"),
(str_store_string,s1,"@{s1} and {s0}"),
(try_end),
]),
# Part II - Helper Scripts
##########################
# script_warp_number
# Takes a number and writes it to s0
# Input: arg1 = number
# Output: s0 = number string
("warp_number",
[ (store_script_param, ":number", 1),
(assign,reg0,":number"),
(str_store_string,s0,"@{reg0}"),
]),
# script_warp_roman
# Takes a number and writes its roman numeral to s0
# Input: arg1 = number
# Output: s0 = roman numeral
("warp_roman",
[ (store_script_param, ":number", 1),
(try_begin),
(gt,":number",0),
(str_clear,s0),
(call_script,"script_warp_roman_aux",":number"),
(else_try),
(eq,":number",0),
(str_store_string,s0,"@0"),
(else_try),
(store_sub,":neg_number",0,":number"),
(str_clear,s0),
(call_script,"script_warp_roman_aux",":neg_number"),
(str_store_string,s0,"@-{s0}"),
(try_end),
]),
# Recursive auxiliary function; requires empty s0 and n>0 at the beginning
("warp_roman_aux",
[ (store_script_param, ":number", 1),
(try_begin),
(ge,":number",1000),
(store_sub,":rest",":number",1000),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@M{s0}"),
(else_try),
(ge,":number",900),
(store_sub,":rest",":number",900),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@CM{s0}"),
(else_try),
(ge,":number",500),
(store_sub,":rest",":number",500),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@D{s0}"),
(else_try),
(ge,":number",400),
(store_sub,":rest",":number",400),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@CD{s0}"),
(else_try),
(ge,":number",100),
(store_sub,":rest",":number",100),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@C{s0}"),
(else_try),
(ge,":number",90),
(store_sub,":rest",":number",90),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@XC{s0}"),
(else_try),
(ge,":number",50),
(store_sub,":rest",":number",50),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@L{s0}"),
(else_try),
(ge,":number",40),
(store_sub,":rest",":number",40),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@XL{s0}"),
(else_try),
(ge,":number",10),
(store_sub,":rest",":number",10),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@X{s0}"),
(else_try),
(ge,":number",9),
(store_sub,":rest",":number",9),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@IX{s0}"),
(else_try),
(ge,":number",5),
(store_sub,":rest",":number",5),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@V{s0}"),
(else_try),
(ge,":number",4),
(store_sub,":rest",":number",4),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@IV{s0}"),
(else_try),
(ge,":number",1),
(store_sub,":rest",":number",1),
(call_script,"script_warp_roman_aux",":rest"),
(str_store_string,s0,"@I{s0}"),
(try_end),
]),
# More printing
("warp_troop_name",[(store_script_param, ":troop", 1), (str_store_troop_name, s0, ":troop")]),
("warp_party_name",[(store_script_param, ":party", 1), (str_store_party_name, s0, ":party")]),
("warp_item_name",[(store_script_param, ":item", 1), (str_store_item_name, s0, ":item")]),
# Some basic sorting functions
("cf_ascending",
[ (store_script_param, ":a", 1),
(store_script_param, ":b", 2),
(ge,":b",":a"),
]),
("cf_descending",
[ (store_script_param, ":a", 1),
(store_script_param, ":b", 2),
(ge,":a",":b"),
]),
As an example, say you want to print a list of all Swadian lords, sorted by their current renown. You would do it this way:
Code:
(call_script,"script_warp_array_init_range","p_temp_party","trp_knight_1_1","trp_knight_2_1"),
(call_script,"script_warp_array_sort","p_temp_party","script_cf_renown_ge"),
(call_script,"script_warp_print_array_and","p_temp_party","script_warp_troop_name"),
(display_message, "@These lords are the most important: {s1}"),
Code:
("cf_renown_ge",
[ (store_script_param, ":lord_a", 1),
(store_script_param, ":lord_b", 2),
(troop_get_slot, ":b_renown", ":lord_b", slot_troop_renown),
(troop_slot_ge, ":lord_a", slot_troop_renown, ":b_renown"),
]),
EDIT: Minor bugfixes, and new functions for reversing arrays and copying subranges.
EDIT2: Some cleanup, and fixed a major bug in warp_array_copy; also changed syntax to be more in line with instructions like str_copy_string_reg (first destination, then source)