Browse Source

added a macro feature and support for nerd fonts

Gemini 3 tuần trước cách đây
mục cha
commit
7e70ef3547
2 tập tin đã thay đổi với 477 bổ sung29 xóa
  1. 42 3
      README.md
  2. 435 26
      roll_tor.sh

+ 42 - 3
README.md

@@ -11,6 +11,12 @@ A CLI dice roller for *The One Ring* RPG, enhanced with `gum` for animations.
   - **Magnificent Success** (6)
 - Cryptographically secure randomness using `/dev/urandom`
 - Terminal animations using `gum`
+- FontAwesome symbols for dice types ( for d12,  for d6)
+- **Macro System**: Save and recall complex dice configurations
+  - Named macros for quick access
+  - Support for Advantage/Disadvantage
+  - Bonus/debuff modifiers
+  - Configurable dice counts
 
 ## Prerequisites
 
@@ -23,12 +29,45 @@ A CLI dice roller for *The One Ring* RPG, enhanced with `gum` for animations.
 # Make executable
 chmod +x roll_tor.sh
 
-# Roll 1 Feat Die (default)
+# Roll 1 Feat Die (default) - shows  symbol
 ./roll_tor.sh
 
-# Roll 1 Feat Die + 3 Success Dice
+# Roll 1 Feat Die + 3 Success Dice - shows  symbols
 ./roll_tor.sh 1 3
 
