167 lines
4.2 KiB
Bash
Executable File
167 lines
4.2 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 the harder way to possibly avoid mapfile/subshell issues with iSH
|
|
IFS=' ' read -r -a deck <<< "$(shuf -e {1..13} {1..31} | { mapfile -t d; echo "${d[@]}"; })"
|
|
|
|
# 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[@]} == 1 && ${#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
|