roll_tor.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. #!/bin/bash
  2. # Defaults
  3. FEAT_COUNT=1
  4. SUCCESS_COUNT=0
  5. ROLL_MODE="" # adv, disadv, or empty
  6. BONUS_VALUE=0 # bonus/debuff value
  7. # Macro configuration
  8. MACRO_FILE="$HOME/.tor-roller-macros"
  9. # Help function
  10. show_help() {
  11. echo "Usage: $0 [FEAT_DICE] [SUCCESS_DICE] [OPTIONS]"
  12. echo ""
  13. echo "Arguments:"
  14. echo " FEAT_DICE Number of 1d12 Feat Dice to roll (default: 1)"
  15. echo " SUCCESS_DICE Number of 1d6 Success Dice to roll (default: 0)"
  16. echo ""
  17. echo "Options:"
  18. echo " -h, --help Show this help message"
  19. echo " macro Use a saved macro (see --macro-help)"
  20. echo ""
  21. echo "Examples:"
  22. echo " $0 # Rolls 1 Feat Die"
  23. echo " $0 1 3 # Rolls 1 Feat Die and 3 Success Dice"
  24. echo " $0 2 0 # Rolls 2 Feat Dice"
  25. echo ""
  26. echo "Macro Examples:"
  27. echo " $0 --macro-help # Show macro usage"
  28. echo " $0 --save-macro hero 1 3 # Save macro named 'hero' with 1d12+3d6"
  29. echo " $0 --save-macro attack 2 4 adv # Save with advantage"
  30. echo " $0 --save-macro spell 1 2 disadv # Save with disadvantage"
  31. echo " $0 --save-macro skill 1 2 +5 # Save with +5 bonus"
  32. echo " $0 --list-macros # List all saved macros"
  33. echo " $0 hero # Use saved macro"
  34. exit 0
  35. }
  36. # Show macro help
  37. show_macro_help() {
  38. echo "Macro Usage:"
  39. echo ""
  40. echo "Save Macros:"
  41. echo " --save-macro NAME FEAT_DICE SUCCESS_DICE [ADV|DISADV] [BONUS]"
  42. echo " NAME: Name for the macro"
  43. echo " FEAT_DICE: Number of d12 dice (1-99)"
  44. echo " SUCCESS_DICE: Number of d6 dice (0-99)"
  45. echo " ADV: Advantage (roll 2 sets, keep highest)"
  46. echo " DISADV: Disadvantage (roll 2 sets, drop highest)"
  47. echo " BONUS: Bonus/debuff value (+5, -3, etc.)"
  48. echo ""
  49. echo "Examples:"
  50. echo " $0 --save-macro hero 1 3"
  51. echo " $0 --save-macro attack 2 4 adv"
  52. echo " $0 --save-macro spell 1 2 disadv"
  53. echo " $0 --save-macro skill 1 2 +5"
  54. echo ""
  55. echo "Use Macros:"
  56. echo " $0 MACRO_NAME"
  57. echo ""
  58. echo "List Macros:"
  59. echo " $0 --list-macros"
  60. echo ""
  61. echo "Delete Macros:"
  62. echo " $0 --delete-macro MACRO_NAME"
  63. echo ""
  64. exit 0
  65. }
  66. # Save a macro
  67. save_macro() {
  68. local name="$1"
  69. local feat="$2"
  70. local success="$3"
  71. local mode="$4"
  72. local bonus="$5"
  73. # Validate inputs
  74. if [[ ! "$feat" =~ ^[0-9]+$ ]] || [ "$feat" -lt 1 ] || [ "$feat" -gt 99 ]; then
  75. echo "Error: FEAT_DICE must be a number between 1 and 99"
  76. exit 1
  77. fi
  78. if [[ ! "$success" =~ ^[0-9]+$ ]] || [ "$success" -gt 99 ]; then
  79. echo "Error: SUCCESS_DICE must be a number between 0 and 99"
  80. exit 1
  81. fi
  82. # Handle mode and bonus arguments
  83. if [[ "$mode" == "adv" ]] || [[ "$mode" == "ADV" ]]; then
  84. mode="adv"
  85. bonus="$5"
  86. elif [[ "$mode" == "disadv" ]] || [[ "$mode" == "DISADV" ]]; then
  87. mode="disadv"
  88. bonus="$5"
  89. elif [[ "$mode" =~ ^[+-]?[0-9]+$ ]]; then
  90. # This is a bonus, mode is empty
  91. mode=""
  92. bonus="$4" # bonus is in the mode position
  93. else
  94. mode=""
  95. bonus="$4" # bonus is in the mode position if provided
  96. fi
  97. # Validate bonus if provided
  98. if [[ "$bonus" != "" ]] && [[ "$bonus" != "0" ]] && [[ ! "$bonus" =~ ^[+-]?[0-9]+$ ]]; then
  99. echo "Error: BONUS must be a number with + or - (e.g., +5, -3)"
  100. exit 1
  101. fi
  102. # If bonus is empty or not a number, set to 0
  103. if [[ "$bonus" == "" ]] || [[ ! "$bonus" =~ ^[+-]?[0-9]+$ ]]; then
  104. bonus="0"
  105. fi
  106. # Create macro entry
  107. local entry="$name|$feat|$success|$mode|$bonus"
  108. # Create directory if it doesn't exist
  109. mkdir -p "$(dirname "$MACRO_FILE")"
  110. # Remove existing macro with same name
  111. if [ -f "$MACRO_FILE" ]; then
  112. grep -v "^$name|" "$MACRO_FILE" > "${MACRO_FILE}.tmp" 2>/dev/null || true
  113. mv "${MACRO_FILE}.tmp" "$MACRO_FILE"
  114. fi
  115. # Add new macro
  116. echo "$entry" >> "$MACRO_FILE"
  117. echo "Macro '$name' saved successfully!"
  118. echo " Feat Dice: $feat d12, Success Dice: $success d6"
  119. if [[ "$mode" == "adv" ]]; then
  120. echo " Mode: Advantage"
  121. elif [[ "$mode" == "disadv" ]]; then
  122. echo " Mode: Disadvantage"
  123. fi
  124. if [[ "$bonus" != "0" ]]; then
  125. echo " Bonus: $bonus"
  126. fi
  127. exit 0
  128. }
  129. # List all macros
  130. list_macros() {
  131. echo "Saved Macros:"
  132. echo ""
  133. if [ ! -f "$MACRO_FILE" ]; then
  134. echo "No macros saved yet."
  135. echo ""
  136. echo "Use --save-macro to create a macro, or --macro-help for examples."
  137. exit 0
  138. fi
  139. printf "%-20s %-10s %-12s %-12s %s\n" "NAME" "FEAT_DICE" "SUCCESS_DICE" "MODE" "BONUS"
  140. printf "%-20s %-10s %-12s %-12s %s\n" "----" "---------" "------------" "----" "-----"
  141. while IFS='|' read -r name feat success mode bonus; do
  142. local mode_display="${mode:-(none)}"
  143. local bonus_display="$bonus"
  144. if [[ "$bonus" == "0" ]]; then
  145. bonus_display="(none)"
  146. fi
  147. printf "%-20s %-10s %-12s %-12s %s\n" "$name" "${feat}d12" "${success}d6" "$mode_display" "$bonus_display"
  148. done < "$MACRO_FILE"
  149. exit 0
  150. }
  151. # Delete a macro
  152. delete_macro() {
  153. local name="$1"
  154. if [ ! -f "$MACRO_FILE" ]; then
  155. echo "No macros saved yet."
  156. exit 1
  157. fi
  158. if ! grep -q "^$name|" "$MACRO_FILE"; then
  159. echo "Macro '$name' not found."
  160. exit 1
  161. fi
  162. # Remove the macro
  163. grep -v "^$name|" "$MACRO_FILE" > "${MACRO_FILE}.tmp"
  164. mv "${MACRO_FILE}.tmp" "$MACRO_FILE"
  165. echo "Macro '$name' deleted successfully!"
  166. exit 0
  167. }
  168. # Load and execute a macro
  169. load_macro() {
  170. local name="$1"
  171. if [ ! -f "$MACRO_FILE" ]; then
  172. echo "No macros saved yet. Use --save-macro to create one."
  173. exit 1
  174. fi
  175. local macro_data=$(grep "^$name|" "$MACRO_FILE")
  176. if [ -z "$macro_data" ]; then
  177. echo "Macro '$name' not found. Use --list-macros to see available macros."
  178. exit 1
  179. fi
  180. # Parse macro data
  181. IFS='|' read -r macro_name feat success mode bonus <<< "$macro_data"
  182. echo "Using macro: $name"
  183. echo ""
  184. # Set global variables for rolling
  185. FEAT_COUNT=$feat
  186. SUCCESS_COUNT=$success
  187. ROLL_MODE=$mode
  188. BONUS_VALUE=$bonus
  189. }
  190. # Check for help flag
  191. if [[ "$1" == "-h" || "$1" == "--help" ]]; then
  192. show_help
  193. fi
  194. # Check for macro commands
  195. if [[ "$1" == "--macro-help" ]]; then
  196. show_macro_help
  197. elif [[ "$1" == "--save-macro" ]]; then
  198. if [ "$#" -lt 4 ]; then
  199. echo "Error: --save-macro requires NAME FEAT_DICE SUCCESS_DICE"
  200. echo "Use --macro-help for examples"
  201. exit 1
  202. fi
  203. save_macro "$2" "$3" "$4" "$5" "$6"
  204. elif [[ "$1" == "--list-macros" ]]; then
  205. list_macros
  206. elif [[ "$1" == "--delete-macro" ]]; then
  207. if [ "$#" -lt 2 ]; then
  208. echo "Error: --delete-macro requires MACRO_NAME"
  209. exit 1
  210. fi
  211. delete_macro "$2"
  212. fi
  213. # Check if using a saved macro
  214. if [ "$#" -eq 1 ] && [ "$1" != "" ]; then
  215. # Check if it's a macro name (not numeric)
  216. if ! [[ "$1" =~ ^[0-9]+$ ]]; then
  217. load_macro "$1"
  218. # Macro will set FEAT_COUNT, SUCCESS_COUNT, etc.
  219. else
  220. FEAT_COUNT=$1
  221. fi
  222. elif [[ "$1" =~ ^[0-9]+$ ]]; then
  223. FEAT_COUNT=$1
  224. if [[ "$2" =~ ^[0-9]+$ ]]; then
  225. SUCCESS_COUNT=$2
  226. fi
  227. fi
  228. # Colors and formatting
  229. BOLD='\033[1m'
  230. RED='\033[31m'
  231. GREEN='\033[32m'
  232. CYAN='\033[36m' # Using Cyan for Gandalf as it's bright/distinct
  233. RESET='\033[0m'
  234. # Function to generate a random number between 1 and max
  235. roll() {
  236. local max=$1
  237. # Read 2 bytes from /dev/urandom, convert to decimal, modulo max, add 1
  238. # This provides better randomness than $RANDOM
  239. val=$(od -An -N2 -tu2 /dev/urandom)
  240. echo $(( (val % max) + 1 ))
  241. }
  242. gum spin --spinner moon --title "🔮 Consulting Palantir..." -- sleep 1
  243. echo ""
  244. # Display macro name if using one
  245. if [ -n "$macro_name" ]; then
  246. if [[ "$ROLL_MODE" == "adv" ]]; then
  247. echo "Mode: Advantage (roll 2 sets, keep highest)"
  248. elif [[ "$ROLL_MODE" == "disadv" ]]; then
  249. echo "Mode: Disadvantage (roll 2 sets, drop highest)"
  250. fi
  251. if [[ "$BONUS_VALUE" != "0" ]]; then
  252. if [[ "$BONUS_VALUE" =~ ^- ]]; then
  253. echo "Effect: $BONUS_VALUE (debuff)"
  254. else
  255. echo "Effect: $BONUS_VALUE (bonus)"
  256. fi
  257. fi
  258. echo ""
  259. fi
  260. TOTAL=0
  261. # Roll Feat Dice (1d12)
  262. if [ "$FEAT_COUNT" -gt 0 ]; then
  263. echo -n -e "Feat Dice (${FEAT_COUNT}d12 ): "
  264. # Handle advantage/disadvantage for Feat Dice
  265. if [[ "$ROLL_MODE" == "adv" ]]; then
  266. echo -n "Set 1: "
  267. total_set1=0
  268. for ((i=0; i<FEAT_COUNT; i++)); do
  269. RESULT=$(roll 12)
  270. total_set1=$((total_set1 + RESULT))
  271. if [ "$RESULT" -eq 1 ]; then
  272. echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
  273. elif [ "$RESULT" -eq 12 ]; then
  274. echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
  275. else
  276. echo -n "[ $RESULT ] "
  277. fi
  278. done
  279. echo -n -e " | Set 2: "
  280. total_set2=0
  281. for ((i=0; i<FEAT_COUNT; i++)); do
  282. RESULT=$(roll 12)
  283. total_set2=$((total_set2 + RESULT))
  284. if [ "$RESULT" -eq 1 ]; then
  285. echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
  286. elif [ "$RESULT" -eq 12 ]; then
  287. echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
  288. else
  289. echo -n "[ $RESULT ] "
  290. fi
  291. done
  292. if [ $total_set1 -ge $total_set2 ]; then
  293. TOTAL=$((TOTAL + total_set1))
  294. echo -e " (Kept: $total_set1)"
  295. else
  296. TOTAL=$((TOTAL + total_set2))
  297. echo -e " (Kept: $total_set2)"
  298. fi
  299. elif [[ "$ROLL_MODE" == "disadv" ]]; then
  300. echo -n "Set 1: "
  301. total_set1=0
  302. for ((i=0; i<FEAT_COUNT; i++)); do
  303. RESULT=$(roll 12)
  304. total_set1=$((total_set1 + RESULT))
  305. if [ "$RESULT" -eq 1 ]; then
  306. echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
  307. elif [ "$RESULT" -eq 12 ]; then
  308. echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
  309. else
  310. echo -n "[ $RESULT ] "
  311. fi
  312. done
  313. echo -n -e " | Set 2: "
  314. total_set2=0
  315. for ((i=0; i<FEAT_COUNT; i++)); do
  316. RESULT=$(roll 12)
  317. total_set2=$((total_set2 + RESULT))
  318. if [ "$RESULT" -eq 1 ]; then
  319. echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
  320. elif [ "$RESULT" -eq 12 ]; then
  321. echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
  322. else
  323. echo -n "[ $RESULT ] "
  324. fi
  325. done
  326. if [ $total_set1 -le $total_set2 ]; then
  327. TOTAL=$((TOTAL + total_set1))
  328. echo -e " (Kept: $total_set1)"
  329. else
  330. TOTAL=$((TOTAL + total_set2))
  331. echo -e " (Kept: $total_set2)"
  332. fi
  333. else
  334. # Normal rolling
  335. for ((i=0; i<FEAT_COUNT; i++)); do
  336. RESULT=$(roll 12)
  337. ((TOTAL+=RESULT))
  338. if [ "$RESULT" -eq 1 ]; then
  339. # Eye of Mordor
  340. echo -n -e "[ ${BOLD}${RED}1 (EYE)${RESET} ] "
  341. elif [ "$RESULT" -eq 12 ]; then
  342. # Gandalf
  343. echo -n -e "[ ${BOLD}${CYAN}12 (GANDALF)${RESET} ] "
  344. else
  345. echo -n "[ $RESULT ] "
  346. fi
  347. done
  348. fi
  349. echo ""
  350. fi
  351. # Roll Success Dice (1d6)
  352. if [ "$SUCCESS_COUNT" -gt 0 ]; then
  353. echo -n -e "Success Dice (${SUCCESS_COUNT}d6 ): "
  354. # Handle advantage/disadvantage for Success Dice
  355. if [[ "$ROLL_MODE" == "adv" ]]; then
  356. echo -n "Set 1: "
  357. total_set1=0
  358. for ((i=0; i<SUCCESS_COUNT; i++)); do
  359. RESULT=$(roll 6)
  360. total_set1=$((total_set1 + RESULT))
  361. if [ "$RESULT" -eq 6 ]; then
  362. echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
  363. else
  364. echo -n "[ $RESULT ] "
  365. fi
  366. done
  367. echo -n -e " | Set 2: "
  368. total_set2=0
  369. for ((i=0; i<SUCCESS_COUNT; i++)); do
  370. RESULT=$(roll 6)
  371. total_set2=$((total_set2 + RESULT))
  372. if [ "$RESULT" -eq 6 ]; then
  373. echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
  374. else
  375. echo -n "[ $RESULT ] "
  376. fi
  377. done
  378. if [ $total_set1 -ge $total_set2 ]; then
  379. TOTAL=$((TOTAL + total_set1))
  380. echo -e " (Kept: $total_set1)"
  381. else
  382. TOTAL=$((TOTAL + total_set2))
  383. echo -e " (Kept: $total_set2)"
  384. fi
  385. elif [[ "$ROLL_MODE" == "disadv" ]]; then
  386. echo -n "Set 1: "
  387. total_set1=0
  388. for ((i=0; i<SUCCESS_COUNT; i++)); do
  389. RESULT=$(roll 6)
  390. total_set1=$((total_set1 + RESULT))
  391. if [ "$RESULT" -eq 6 ]; then
  392. echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
  393. else
  394. echo -n "[ $RESULT ] "
  395. fi
  396. done
  397. echo -n -e " | Set 2: "
  398. total_set2=0
  399. for ((i=0; i<SUCCESS_COUNT; i++)); do
  400. RESULT=$(roll 6)
  401. total_set2=$((total_set2 + RESULT))
  402. if [ "$RESULT" -eq 6 ]; then
  403. echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
  404. else
  405. echo -n "[ $RESULT ] "
  406. fi
  407. done
  408. if [ $total_set1 -le $total_set2 ]; then
  409. TOTAL=$((TOTAL + total_set1))
  410. echo -e " (Kept: $total_set1)"
  411. else
  412. TOTAL=$((TOTAL + total_set2))
  413. echo -e " (Kept: $total_set2)"
  414. fi
  415. else
  416. # Normal rolling
  417. for ((i=0; i<SUCCESS_COUNT; i++)); do
  418. RESULT=$(roll 6)
  419. ((TOTAL+=RESULT))
  420. if [ "$RESULT" -eq 6 ]; then
  421. # Magnificent Outcome
  422. echo -n -e "[ ${BOLD}${GREEN}6 (MAGNIFICENT)${RESET} ] "
  423. else
  424. echo -n "[ $RESULT ] "
  425. fi
  426. done
  427. fi
  428. echo ""
  429. fi
  430. # Apply bonus/debuff
  431. if [[ "$BONUS_VALUE" != "0" ]]; then
  432. if [[ "$BONUS_VALUE" =~ ^- ]]; then
  433. TOTAL=$((TOTAL + BONUS_VALUE)) # BONUS_VALUE is negative
  434. echo -e "\n${BOLD}Total Face Value (before debuff): $((TOTAL - BONUS_VALUE))${RESET}"
  435. echo -e "${BOLD}Final Total (after ${BONUS_VALUE} debuff): $TOTAL${RESET}"
  436. else
  437. TOTAL=$((TOTAL + BONUS_VALUE))
  438. echo -e "\n${BOLD}Total Face Value (before bonus): $((TOTAL - BONUS_VALUE))${RESET}"
  439. echo -e "${BOLD}Final Total (after ${BONUS_VALUE} bonus): $TOTAL${RESET}"
  440. fi
  441. else
  442. echo -e "\n${BOLD}Total Face Value: $TOTAL${RESET}"
  443. fi