Closed MnFeN closed 8 months ago
Thanks for the PR! Sorry for the delay; I've been pretty busy and there's a lot to go over here, but I'll get to reviewing it.
Thanks for the PR! Sorry for the delay; I've been pretty busy and there's a lot to go over here, but I'll get to reviewing it.
There are still some changes I want to combine with it and it will take some time. I will send another commit so maybe it's better to review it later. Thank you for your work!
I've been reviewing it but it's so many updates at once, so it'll still take some time!
I've been reviewing it but it's so many updates at once, so it'll still take some time!
Thank you for taking the time and effort to review this! I understand there are a lot of edited codes, so please feel free to reply if you have any questions or concerns. I'm happy to provide further explanations or clarifications.
I also plan to push a final small update soon, which is primarily aimed at adding more built-in help text for each variable operation in the ActionForm, since some of them are too complicated to describe with only a line of instruction in the combobox. Aside from that, there will be some minor UI tweaks and bug fixes such as in the Triggernometry Discord suggestions.
Here is the summary of the latest edit:
ActionViewer
ActionViewer
sometimes are not enabled/disabled correctly;dgvActions
without actually selecting an action;OrderNumber
. Also changed shallow copy to deep copy when saving the actions, allowing to undo modifications of individual actions;ConditionViewer
ExpressionTextBox
32767
to 1000000
for scripting usage;vlookup()
;MaximumSize
and adjusted the height;ActionForm
SendKeys
;KeyPress
action test: the text box says it would delay two seconds before execution, but actually there were no code for this delay;SendKeys
action.LogForm
Select All
is automatically checked;StateForm
BridgeFFXIV
NullCombatant
was defined but never initialized;ClearCombatant
to empty strings to avoid confusion, like NullCombatant.x
being 0
might be mistaken for querying an entity with x = 0
;Jobs
to the entity dictionary;TranslateJob
, TranslateRole
functions with a dictionary defined in Entity.cs
;Action
KeyPress
, SendWindowMessage
action descriptions did not support procid
;GetEntityByName
/ GetEntityById
, table action GetAllEntities
, to obtain single or all entity information at once;Resize
can omit one of width/height to indicate that dimension remains unchanged;Context
_rowcl[...]
_colrl[...]
to return the cell value by its corresponding header (like tvarrl
tvarcl
);ecallback:...
to check for the existence of a named callback;contain
and ifcontain
methods in the dictionary variable code block;func:pick():string
to split string based on the same logic as splitting parameters, instead of simply splitting by comma.Interpreter
AggregateException
and generates corresponding error messages;SetDictVariable
and GetDictVariable
;SetXXXVariable
, deletes the variable if the provided variable parameter is given as null
;MathParser
??
: If the previous parameter cannot be parsed as a number, then returns the next parameter, similar to the null coalescing operator.1 ?? 2 = 1
, A ?? 2 = 2
;roundir
/ roundvec
functions;0xFF
, 0b11
, 0o77
;${...}
expression returns an empty string causing ==
, ??
to be at the start or end of tokens listRealPlugin
StreamWriter.write
;.previous
backup file will be automatically loaded.ReadyForOperation
function;RegisterNamedCallback
and UnregisterNamedCallback
functions for adding/removing callbacks by name in scripts.I went through it and it looks good honestly, just need a lot of testing :D
View in Chinese:
MathParser
The core of the MathParser had been mainly rewritten:
Parsing Minus Signs:
Several bugs in the parsing logic for minus signs have been fixed:
+
/-
was a sign or an operator. This oversight caused incorrect parsing of expressions likefunc(1, -1)
or1 - -1
.BasicArithmeticalExpression()
, only positive values were handled when applying a minus sign, neglecting non-positive values. This led to incorrect parsing of expressions like-(-1)
into-1
.-2^2 = 4
(whereas the answer should be -4 in most modern languages).-
in-func(args)
as a minus sign, causing errors in expressions like0 = -sin(0)
. The code would attempt to apply a minus operation between=
andsin(0)
.These bugs have been fixed, and the entire logic for handling +/- signs has been rewritten for simplicity. Previously, special logic was used to treat
+
/-
in both the lexer and the parser. Now, the lexer tokenizes every+
/-
without discrimination, leaving simple logic in the parser to handle them correctly.Lexer Logic
*
between expressions like3abs(0)
, but this approach is not correct both in logic and in code:3ab
in3abs(0)
and the hex number3ab
is not possible without scanning the entire list of functions to match names;a
as the first character of the token to apply this logic, when there is a3
in front of it.*
between numbers and functions/constants is still required.Operators
&&
||
^^
!
: logical AND / OR / XOR / NOT%%
//
: realmod
and exact division√
: sqrt()>=
<=
!=
as aliases==
the only string operator: string equal to.DD5 == DD5
=1
.&
|
⊕
~
<<
>>
: bitwise operations (useful for representing the state of multiple entities appearing in random locations using a single variable)? :
: ternary operator√
!
~
^
%
%%
/
//
*
-
+
<<
>>
>
≥
>=
<
≤
<=
==
=
≠
!=
&
⊕
|
&&
^^
||
?
:
(
)
,
(not parsed directly)Precision Error Tolerance
double.Epsilon
to a constant (set toδ
=1E-9
), making it more suitable for Triggernometry use.=
>
>=
sign
truncate
, etc.x ± δ
will be considered equal tox
during comparisons.Aliases
atan2
=>arctan2
) existed but was not utilized.Spaces
Arguments
SplitArgument
function generated incorrectargs
lists when encountering unmatched quotes or consecutive commas, and it also did not support line breaks.1
2
3
4
5
'
,
(empty)
.Arguments should not contain
)
{
}
due to the parsing logic in expanding expressions, so the following escape rules are applied:{
should be escaped with full-width{
or__LB__
;}
should be escaped with full-width}
or__RB__
;(
can be escaped with full-width(
or__LP__
;)
should be escaped with full-width)
or__RP__
;{
}
should be escaped with__FLB__
__FRB__
;(
)
should be escaped with__FLP__
__FRP__
;Indices and Slices
Negative Indices
substring
;lvar
;tvar
;tvarrl
;tvarcl
; and theindex
/row
/column
text boxes in list/table actions.${tvar:myTable[2][-3]}
returns the value in the2nd
column and the-3rd
row.Slices
start:end:step
can represent a series of indices starting fromstart
, incrementing bystep
each time, and ending beforeend
.a:b:c
a:b:
a::c
:b:c
a::
:b:
::c
::
a:b
a:
:b
:
":5, 7, 8:13:2, 16:"
represents(0), 1, 2, 3, 4, 7, 8, 10, 12, 16, 17, (...to the end)
.""
or''
.Autofill
tvar:
and still showingtvarrl
;{
to provide smarter autofill suggestions; (e.g.,${lvar:xxx${var:yyy}${index}.
will display list properties);Dictionary Variables
Expressions and Functions
Special Variables:
_ETprecise
A more accurate version of
_ffxivtime
(_ET
)._idx
_col
_row
_col[i]
i
in the current column._row[i]
i
in the current row._this
_key
_val
_clipboard
_config[x]
For details on dynamic expressions, refer to the actions section.
Numeric Constants:
semitone
2^(1/12)
.The frequency ratio between 2 adjacent semitones.
cent
2^(1/1200)
.The frequency ratio between 2 adjacent cents.
ETmin2sec
35/12
.The ratio between 1 ET minute and 1 real second.
δ
1E-9
.The math tolerance for comparison. (check the previous part)
Numeric Functions:
distance()
Behaves the same as previous versions when n = 2.
distance(0,0,0, 3,4,12)
=13
projectdistance()
projectheight()
e.g. Useful for calculations involving linear AoE.
projd(0,0, pi/6, -2,0)
=-1
projh(0,0, pi/6, -2,0)
=1.732...
angle(x1, y1, x2, y2)
atan2(x2-x1, y2-y1)
angle(100, 100, 120, 100)
=1.57...
relangle(θ1, θ2)
θ1
as relative north, it returns the direction ofθ2
. Normalized to[-π, π)
.relangle(0, -pi/2)
=1.57...
roundir(θ, ±n, digits = 0)
roundvec(x, y, ±n, digits = 0)
roundir
; dx/dy offsets forroundvec
) to a direction in a circle divided into\|n\|
segments, then returns the index of that direction.The sign of
n
indicates two division modes: north as a segment point or as a boundary between two segments, as shown below.digits
specifies the number of decimal places for rounding; a negative value means no rounding.e.g. Useful for calculating the direction of an entity with multiple potential spawn points. Could be combined with
func:pick(index)
to easily output any direction as a string from radians or coordinates without complexarctan2
andmod
calculations.roundir(-1.57,4)
=
1
(West)roundvec(8,-6,-4)
=
3
(NE)Numeric String Functions:
parsedmg(hex)
0x15
/0x16
ACT log lines to the corresponding decimal value.Rule: Padleft the hex string with
0
s to 8 digits asXXXXYYZZ
, then convertZZXXXX
to decimal.parsedmg(A00000)
=160
freq(note, semitones = 0)
#
,b
,x
) adjusted by the semitones offset.freq(A4)
=
freq(G#4, 1)
=
freq(Bb4, -1)
=
freq(A5, -12)
=
440
nextETms(XX:XX)
nextETms(ETminutes)
1:00
:nextETms(2:00)
=
nextETms(02:00.00)
=
nextETms(120)
=
175000
(2 min 55 s)String Functions:
parsedmg
func:parsedmg:A00000
=
160
slice(slices = ":")
Returns the specified slice of the string.
func:slice(-3):01234
=2
func:slice(1:4):01234
=123
func:slice(::-1):01234
=43210
func:slice("1,3:"):01234
=134
pick(index, separator = ",")
Returns a substring based on the index, starting from 0.
Also supports negative indices.
func:pick(3):north,west,south,east
=
east
func:pick(-1,", "):1, 22, 3, 44, 5
=
5
contain(str)
startwith(str)
endwith(str)
equal(str)
1
or0
.func:contain(23):1234
=1
func:endwith(23):1234
=0
func:equal(23):1234
=0
ifcontain(str, t, f)
ifstartwith(str, t, f)
ifendwith(str, t, f)
ifequal(str, t, f)
if()
.func:ifcontain(23, a, b):1234
=a
func:ifendwith(23, a, b):1234
=b
func:ifequal(23, a, b):1234
=b
indicesof(str, joiner = ",", slices = ":")
func:indicesof(a):abcbabcba
=0,4,8
func:indicesof(a, "-", ::-1):abcbabcba
=8-4-0
match(str):regex
1
or0
.Note:
regex
should not contain{
}
.{
should be escaped with full-width{
or__LB__
;}
should be escaped with full-width}
or__RB__
;{
}
must be escaped with__FLB__
__FRB__
.Same regex rules apply to the next two functions.
func:match(404D):404[B-D]
=1
func:match(4000A3BF):4.{7}
=1
capture(str, group):regex
$groupindex
or${groupname}
. Ifgroupindex
=0
, it returns the entire matched string.If
groupname
isn't found orgroupindex
is out of range, it returns an empty string.Adheres to the previously mentioned regex rules.
func:capture(Player NameGilgamesh, server):.+ .+(?<server>[A-Z].+)
=
Gilgamesh
ifmatch(str, t, f):regex
if()
.func:ifmatch(404D, a, b):404[B-D]
=a
replace(oldStr, newStr = "", isLooped = false)
func:replace(" "):1 2 3
=
123
func:replace(aa,a):aaaaaa
=
aaa
func:replace(aa,a,true):aaaaaa
=
a
repeat(times, joiner = "")
func:repeat(3):a
=aaa
func:repeat(3, +):1
=1+1+1
padleft
padright
trim
trimleft
trimright
0
-9
are interpreted as characters rather than charcodes since ASCII 0-9 control characters are rarely used here.Numbers ≥ 10 are seen as charcodes.
No need for the 5-digit charcodes of CJK-region characters (including full-width spaces).
func:trim(48, 2, a):abcd0320
=
bcd03
func:padleft(0,8):1ABCD
=
0001ABCD
List Variables:
lvar:test
=1, 2, 3, 4, 5, 6, 7, 8, 9
(this string is only a representation of the list) for demonstration.${?lvar:...}
,
, and uses any properties on it to return a string.Uses the same splitting rule as for splitting arguments.
Building a variable might be slightly slower, but it provides a way to combine multiple actions with conditions into a single action.
${?lvar:a, b, c, d, e.indexof(c)}
=3
${?lvar:a, b, "c,c", d, e[3]}
=c,c
sum(slices = ":")
Only values that can be parsed into the
double
format are summed.${lvar:test.sum}
=45
${lvar:test.sum(1:5)}
=10
count(str, slices = ":")
${lvar:test.count(3)}
=1
${lvar:test.count(a)}
=0
join(joiner = ",", slices = ":")
${lvar:test.join}
=1,2,3,4,5,6,7,8,9
${lvar:test.join(" ",5::-1)}
=
5 4 3 2 1
randjoin(joiner = ",", slices = ":")
${lvar:test.randjoin}
=
4,9,2,3,5,7,8,1,6
(random example)
contain(str, slices = ":")
...contain(3)
=1
...contain(3, 4:)
=0
ifcontain(str, trueExpe, falseExpr)
if()
. If the list contains the string, returnstrueExpr
; otherwise, returnsfalseExpr
....ifcontain(3, found, missing)
=found
...ifcontain(a, found, missing)
=missing
indicesof(str, joiner = ",", slices = ":")
max(type = "n", slices = ":")
min(type = "n", slices = ":")
n
for numeric,h
for hex,s
for string.${lvar:test.max}
=9
...min(n, 3:)
=3
Table Variables:
tvar:test
:${?tvar:...}
,
and\|
.Similar to
${?lvar:}
.${?tvar: a,b | c,d [2][2]}
=d
tvardl:
ptvardl:
tvarrl:
/tvarcl:
.Returns the value identified by the column and row headers.
${tvardl:test[41][13]}
=43
sum(colSlices = ":", rowSlices = ":")
Only values that can be parsed into the
double
format are summed.${lvar:test.sum}
=440
...sum(1, :)
=
11 + 12 + 13 + 14
=
50
count(str, colSlices = ":", rowSlices = ":")
${lvar:test.count(33)}
=1
${lvar:test.count(1)}
=0
hjoin(joiner1 = ",", joiner2 = "⏎", colSlices = ":", rowSlices = ":")
...hjoin(",", ",", 1:3, 3:)
=
13,23,14,24
${tvar:test.hjoin}
=11,21,31,41
12,22,32,42
13,23,33,43
14,24,34,44
vjoin(joiner1 = ",", joiner2 = "⏎", colSlices = ":", rowSlices = ":")
${tvar:test.vjoin}
=11,12,13,14
21,22,23,24
31,32,33,34
41,42,43,44
hlookup(str, rowIndex, colSlices = ":")
If not found, returns 0.
...hlookup(13,3)
=1
...hlookup(13,3,2:)
=0
vlookup(str, colIndex, rowSlices = ":")
If not found, returns 0.
...vlookup(13,1)
=3
max(type = "n", colSlices = ":", rowSlices = ":")
min(type = "n", colSlices = ":", rowSlices = ":")
Dict Variables:
dvar:test
=a=1, b=2, c=3, d=3, e=3
(this string is only a representation of the dictionary) to demonstrate:${?dvar:...}
=
,.
.Similar to
${?lvar:}
.${?dvar: 7CD2=in, 7CD6=out, 7CD7=spread [7CD2] }
=in
sumkeys()
sum()
double
format.${dvar:test.sumkey}
=0
${dvar:test.sum}
=12
count(value)
...countvalue(3)
=3
dvar:
edvar:
pdvar:
epdvar:
${epdvar:dictname}
${dvar:test[e]}
=3
length
/size
${dvar:test.size}
=5
ekey(key)
evalue(value)
${dvar:test.ekey(a)}
=1
${dvar:test.evalue(4)}
=0
ifekey(key, t, f)
ifevalue(value, t, f)
...ifekey(a, found, missing)
=found
keyof(value)
${dvar:test.keyof(1)}
=a
${dvar:test.keyof(4)}
= ``keysof(value, joiner = ",")
...keyof(3)
=c,d,e
joinkeys(joiner = ",")
joinvalues(joiner = ",")
joinall(kvjoiner = "=", pairjoiner = ",")
...joinkeys(-)
=a-b-c-d-e
...joinall
=a=1,b=2,c=3,d=3,e=3
max(type = "n")
min(type = "n")
maxkey(type = "n")
minkey(type = "n")
Job Properties:
${_job[XXX].prop}
: returns the property of the specified job.jobXX
,jobXXn
,jobid
could be used as the keyXXX
in${_job[XXX].prop}
._ffxiventity
and_ffxivparty
.${_job[Gladiator].jobid}
=1
;${_job[1].jobFR}
=Gladiateur
;${_job[GLA].jobCN1}
=剑
;${_ffxiventity[Gladiator Player].isTM}
=1
Entity Properties:
${_ffxiventity}
and${_ffxivparty}
:bnpcid
,bnpcnameid
,ownerid
,type
,partytype
,address
castid
,casttime
,maxcasttime
,iscasting
Abbreviations to Enhance User Experience:
${}
instances.${numeric:...}
${n:...}
${string:...}
${s:...}
${func:...}
${f:...}
${exvar:...}
${ev:}
${el:}
${et:}
${ed:}
${(p)var:...}
${(p)lvar:...}
${(p)tvar:...}
${(p)dvar:...}
${(p)v:}
${(p)l:}
${(p)t:}
${(p)d:}
${?lvar:...}
${?tvar:...}
${?dvar:...}
${?l:}
${?t:}
${?d:}
${_loopiterator}
${_i}
${_ffxiventity[...].prop}
${_entity[...].prop}
${_ffxivparty[...].prop}
${_party[...].prop}
${_ffxivplayer}
${_me}
${_ffxiventity[${_ffxivplayer}].prop}
${_me.prop}
indexof()
i()
_ffxivtime
_ET
pi
π
distance()
d()
projectdistance()
projd()
projectheight()
projh()
angle()
θ()
relangle()
relθ()
.width
.w
.height
.h
.hlookup()
.hl()
.vlookup()
.vl()
.heading
.h
Actions
Fixed a bug in the list method
Insert
and table variableResize
:null
directly as placeholders when the given index exceeded the length of the list. It should have used a newVariableScalar
instead.null
:vtr.Values.AddRange(new Variable[newWidth]);
Rows[i].Values.AddRange(new Variable[newWidth - Rows[i].Values.Count]);
Fixed a bug in the list method
Set
:VariableScalar
into the list as placeholders when the list needs to be expanded.index
in a list with lengthindex - 1
. The result was an appended list that lacked the value to be set.Fixed a bug in the list method
Split
:Fixed a bug about the persistent button:
Add dynamic expression support for the
Set
action of lists, tables, and dictionaries:_this
_idx
_this
_row
col
_row[i]
col[i]
_val
Updated
PopFirst
/PopLast
actions for list variables:PopFirst
was modified to accept an optionalindex
argument, which also supports negative values.PopFirst
in the code remained unchanged, andPopLast
was retained for XML compatibility reasons.PopLast
now redirects toPopFirst
with index =-1
.Added
PopToList
actions (set/insert):-1
, which inserts the value between the last 2 elements1,2,3,4,5
, source index:3
, target list:a,b,c,d,e
3
=>a,b,3,c,d,e
;-1
=>a,b,c,d,3,e
;` =>
a,b,c,d,e,3`;3
=>a,b,3,d,e
;-1
=>a,b,c,d,3
;Pop
andSet
/Insert
.expression
textbox is used as the target index input, and the descriptions and label instructions would be changed automaticlly.Added basic actions for dict variables:
Unset
,UnsetAll
,UnsetRegex
Set
: Assigns a value to a key.Remove
: Removes a specified key if it exists.Merge
: Combines dictionaries but leaves repeated keys unaltered.MergeHard
: Combines dictionaries and overwrites any repeated keys.Added
Build
action for list/table/dict variables:list.join
,table.hjoin
, ortable.vjoin
.,1,2,3,4,5,6,7,8,9
and,|11,21,31,41|12,22,32,42|13,23,33,43|14,24,34,44
can build the previouslvar:test
andtvar:test
;=,a=1,b=2,c=3
can build the dictionarya=1, b=2, c=3
.Added
SetAll
action for list/table/dict variables:Select()
in LINQ expressions.${_this}
${_idx}
${_idx}
(when a dict length is specified) or${_key}
${_val}
${_this}
,${_row}
,${_col}
can be used.${_idx}
on a list (length = 9) produceslvar:test
with values (1-9);${_this}^2
onlvar:test
results in1, 4, 9, ..., 81
.5
, using key expression${_idx}
and value expression${_idx}^2
produces a dictionary1:1, 2:4, 3:9, 4:16, 5:25
.${_value}
and value expression${_key}
reverses it to1:1, 4:2, 9:3, 16:4, 25:5
.Added
Filter
action for list/table/dict variables:Where()
function in LINQ expressions.!= 0
).${func:ifequal("", 0, 1):${_this}}
eliminates empty elements;${lvar:listname.indexof(${_this})} = ${_idx}
removes duplicate elements in the list namedlistname
;${_ffxiventity[${_this}].distance} > 15 && ${func:ifequal(DPS):${_ffxiventity[${_this}].role}}
on a list containing player names filters out DPSs located more than 15 m away from the player.Added
SetLine
/InsertLine
action for table variables:Build
action for lists, it splits the expression into a list based on its first character. Depending on the provided index (row/col), the list of values is then set/inserted into the specified row/col. This counting - - logic is similar to that of theSet
/Insert
actions for list variables.3
and the expression,a,b,c,d,e
on the priorlvar:test
yields:Added
RemoveLine
action for table variables:3
from the previouslvar:test
results in:Added
SortByKeys
for lists, andSortLine
for tables:n+:key1, n-:key2, s+:key3, ...
n
/s
: numeric/string comparison+
/-
: ascending/descending (the+
is optional)key
: should include${_this}
/${_idx}
for lists,${_row}
or${_row[i]}
for row sorting,${_col}
or${_col[i]}
for column sorting."s+:key", ...
or's+:key', ...
. e.g.n+:${_this}
n-:${_this}
s+:${_this}
s-:${_this}
correspond to the previous four sorting actions.n-:${_idx}
reverses the list.[11, 12, 13, 21, 22, 23, 31, 32, 33]
with the functionsn-:${f:substring(0):${_this}}, n+:${_idx}%3
produces[33, 31, 32, 23, 21, 22, 13, 11, 12]
.Unset all types of variables matching the regex (in the scalar variable tabpage)
Copy the Value of the Variable/Expression to the Clipboard (in the Scalar Variable Tabpage)
${var:name}
in the expression (unless your clipboard contains${...}
expressions). I have organized it this way just to logically group this clipboard operation under the scalar variable category, avoiding the creation of a separate tabpage which could further slow down the action form loading.Introduced the folder action "Cancel All Actions of Triggers Within Folder"
Refined Actions List Order
Adjusted Tab Index in Action Form
Expression Textbox
Color
ExpressionType enum to let the textbox show the input as its background color;${...}
expression that is not a capture group or a special variable (like_since
).Trigger Form / Action Viewer
[Sync]
prefix to the description if the async option of an action is unchecked;Lavender
/230,230,250
/#e6e6fa
/#eef
)Move to top
andMove to bottom
, and enabled the moving of multiple selected actions;Undo
to enable the undo of movement / delete of actions for one step;Add action
now insert the action under the selected line instead of set it to the bottom;Save changes
button would change intoSave and Fire
if autofire is enabled;Log Form
custom
log types (info
<custom2
<custom
<warning
) which should contain no log from the program.warning
/error
log message colors to be less saturated instead of pure red/yellow, and also added a green color to thecustom
logs.Variable State Viewer / Editor
Fill
, which forbids the adjustment of its column width if it is not the last column.Main UI:
Add trigger / folder
buttons when a local trigger is selected, which adds the trigger / folder to the parent folder, just like pasting triggers from xml;Others
Bug Fixes:
Func Without Parameters: Fixed an issue where string functions without arguments weren't parsed correctly.
func:length:3*(1+2)
, which considerslength:3*
as the function name and1+2
as the argument.:
later.Hi-Res Action Checkboxes: Rectified a bug causing action checkboxes to remain hidden on Hi-Resolution screens.
MessageBox Display: Addressed an issue causing the MessageBox to sometimes hide behind active windows. Now, MessageBoxes will always appear above the currently active window.
Enhancements:
Linebreaks in Expressions:
⏎
was introduced to act as a placeholder for linebreaks during parsing, which is replaced post-parse.${func:repeat(5, ⏎):text}
.Translations:
Trigger Firing Settings:
Fire (Allow Conditions)
right-click menu option.Allow conditions for autofiring
setting for triggers.Test Action: Introduced a
Test action with live values (ignore conditions)
option and a corresponding default configuration setting to bypass conditions during tests.CSV Export: Enhanced support for table variables that contain commas and double quotes, providing more accurate exports, instead of simply joining together with
,
.Miscellaneous Adjustments:
CultureInfo.InvariantCulture
to some of theParse()
andToString()
functions that missed localization settings.Different Behaviours
Besides the bug fuxes and UI adjustments, the following behaviors are different comparing with the old version:
Mathparser Adjustments:
:
character is now part of the? :
ternary operator.^
operator is now right-associative.1E-9
(i.e.,0.1234567890 = 0.1234567899
is considered true).Input Validation: Some of the undefined or invalid inputs, which previously returned default values
` or
0`, now raise specific error messages.Beep Frequency: The default beep frequency is not out-of-tune anymore (C6, 1046.5 Hz).
Known Issues
${_idx}
aren't thread-safe and should be used exclusively with synchronous actions.