diff --git a/artifacts/scripting/headers/command_lite.h b/artifacts/scripting/headers/command_lite.h index 734f7f58..f6de703e 100644 --- a/artifacts/scripting/headers/command_lite.h +++ b/artifacts/scripting/headers/command_lite.h @@ -170,9 +170,26 @@ #define self_elevation (elevation(self_obj)) #define self_pid (obj_pid(self_obj)) + +// team #define self_team has_trait(TRAIT_OBJECT,self_obj,OBJECT_TEAM_NUM) +#define get_team(cr) has_trait(TRAIT_OBJECT,cr,OBJECT_TEAM_NUM) +#define set_team(cr,team) critter_add_trait(cr,TRAIT_OBJECT,OBJECT_TEAM_NUM,team) +#define set_self_team(team) set_team(self_obj,team) + +// ai #define self_ai has_trait(TRAIT_OBJECT,self_obj,OBJECT_AI_PACKET) +#define get_ai(cr) has_trait(TRAIT_OBJECT,cr,OBJECT_AI_PACKET) +#define set_ai(cr,ai) critter_add_trait(cr,TRAIT_OBJECT,OBJECT_AI_PACKET,ai) +#define set_self_ai(ai) set_ai(self_obj,ai) + +// visibility #define self_visible obj_is_visible_flag(self_obj) +#define set_obj_invisible(cr) set_obj_visibility(cr,true) +#define set_obj_visible(cr) set_obj_visibility(cr,false) +#define set_self_invisible set_obj_visibility(self_obj,true) +#define set_self_visible set_obj_visibility(self_obj,false) +#define is_visible(cr) has_trait(TRAIT_OBJECT,cr,OBJECT_VISIBILITY) // aka obj_is_visible_flag(x) #define self_cur_hits (get_critter_stat(self_obj,STAT_current_hp)) #define self_max_hits (get_critter_stat(self_obj,STAT_max_hp)) diff --git a/artifacts/scripting/headers/lib.arrays.h b/artifacts/scripting/headers/lib.arrays.h index 75bd1594..07708e9b 100644 --- a/artifacts/scripting/headers/lib.arrays.h +++ b/artifacts/scripting/headers/lib.arrays.h @@ -35,6 +35,9 @@ procedure array_keys(variable array); // list of array values (useful for maps) procedure array_values(variable array); +// makes given array permanent and returns it +procedure array_fixed(variable array); + // returns temp array containing a subarray starting from $index with $count elements // negative $index means index from the end of array // negative $count means leave this many elements from the end of array @@ -91,6 +94,11 @@ procedure get_empty_array_index(variable array); procedure add_array_set(variable array, variable item); procedure remove_array_set(variable array, variable item); +// Creates a new array filled from a given array by transforming each value using given procedure name. +procedure array_transform(variable arr, variable valueFunc); + +// Create a new temp array filled from a given array by transforming each key and value using given procedure name. +procedure array_transform_kv(variable arr, variable keyFunc, variable valueFunc); @@ -223,6 +231,11 @@ procedure array_values(variable array) begin return tmp; end +procedure array_fixed(variable array) begin + fix_array(array); + return array; +end + procedure array_slice(variable array, variable index, variable count) begin variable tmp, i, n; n := len_array(array); @@ -401,14 +414,25 @@ procedure remove_array_set(variable array, variable item) begin end end -// Transform every value in array using given callback -procedure array_map_func(variable arr, variable callback) begin - variable k, v; - foreach (k: v in arr) - arr[k] := callback(v); - return arr; +// Creates a new array filled from a given array by transforming each value using given procedure name. +procedure array_transform(variable arr, variable valueFunc) begin + variable k, v, retArr := temp_array_map if array_is_map(arr) else temp_array(len_array(arr), 0); + foreach (k: v in arr) begin + retArr[k] := valueFunc(v); + end + return retArr; end +// Create a new temp array filled from a given array by transforming each key and value using given procedure name. +procedure array_transform_kv(variable arr, variable keyFunc, variable valueFunc) begin + variable k, v, retArr := temp_array_map; + foreach (k: v in arr) begin + retArr[keyFunc(k)] := valueFunc(v); + end + return retArr; +end + + #define ARRAY_EMPTY_INDEX (-1) /** @@ -484,6 +508,7 @@ procedure array_append(variable arr1, variable arr2) begin return arr1; end +// Loads a "saved" array. If it doesn't exist, creates it (with a given size). procedure load_create_array(variable name, variable size) begin variable arr; arr := load_array(name); @@ -494,6 +519,7 @@ procedure load_create_array(variable name, variable size) begin return arr; end +// Creates and returns a new "saved" array. If array already existed with this name, frees it. procedure get_saved_array_new(variable name, variable size) begin variable arr; arr := load_array(name); @@ -584,30 +610,45 @@ end Different utility functions... */ -procedure debug_array_str(variable arr) begin - variable i := 0, k, s, len; +#define debug_array_str(arr) debug_array_str_deep(arr, 1) + +procedure debug_array_str_deep(variable arr, variable levels) begin +#define _newline if (levels > 1) then s += "\n"; +#define _indent string_repeat(" ", levels - 1) +#define _value(v) (v if (levels <= 1 or not array_exists(v)) else debug_array_str_deep(v, levels - 1)) + variable i := 0, k, v, s, len; len := len_array(arr); if (array_is_map(arr)) then begin // print assoc array s := "Map("+len+"): {"; while i < len do begin + _newline k := array_key(arr, i); - s += k + ": "+ get_array(arr, k); + v := get_array(arr, k); + s += _indent + k + ": " + _value(v); if i < (len - 1) then s += ", "; i++; end - if (strlen(s) > 254) then s := substr(s, 0, 251) + "..."; + //if (strlen(s) > 254) then s := substr(s, 0, 251) + "..."; + _newline s += "}"; end else begin // print list s := "List("+len+"): ["; + _newline while i < len do begin - s += arr[i]; + _newline + v := get_array(arr, i); + s += _indent + _value(v); if i < (len - 1) then s += ", "; i++; end - if (strlen(s) > 254) then s := substr(s, 0, 251) + "..."; + //if (strlen(s) > 254) then s := substr(s, 0, 251) + "..."; + _newline s += "]"; end return s; +#undef _newline +#undef _indent +#undef _value end procedure _PURGE_all_saved_arrays begin diff --git a/artifacts/scripting/headers/lib.inven.h b/artifacts/scripting/headers/lib.inven.h index 74429bcb..1c757a61 100644 --- a/artifacts/scripting/headers/lib.inven.h +++ b/artifacts/scripting/headers/lib.inven.h @@ -51,13 +51,15 @@ procedure remove_items_pid(variable invenObj, variable itemPid, variable quantit variable begin item; toRemoveQty; + actualQty; end if (not(invenObj)) then return; - toRemoveQty := obj_is_carrying_obj_pid(invenObj, itemPid); - if (quantity < toRemoveQty and quantity != -1) then begin - toRemoveQty := quantity; + actualQty := obj_is_carrying_obj_pid(invenObj, itemPid); + if (quantity > actualQty or quantity < 0) then begin + quantity := actualQty; end - while (toRemoveQty > 0) do begin + toRemoveQty := quantity; + while (quantity > 0) do begin item := obj_carrying_pid_obj(invenObj, itemPid); if (obj_type(invenObj) == OBJ_TYPE_CRITTER) then begin if (critter_inven_obj(invenObj, INVEN_TYPE_WORN) == item) then begin @@ -66,9 +68,10 @@ procedure remove_items_pid(variable invenObj, variable itemPid, variable quantit inven_unwield(invenObj); end end - toRemoveQty -= rm_mult_objs_from_inven(invenObj, item, toRemoveQty); + quantity -= rm_mult_objs_from_inven(invenObj, item, quantity); destroy_object(item); end + return toRemoveQty; end procedure remove_item_obj(variable invenObj, variable item) begin diff --git a/artifacts/scripting/headers/lib.math.h b/artifacts/scripting/headers/lib.math.h index a854741c..5101828a 100644 --- a/artifacts/scripting/headers/lib.math.h +++ b/artifacts/scripting/headers/lib.math.h @@ -5,13 +5,8 @@ Numbers... */ -#define above(a, b) (unsigned_comp(a, b) > 0) -#define above_equal(a, b) (unsigned_comp(a, b) >= 0) -#define below(a, b) (unsigned_comp(a, b) < 0) -#define below_equal(a, b) (unsigned_comp(a, b) <= 0) - // for sfall 4.2.3/3.8.23 -pure procedure unsigned_comp(variable a, variable b) begin +pure procedure unsigned_int_compare(variable a, variable b) begin if ((a bwxor b) == 0) then return 0; // a == b return 1 if ((b == 0) orElse a div b) else -1; end @@ -21,10 +16,10 @@ end #define math_max(x, y) (x if x > y else y) #define math_min(x, y) (x if x < y else y) -#define in_range(x, from, to) (x >= from and x <= to) +#define math_in_range(x, from, to) (x >= from and x <= to) -procedure get_clamped(variable val, variable a, variable b) begin +procedure math_clamp(variable val, variable a, variable b) begin variable min, max; if (a < b) then begin min := a; diff --git a/artifacts/scripting/headers/lib.strings.h b/artifacts/scripting/headers/lib.strings.h index 942bb3b0..9a798e81 100644 --- a/artifacts/scripting/headers/lib.strings.h +++ b/artifacts/scripting/headers/lib.strings.h @@ -13,17 +13,10 @@ #define is_in_string(str, sub) (string_pos(str, sub) != -1) #define string_starts_with(str, sub) (substr(str, 0, strlen(sub)) == sub) - -/** - * Returns position of first occurance of substr in str, or -1 if not found - */ -procedure string_pos(variable str, variable subst) begin - variable lst, n; - lst := string_split(str, subst); - if (len_array(lst) < 2) then - return -1; - return strlen(lst[0]); -end +#define string_format_array(fmt, arr) sprintf_array(fmt, arr) +// Replaces all occurances of substring in a string with another substring +#define string_replace(str, search, replace) (string_join(string_split(str, search), replace)) +#define sprintf2(fmt, arg1, arg2) string_format2(fmt, arg1, arg2) /** * Join array of strings using delimeter @@ -41,44 +34,6 @@ procedure string_join(variable array, variable join) begin return str; end -/** - * replaces all occurances of substring in a string with another substring - */ -#define string_replace(str, search, replace) (string_join(string_split(str, search), replace)) - -/** - * sprintf with two arguments - */ -procedure sprintf2(variable str, variable arg1, variable arg2) begin - variable split, len, i, j, arg; - split := string_split(str, "%"); - len := len_array(split); - str := ""; - if (len > 0) then begin - str := split[0]; - j := 0; - for (i := 1; i < len; i++) begin - if (split[i] == "") then begin - if (i < len - 1) then begin - str += "%" + split[i+1]; - i++; - end - end else begin - if (j == 0) then - arg := arg1; - else if (j == 1) then - arg := arg2; - else - arg := 0; - - str += sprintf("%" + split[i], arg); - j++; - end - end - end - return str; -end - /** * sprintf with unlimited number of arguments */ @@ -151,18 +106,6 @@ procedure string_repeat(variable str, variable count) begin return out; end -/** - * Workaround for string_split bug in sfall 3.3 - * DEPRECATED as of sfall 3.4 (bug fixed) - */ -procedure string_split_safe(variable str, variable split) begin - variable lst; - str += split; - lst := string_split(str, split); - resize_array(lst, len_array(lst) - 1); - return lst; -end - /** * The same as sfall string_split, but returns array of integers instead * Useful in cunjunction with is_in_array() @@ -186,8 +129,25 @@ procedure string_split_ints(variable str, variable split) begin return result; end +// atoi proc wrapper, for use as callback +procedure string_to_int(variable str) begin + return atoi(str); +end + +// atof proc wrapper, for use as callback +procedure string_to_float(variable str) begin + return atof(str); +end + +// converts any value to a string, for use as callback +procedure to_string(variable val) begin + return ""+val; +end + /** + DEPRECATED, use string_format instead! + String parse functions. Idea taken from KLIMaka on TeamX forums. Placeholders in format %d% are replaced from string. d refers to variable index (starting from 1). You can repeat one placeholder multiple times, or use placeholders in any order. diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 80f76316..037c2e95 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -149,7 +149,7 @@ // sort map in descending order by key value #define sort_map_reverse(array) resize_array(array, -7) // remove element from map or just replace value with 0 for list -#define unset_array(array, item) set_array(array, item, 0) +#define unset_array(array, key) set_array(array, key, 0) // same as "key_pressed" but checks VK codes instead of DX codes #define key_pressed_vk(key) (key_pressed(key bwor 0x80000000)) @@ -282,6 +282,8 @@ /* SFALL_FUNCX MACROS */ +#define FUNC_SELECTOR_7(_1,_2,_3,_4,_5,_6,_7,FUNC,...) FUNC + #define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name) #define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam) #define add_iface_tag sfall_func0("add_iface_tag") @@ -387,6 +389,8 @@ #define spatial_radius(obj) sfall_func1("spatial_radius", obj) #define string_compare(str1, str2) sfall_func2("string_compare", str1, str2) #define string_compare_locale(str1, str2, codePage) sfall_func3("string_compare", str1, str2, codePage) +#define string_pos(haystack, needle) sfall_func2("string_pos", haystack, needle) +#define string_pos_from(haystack, needle, pos) sfall_func3("string_pos", haystack, needle, pos) #define string_format1(format, a1) sfall_func2("string_format", format, a1) #define string_format2(format, a1, a2) sfall_func3("string_format", format, a1, a2) #define string_format3(format, a1, a2, a3) sfall_func4("string_format", format, a1, a2, a3) @@ -394,8 +398,7 @@ #define string_format5(format, a1, a2, a3, a4, a5) sfall_func6("string_format", format, a1, a2, a3, a4, a5) #define string_format6(format, a1, a2, a3, a4, a5, a6) sfall_func7("string_format", format, a1, a2, a3, a4, a5, a6) #define string_format7(format, a1, a2, a3, a4, a5, a6, a7) sfall_func8("string_format", format, a1, a2, a3, a4, a5, a6, a7) -#define string_format_MACRO(_0,_1,_2,_3,_4,_5,_6,FUNC,...) FUNC -#define string_format(...) string_format_MACRO(__VA_ARGS__, string_format6, string_format5, string_format4, string_format3, string_format2, string_format1)(__VA_ARGS__) +#define string_format(format, ...) FUNC_SELECTOR_7(__VA_ARGS__,string_format7,string_format6,string_format5,string_format4,string_format3,string_format2,string_format1)(format, __VA_ARGS__) #define string_tolower(text) sfall_func2("string_to_case", text, 0) #define string_toupper(text) sfall_func2("string_to_case", text, 1) #define tile_by_position(x, y) sfall_func2("tile_by_position", x, y) diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index 1dfd1413..7b95031f 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -327,11 +327,11 @@ FUNCTION REFERENCE - You can use this to search for a substring in a string like this: `strlen(get_array(string_split(haystack, needle), 0))` ----- -##### `string substr(string text, start, length)` +##### `string substr(string text, int start, int length)` - Cuts a substring from a string starting at `start` up to `length` characters. The first character position is 0 (zero). -- If `start` is negative - it indicates starting position from the end of the string (for example, `substr("test", -2, 2)` will return last 2 charactes: "st"). +- If `start` is negative - it indicates a position starting from the end of the string (for example, `substr("test", -2, 2)` will return last 2 charactes: "st"). - If `length` is negative - it means so many characters will be omitted from the end of string (example: `substr("test", 0, -2)` will return string without last 2 characters: "te"). -- If `length` is zero - it will return a string from the starting position to the end of the string (new behavior for sfall 4.2.2/3.8.22). +- If `length` is zero - it will return a string from the starting position to the end of the string (**new behavior** since sfall 4.2.2/3.8.22). ----- ##### `int strlen(string text)` @@ -1084,6 +1084,7 @@ sfall_funcX metarule functions - Allows changing "bonus move" points (yellow lights on the interface bar) that can only be used for movement, not attacking - Can be called from `HOOK_COMBATTURN` at the start of the turn (will not work on `dude_obj`) - Can be called from `HOOK_STDPROCEDURE` with `combat_proc` event (will work on both NPCs and `dude_obj`) + ---- #### get_ini_config `array sfall_func2("get_ini_config", string file, bool searchDB)` @@ -1093,6 +1094,16 @@ sfall_funcX metarule functions True - searches the file in database (DAT) files. If not found, then it will try the regular file system - Subsequent calls for the same file will return the same array, unless it was disposed using `free_array` +---- +#### string_pos +`int sfall_func2("string_pos", string haystack, string needle)` +`int sfall_func3("string_pos", string haystack, string needle, int pos)` + +- Returns the position of the first occurrence of a `needle` string in a `haystack` string, or -1 if not found. The first character position is 0 (zero) + +**Optional argument:** +- `pos`: the position at which to start the search. If negative, it indicates a position starting from the end of the string + **** _See other documentation files (arrays.md, hookscripts.md) for related functions reference._ diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index d1ac113b..c34d2354 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -680,12 +680,16 @@ static void __declspec(naked) combat_ai_hook_target() { } void PartyControl::OrderAttackPatch() { + static bool orderAttackPatch = false; + if (orderAttackPatch) return; + MakeCall(0x44C4A7, gmouse_handle_event_hack, 2); HookCall(0x44C75F, gmouse_handle_event_hook); HookCall(0x44C69A, gmouse_handle_event_hook_restore); MakeCall(0x44B830, gmouse_bk_process_hack); HookCall(0x42B235, combat_ai_hook_target); + orderAttackPatch = true; } static void NpcAutoLevelPatch() { diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 6f7051c2..95e5a3f4 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -153,6 +153,7 @@ static const SfallMetarule metarules[] = { {"show_window", mf_show_window, 0, 1, -1, {ARG_STRING}}, {"spatial_radius", mf_spatial_radius, 1, 1, 0, {ARG_OBJECT}}, {"string_compare", mf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}}, + {"string_pos", mf_string_pos, 2, 3, -1, {ARG_STRING, ARG_STRING, ARG_INT}}, {"string_format", mf_string_format, 2, 8, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}}, {"string_to_case", mf_string_to_case, 2, 2, -1, {ARG_STRING, ARG_INT}}, {"tile_by_position", mf_tile_by_position, 2, 2, -1, {ARG_INT, ARG_INT}}, diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index f34f77ee..a4439257 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -198,6 +198,25 @@ void mf_string_compare(OpcodeContext& ctx) { } } +void mf_string_pos(OpcodeContext& ctx) { + const char* const haystack = ctx.arg(0).strValue(); + int pos = 0; + if (ctx.numArgs() > 2) { + int len = strlen(haystack); + pos = ctx.arg(2).intValue(); + if (pos >= len) { + ctx.setReturn(-1); + return; + } else if (pos < 0) { + pos += len; + } + } + const char* needle = strstr(haystack + pos, ctx.arg(1).strValue()); + ctx.setReturn( + needle != nullptr ? (int)(needle - haystack) : -1 + ); +} + // A safer version of sprintf for using in user scripts. static const char* sprintf_lite(OpcodeContext& ctx, const char* opcodeName) { const char* format = ctx.arg(0).strValue(); diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index 305d9313..f78ba2db 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -37,6 +37,8 @@ void op_strlen(OpcodeContext&); void mf_string_compare(OpcodeContext&); +void mf_string_pos(OpcodeContext&); + void op_sprintf(OpcodeContext&); void mf_string_format(OpcodeContext&); diff --git a/sfall/Modules/Scripting/ScriptValue.cpp b/sfall/Modules/Scripting/ScriptValue.cpp index 07a61bbb..947c8df9 100644 --- a/sfall/Modules/Scripting/ScriptValue.cpp +++ b/sfall/Modules/Scripting/ScriptValue.cpp @@ -135,6 +135,10 @@ unsigned long ScriptValue::rawValue() const { return _val.dw; } +long ScriptValue::intValue() const { + return _val.i; +} + const char* ScriptValue::strValue() const { return _val.str; } diff --git a/sfall/Modules/Scripting/ScriptValue.h b/sfall/Modules/Scripting/ScriptValue.h index efa906f6..51f4b6e4 100644 --- a/sfall/Modules/Scripting/ScriptValue.h +++ b/sfall/Modules/Scripting/ScriptValue.h @@ -57,6 +57,8 @@ public: unsigned long rawValue() const; + long intValue() const; + float floatValue() const; const char* strValue() const;