-# Roll 2 Feat Dice
+# Roll 2 Feat Dice - shows  symbols
 ./roll_tor.sh 2 0
 ```
+
+### Macro System
+
+Save frequently used dice configurations for quick access:
+
+```bash
+# Save macros
+./roll_tor.sh --save-macro hero 1 3          # Basic macro
+./roll_tor.sh --save-macro attack 2 4 adv    # With advantage
+./roll_tor.sh --save-macro spell 1 2 disadv  # With disadvantage
+./roll_tor.sh --save-macro skill 1 2 +5      # With +5 bonus
+./roll_tor.sh --save-macro heroic 2 3 adv +5 # Complex macro
+
+# List all saved macros
+./roll_tor.sh --list-macros
+
+# Use a saved macro
+./roll_tor.sh hero
+
+# Delete a macro
+./roll_tor.sh --delete-macro hero
+
+# Get help
+./roll_tor.sh --macro-help
+```
+
+### Macro Features
+
+- **Named Macros**: Assign descriptive names to your dice configurations
+- **Advantage/Disadvantage**: Roll 2 sets and keep highest/lowest
+- **Modifiers**: Add bonuses (+) or debuffs (-) to the final result
+- **Persistent Storage**: Macros are saved to `~/.tor-roller-macros`
+- **Validation**: Input validation ensures valid dice counts and modifiers

+ 435 - 26
roll_tor.sh

@@ -3,10 +3,15 @@
 # Defaults
 FEAT_COUNT=1
 SUCCESS_COUNT=0
+ROLL_MODE=""  # adv, disadv, or empty
+BONUS_VALUE=0 # bonus/debuff value
+
+# Macro configuration
+MACRO_FILE="$HOME/.tor-roller-macros"
 
 # Help function
 show_help() {
-    echo "Usage: $0 [FEAT_DICE] [SUCCESS_DICE]"
+    echo "Usage: $0 [FEAT_DICE] [SUCCESS_DICE] [OPTIONS]"
     echo ""
     echo "Arguments:"
     echo "  FEAT_DICE     Number of 1d12 Feat Dice to roll (default: 1)"
@@ -14,21 +19,237 @@ show_help() {
     echo ""
     echo "Options:"
     echo "  -h, --help    Show this help message"
+    echo "  macro         Use a saved macro (see --macro-help)"
     echo ""
     echo "Examples:"
     echo "  $0          # Rolls 1 Feat Die"
     echo "  $0 1 3      # Rolls 1 Feat Die and 3 Success Dice"
     echo "  $0 2 0      # Rolls 2 Feat Dice"
+    echo ""
+    echo "Macro Examples:"
+    echo "  $0 --macro-help     # Show macro usage"
+    echo "  $0 --save-macro hero 1 3          # Save macro named 'hero' with 1d12+3d6"
+    echo "  $0 --save-macro attack 2 4 adv    # Save with advantage"
+    echo "  $0 --save-macro spell 1 2 disadv  # Save with disadvantage"
+    echo "  $0 --save-macro skill 1 2 +5      # Save with +5 bonus"
+    echo "  $0 --list-macros                  # List all saved macros"
+    echo "  $0 hero                           # Use saved macro"
+    exit 0
+}
+
+# Show macro help
+show_macro_help() {
+    echo "Macro Usage:"
+    echo ""
+    echo "Save Macros:"
+    echo "  --save-macro NAME FEAT_DICE SUCCESS_DICE [ADV|DISADV] [BONUS]"
+    echo "    NAME:         Name for the macro"
+    echo "    FEAT_DICE:    Number of d12 dice (1-99)"
+    echo "    SUCCESS_DICE: Number of d6 dice (0-99)"
+    echo "    ADV:          Advantage (roll 2 sets, keep highest)"
+    echo "    DISADV:       Disadvantage (roll 2 sets, drop highest)"
+    echo "    BONUS:        Bonus/debuff value (+5, -3, etc.)"
+    echo ""
+    echo "Examples:"
+    echo "  $0 --save-macro hero 1 3"
+    echo "  $0 --save-macro attack 2 4 adv"
+    echo "  $0 --save-macro spell 1 2 disadv"
+    echo "  $0 --save-macro skill 1 2 +5"
+    echo ""
+    echo "Use Macros:"
+    echo "  $0 MACRO_NAME"
+    echo ""
+    echo "List Macros:"
+    echo "  $0 --list-macros"
+    echo ""
+    echo "Delete Macros:"
+    echo "  $0 --delete-macro MACRO_NAME"
+    echo ""
     exit 0
 }
 
+# Save a macro
+save_macro() {
+    local name="$1"
+    local feat="$2"
+    local success="$3"
+    local mode="$4"
+    local bonus="$5"
+    
+    # Validate inputs
+    if [[ ! "$feat" =~ ^[0-9]+$ ]] || [ "$feat" -lt 1 ] || [ "$feat" -gt 99 ]; then
+        echo "Error: FEAT_DICE must be a number between 1 and 99"
+        exit 1
+    fi
+    
+    if [[ ! "$success" =~ ^[0-9]+$ ]] || [ "$success" -gt 99 ]; then
+        echo "Error: SUCCESS_DICE must be a number between 0 and 99"
+        exit 1
+    fi
+    
+    # Handle mode and bonus arguments
+    if [[ "$mode" == "adv" ]] || [[ "$mode" == "ADV" ]]; then
+        mode="adv"
+        bonus="$5"
+    elif [[ "$mode" == "disadv" ]] || [[ "$mode" == "DISADV" ]]; then
+        mode="disadv"
+        bonus="$5"
+    elif [[ "$mode" =~ ^[+-]?[0-9]+$ ]]; then
+        # This is a bonus, mode is empty
+        mode=""
+        bonus="$4"  # bonus is in the mode position
+    else
+        mode=""
+        bonus="$4"  # bonus is in the mode position if provided
+    fi
+    
+    # Validate bonus if provided
+    if [[ "$bonus" != "" ]] && [[ "$bonus" != "0" ]] && [[ ! "$bonus" =~ ^[+-]?[0-9]+$ ]]; then
+        echo "Error: BONUS must be a number with + or - (e.g., +5, -3)"
+        exit 1
+    fi
+    
+    # If bonus is empty or not a number, set to 0
+    if [[ "$bonus" == "" ]] || [[ ! "$bonus" =~ ^[+-]?[0-9]+$ ]]; then
+        bonus="0"
+    fi
+    
+    # Create macro entry
+    local entry="$name|$feat|$success|$mode|$bonus"
+    
+    # Create directory if it doesn't exist
+    mkdir -p "$(dirname "$MACRO_FILE")"
+    
+    # Remove existing macro with same name
+    if [ -f "$MACRO_FILE" ]; then
+        grep -v "^$name|" "$MACRO_FILE" > "${MACRO_FILE}.tmp" 2>/dev/null || true
+        mv "${MACRO_FILE}.tmp" "$MACRO_FILE"
+    fi
+    
+    # Add new macro
+    echo "$entry" >> "$MACRO_FILE"
+    echo "Macro '$name' saved successfully!"
+    echo "  Feat Dice: $feat d12, Success Dice: $success d6"
+    if [[ "$mode" == "adv" ]]; then
+        echo "  Mode: Advantage"
+    elif [[ "$mode" == "disadv" ]]; then
+        echo "  Mode: Disadvantage"
+    fi
+    if [[ "$bonus" != "0" ]]; then
+        echo "  Bonus: $bonus"
+    fi
+    exit 0
+}
+
+# List all macros
+list_macros() {
+    echo "Saved Macros:"
+    echo ""
+    if [ ! -f "$MACRO_FILE" ]; then
+        echo "No macros saved yet."
+        echo ""
+        echo "Use --save-macro to create a macro, or --macro-help for examples."
+        exit 0
+    fi
+    
+    printf "%-20s %-10s %-12s %-12s %s\n" "NAME" "FEAT_DICE" "SUCCESS_DICE" "MODE" "BONUS"
+    printf "%-20s %-10s %-12s %-12s %s\n" "----" "---------" "------------" "----" "-----"
+    
+    while IFS='|' read -r name feat success mode bonus; do
+        local mode_display="${mode:-(none)}"
+        local bonus_display="$bonus"
+        if [[ "$bonus" == "0" ]]; then
+            bonus_display="(none)"
+        fi
+        printf "%-20s %-10s %-12s %-12s %s\n" "$name" "${feat}d12" "${success}d6" "$mode_display" "$bonus_display"
+    done < "$MACRO_FILE"
+    exit 0
+}
+
+# Delete a macro
+delete_macro() {
+    local name="$1"
+    
+    if [ ! -f "$MACRO_FILE" ]; then
+        echo "No macros saved yet."
+        exit 1
+    fi
+    
+    if ! grep -q "^$name|" "$MACRO_FILE"; then
+        echo "Macro '$name' not found."
+        exit 1
+    fi
+    
+    # Remove the macro
+    grep -v "^$name|" "$MACRO_FILE" > "${MACRO_FILE}.tmp"
+    mv "${MACRO_FILE}.tmp" "$MACRO_FILE"
+    echo "Macro '$name' deleted successfully!"
+    exit 0
+}
+
+# Load and execute a macro
+load_macro() {
+    local name="$1"
+    
+    if [ ! -f "$MACRO_FILE" ]; then
+        echo "No macros saved yet. Use --save-macro to create one."
+        exit 1
+    fi
+    
+    local macro_data=$(grep "^$name|" "$MACRO_FILE")
+    if [ -z "$macro_data" ]; then
+        echo "Macro '$name' not found. Use --list-macros to see available macros."
+        exit 1
+    fi
+    
+    # Parse macro data
+    IFS='|' read -r macro_name feat success mode bonus <<< "$macro_data"
+    
+    echo "Using macro: $name"
+    echo ""
+    
+    # Set global variables for rolling
+    FEAT_COUNT=$feat
+    SUCCESS_COUNT=$success
+    ROLL_MODE=$mode
+    BONUS_VALUE=$bonus
+}
+
 # Check for help flag
 if [[ "$1" == "-h" || "$1" == "--help" ]]; then
     show_help
 fi
 
-# Check for arguments
-if [[ "$1" =~ ^[0-9]+$ ]]; then
+# Check for macro commands
+if [[ "$1" == "--macro-help" ]]; then
+    show_macro_help
+elif [[ "$1" == "--save-macro" ]]; then
+    if [ "$#" -lt 4 ]; then
+        echo "Error: --save-macro requires NAME FEAT_DICE SUCCESS_DICE"
+        echo "Use --macro-help for examples"
+        exit 1
+    fi
+    save_macro "$2" "$3" "$4" "$5" "$6"
+elif [[ "$1" == "--list-macros" ]]; then
+    list_macros
+elif [[ "$1" == "--delete-macro" ]]; then
+    if [ "$#" -lt 2 ]; then
+        echo "Error: --delete-macro requires MACRO_NAME"
+        exit 1
+    fi
+    delete_macro "$2"
+fi
+
+# Check if using a saved macro
+if [ "$#" -eq 1 ] && [ "$1" != "" ]; then
+    # Check if it's a macro name (not numeric)
+    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
+        load_macro "$1"
+        # Macro will set FEAT_COUNT, SUCCESS_COUNT, etc.
+    else
+        FEAT_COUNT=$1
+    fi
+elif [[ "$1" =~ ^[0-9]+$ ]]; then
     FEAT_COUNT=$1
     if [[ "$2" =~ ^[0-9]+$ ]]; then
         SUCCESS_COUNT=$2
@@ -51,46 +272,234 @@ roll() {
     echo $(( (val % max) + 1 ))
 }
 
-gum spin --spinner moon --title "🎲 Rolling for The One Ring..." -- sleep 1
+gum spin --spinner moon --title "🔮 Consulting Palantir..." -- sleep 1
 echo ""
 
+# Display macro name if using one
+if [ -n "$macro_name" ]; then
+    if [[ "$ROLL_MODE" == "adv" ]]; then
+        echo "Mode: Advantage (roll 2 sets, keep highest)"
+    elif [[ "$ROLL_MODE" == "disadv" ]]; then
+        echo "Mode: Disadvantage (roll 2 sets, drop highest)"
+    fi
+    if [[ "$BONUS_VALUE" != "0" ]]; then
+        if [[ "$BONUS_VALUE" =~ ^- ]]; then
+            echo "Effect: $BONUS_VALUE (debuff)"
+        else
+            echo "Effect: $BONUS_VALUE (bonus)"
+        fi
+    fi
+    echo ""
+fi
+
 TOTAL=0
 
 # Roll Feat Dice (1d12)
 if [ "$FEAT_COUNT" -gt 0 ]; then
-    echo -n -e "Feat Dice (${FEAT_COUNT}d12): "
-    for ((i=0; i<FEAT_COUNT; i++)); do
-        RESULT=$(roll 12)
-        ((TOTAL+=RESULT))
+    echo -n -e "Feat Dice (${FEAT_COUNT}d12 ): "
+    
+    # Handle advantage/disadvantage for Feat Dice
+    if [[ "$ROLL_MODE" == "adv" ]]; then
+        echo -n "Set 1: "
+        total_set1=0
+        for ((i=0; i<FEAT_COUNT; i++)); do
+            RESULT=$(roll 12)
+            total_set1=$((total_set1 + RESULT))
+            
+            if [ "$RESULT" -eq 1 ]; then
+                echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
+            elif [ "$RESULT" -eq 12 ]; then
+                echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        echo -n -e " | Set 2: "
+        total_set2=0
+        for ((i=0; i<FEAT_COUNT; i++)); do
+            RESULT=$(roll 12)
+            total_set2=$((total_set2 + RESULT))
+            
+            if [ "$RESULT" -eq 1 ]; then
+                echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
+            elif [ "$RESULT" -eq 12 ]; then
+                echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
         
-        if [ "$RESULT" -eq 1 ]; then
-            # Eye of Mordor
-            echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
-        elif [ "$RESULT" -eq 12 ]; then
-            # Gandalf
-            echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+        if [ $total_set1 -ge $total_set2 ]; then
+            TOTAL=$((TOTAL + total_set1))
+            echo -e " (Kept: $total_set1)"
         else
-            echo -n "[ $RESULT ] "
+            TOTAL=$((TOTAL + total_set2))
+            echo -e " (Kept: $total_set2)"
         fi
-    done
+        
+    elif [[ "$ROLL_MODE" == "disadv" ]]; then
+        echo -n "Set 1: "
+        total_set1=0
+        for ((i=0; i<FEAT_COUNT; i++)); do
+            RESULT=$(roll 12)
+            total_set1=$((total_set1 + RESULT))
+            
+            if [ "$RESULT" -eq 1 ]; then
+                echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
+            elif [ "$RESULT" -eq 12 ]; then
+                echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        echo -n -e " | Set 2: "
+        total_set2=0
+        for ((i=0; i<FEAT_COUNT; i++)); do
+            RESULT=$(roll 12)
+            total_set2=$((total_set2 + RESULT))
+            
+            if [ "$RESULT" -eq 1 ]; then
+                echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
+            elif [ "$RESULT" -eq 12 ]; then
+                echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        if [ $total_set1 -le $total_set2 ]; then
+            TOTAL=$((TOTAL + total_set1))
+            echo -e " (Kept: $total_set1)"
+        else
+            TOTAL=$((TOTAL + total_set2))
+            echo -e " (Kept: $total_set2)"
+        fi
+        
+    else
+        # Normal rolling
+        for ((i=0; i<FEAT_COUNT; i++)); do
+            RESULT=$(roll 12)
+            ((TOTAL+=RESULT))
+            
+            if [ "$RESULT" -eq 1 ]; then
+                # Eye of Mordor
+                echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
+            elif [ "$RESULT" -eq 12 ]; then
+                # Gandalf
+                echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+    fi
     echo ""
 fi
 
 # Roll Success Dice (1d6)
 if [ "$SUCCESS_COUNT" -gt 0 ]; then
-    echo -n -e "Success Dice (${SUCCESS_COUNT}d6): "
-    for ((i=0; i<SUCCESS_COUNT; i++)); do
-        RESULT=$(roll 6)
-        ((TOTAL+=RESULT))
+    echo -n -e "Success Dice (${SUCCESS_COUNT}d6 ): "
+    
+    # Handle advantage/disadvantage for Success Dice
+    if [[ "$ROLL_MODE" == "adv" ]]; then
+        echo -n "Set 1: "
+        total_set1=0
+        for ((i=0; i<SUCCESS_COUNT; i++)); do
+            RESULT=$(roll 6)
+            total_set1=$((total_set1 + RESULT))
+            
+            if [ "$RESULT" -eq 6 ]; then
+                echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
         
-        if [ "$RESULT" -eq 6 ]; then
-            # Magnificent Outcome
-            echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+        echo -n -e " | Set 2: "
+        total_set2=0
+        for ((i=0; i<SUCCESS_COUNT; i++)); do
+            RESULT=$(roll 6)
+            total_set2=$((total_set2 + RESULT))
+            
+            if [ "$RESULT" -eq 6 ]; then
+                echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        if [ $total_set1 -ge $total_set2 ]; then
+            TOTAL=$((TOTAL + total_set1))
+            echo -e " (Kept: $total_set1)"
         else
-            echo -n "[ $RESULT ] "
+            TOTAL=$((TOTAL + total_set2))
+            echo -e " (Kept: $total_set2)"
         fi
-    done
+        
+    elif [[ "$ROLL_MODE" == "disadv" ]]; then
+        echo -n "Set 1: "
+        total_set1=0
+        for ((i=0; i<SUCCESS_COUNT; i++)); do
+            RESULT=$(roll 6)
+            total_set1=$((total_set1 + RESULT))
+            
+            if [ "$RESULT" -eq 6 ]; then
+                echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        echo -n -e " | Set 2: "
+        total_set2=0
+        for ((i=0; i<SUCCESS_COUNT; i++)); do
+            RESULT=$(roll 6)
+            total_set2=$((total_set2 + RESULT))
+            
+            if [ "$RESULT" -eq 6 ]; then
+                echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+        
+        if [ $total_set1 -le $total_set2 ]; then
+            TOTAL=$((TOTAL + total_set1))
+            echo -e " (Kept: $total_set1)"
+        else
+            TOTAL=$((TOTAL + total_set2))
+            echo -e " (Kept: $total_set2)"
+        fi
+        
+    else
+        # Normal rolling
+        for ((i=0; i<SUCCESS_COUNT; i++)); do
+            RESULT=$(roll 6)
+            ((TOTAL+=RESULT))
+            
+            if [ "$RESULT" -eq 6 ]; then
+                # Magnificent Outcome
+                echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
+            else
+                echo -n "[ $RESULT ] "
+            fi
+        done
+    fi
     echo ""
 fi
 
-echo -e "\n${BOLD}Total Face Value: $TOTAL${RESET}"
+# Apply bonus/debuff
+if [[ "$BONUS_VALUE" != "0" ]]; then
+    if [[ "$BONUS_VALUE" =~ ^- ]]; then
+        TOTAL=$((TOTAL + BONUS_VALUE))  # BONUS_VALUE is negative
+        echo -e "\n${BOLD}Total Face Value (before debuff): $((TOTAL - BONUS_VALUE))${RESET}"
+        echo -e "${BOLD}Final Total (after ${BONUS_VALUE} debuff): $TOTAL${RESET}"
+    else
+        TOTAL=$((TOTAL + BONUS_VALUE))
+        echo -e "\n${BOLD}Total Face Value (before bonus): $((TOTAL - BONUS_VALUE))${RESET}"
+        echo -e "${BOLD}Final Total (after ${BONUS_VALUE} bonus): $TOTAL${RESET}"
+    fi
+else
+    echo -e "\n${BOLD}Total Face Value: $TOTAL${RESET}"
+fi