\ExplSyntaxOn % Useful regex strings \regex_const:Nn \c__pos_int_regex { \d+ } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Dice macro %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Matches { # | #d# | #d# + # | #d# - # } while ignoring spaces \regex_const:Nn \c__valid_dice_regex { \A\s*(\d+)(?:[dD](\d+)\s*(?:([+\-])\s*(\d+))?)?\s*\Z } \seq_new:N \l__dice_args_seq \tl_new:N \l__dice_number_tl \tl_new:N \l__dice_sides_tl \tl_new:N \l__dice_mod_sign_tl \tl_new:N \l__dice_mod_tl \msg_new:nnn { rpg } { dice / invalid_argument } { Invalid~argument~``#1''~passed~to~\noexpand\RpgDice. } \NewDocumentCommand { \RpgDice } { m } { \group_begin: \regex_extract_once:NnNTF { \c__valid_dice_regex } { #1 } \l__dice_args_seq { \seq_pop_left:NN \l__dice_args_seq \l_tmpa_str % Don't need the full match \seq_pop_left:NN \l__dice_args_seq \l__dice_number_tl % # of dice \seq_pop_left:NN \l__dice_args_seq \l__dice_sides_tl % # of sides \seq_pop_left:NN \l__dice_args_seq \l__dice_mod_sign_tl % +/- for add/sub \seq_pop_left:NN \l__dice_args_seq \l__dice_mod_tl % modifier \str_if_empty:NTF { \l__dice_sides_tl } % are there dice ? { \l__dice_number_tl } % No, just a number { %Yes, break down the roll \fp_eval:n { floor ( \l__dice_number_tl * ( \l__dice_sides_tl + 1 ) / 2 )\l__dice_mod_sign_tl \l__dice_mod_tl }~ ( \l__dice_number_tl \dname \l__dice_sides_tl \str_case_e:nn { \l__dice_mod_sign_tl } { { + } { ~+~\l__dice_mod_tl } { - } { ~--~\l__dice_mod_tl } } ) } } { \msg_error:nnn { rpg } { dice / invalid_argument } { #1 }} \group_end: } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Gets the width of a space %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \box_new:N \l__rpg_space_box \hbox_set:Nn \l__rpg_space_box {~} \dim_new:N \l__rpg_space_dim \dim_set:Nn \l__rpg_space_dim { \box_wd:N \l__rpg_space_box } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some keys are required for proper execution of macros. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \msg_new:nnn { rpg } { key_required } { #1~requires~key~``#2''~to~be~set. } % #1 - key value % #2 - macro name % #3 - key name \cs_new_protected:Npn \__rpg_check_for_key:Nnn #1#2#3 { \group_begin: \str_set:Nx \l_tmpa_str { #1 } \str_if_empty:NT { \l_tmpa_str } { \msg_error:nnnn { rpg } { key_required } { #2 } { #3 } } \group_end: }