Files
bash-goodies/scoundrel
T

167 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# SHoundrel: a Bash version of Scoundrel roguelike solitaire
# Created by Luxferre in 2026, released into public domain
# helpers
get_choice() {
local c
read -r -p "$1" -n 1 c
printf '%s' "${c@L}"
}
get_valid_choice() {
local pr="$1" vcs="$2" c="_"
until [[ "$vcs" == *$c* ]]; do
c="$(get_choice "$pr")"
done
echo "$c"
}
# variables and states
declare -i score=-208 hp=20 can_run=1 engaged=0 drank=0 \
weapon=0 durability=0 rooms_clrd=0 last_potion_val=0
declare -a deck room=()
# initialize the deck
deck=($(shuf -e {1..13} {1..31}))
# game logic functions
game_field_display() {
local -i x
echo '--------------------------'
printf 'H%02d | W%02d | D%02d | Deck: %02d\n' $hp $weapon $durability ${#deck[@]}
echo '--------------------------'
for x in "${room[@]}"; do
if ((x < 14)); then
printf 'M%02d ' $((x + 1))
elif ((x < 23)); then
printf 'W%02d ' $((x - 12))
else
printf 'H%02d ' $((x - 21))
fi
done
printf '\n %s' '1 2 3 4'
if ((can_run && !engaged)); then
printf '%s' ' r)un->'
fi
echo
}
# monster handling logic (adds monster value to the score)
handle_monster() {
local -i val="$1" healthlost="$1"
printf 'You encounter a monster of strength %d.\n' $val
if ((weapon > 0 && durability >= val)); then # can use weapon
if [[ "$(get_valid_choice 'Use weapon? (y/n) ' 'yn')" = 'y' ]]; then
healthlost=$((val - weapon))
((healthlost < 0)) && healthlost=0
printf '\nYou fight the monster with your %d-level weapon.\n' $weapon
durability=$((val - 1))
if ((durability < 2)); then
durability=0
weapon=0
echo 'Your weapon fully breaks!'
fi
else # barehanded encounter
printf '\nYou fight the monster barehanded.\n'
fi
else # barehanded encounter
printf '\nYou fight the monster barehanded.\n'
fi
((hp -= healthlost))
((hp < 0)) && hp=0
printf 'Lost %d HP. New HP: %d\n' $healthlost $hp
((score += val))
}
# weapon handling logic
handle_weapon() {
weapon="$1"
durability=14
printf 'Equipped a %d-level weapon.\n' $weapon
}
# potion handling logic
handle_potion() {
if ((drank > 0)); then
echo 'Already drank a potion here! Discarding...'
else
local -i val="$1"
((${#room[@]} == 0 && ${#deck[@]} == 0)) && last_potion_val=$val
((hp += val))
((hp > 20)) && hp=20
drank=1
printf 'Drank a %d-level potion. New HP: %d\n' $val $hp
fi
}
# main game loop
declare choice
declare -i item idx
echo '----- Scoundrel 2026 -----'
echo ' Bash port by Luxferre'
echo ' Good luck!'
while (( (${#deck[@]} + ${#room[@]}) > 0 && hp > 0 )); do
if (( !engaged )); then # build the room
while (( ${#deck[@]} > 0 && ${#room[@]} < 4 )); do
room+=("${deck[0]}")
unset 'deck[0]'
deck=("${deck[@]}")
done
fi
game_field_display
if ((can_run == 1 && engaged == 0)); then
choice=$(get_valid_choice 'Your choice: ' '1234r')
else
choice=$(get_valid_choice 'Your choice: ' '1234')
fi
echo
if [[ "$choice" = 'r' ]] ; then # running logic
can_run=0 # cannot run twice in a row
while (( ${#room[@]} > 0 )); do
idx=$((RANDOM % ${#room[@]}))
deck+=("${room[$idx]}")
unset 'room[idx]'
room=("${room[@]}")
done
echo 'You run from the room.'
else # engagement logic
((-- choice))
item=${room[$choice]}
if ((item > 0)); then
engaged=1
unset 'room[choice]'
room=("${room[@]}")
if ((item < 14)); then
handle_monster $((item + 1))
elif ((item < 23)); then
handle_weapon $((item - 12))
else
handle_potion $((item - 21))
fi
if (( ${#deck[@]} > 0 && ${#room[@]} == 1 )); then
can_run=1
engaged=0
drank=0
((++ rooms_clrd))
fi
fi
fi
done
# now score and output the game result
if ((hp > 0)); then
score=$hp
((hp == 20)) && ((score += last_potion_val))
((++ rooms_clrd))
printf 'You win! '
else
printf 'You lose! '
fi
printf 'Your score: %d. Rooms cleared: %d\n' $score $rooms_clrd