diff --git a/README.md b/README.md index cfdd15c..32a2aef 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This is a collection of my own ports of the [Scoundrel](http://stfj.net/art/2011 * [Game Boy port](gb/gbscoundrel.c) - gamepad-oriented, considered stable but some impovements may come * [Janet port](janet/scoundrel.janet) - close to C89 port but not fully identical * [Fennel port](fennel/scoundrel.fnl) - functionally identical to the Janet port +* [Jim Tcl port](scoundrel-jim.tcl) - functionally close to the Bash port ## Ports NOT included in this repo for various reasons diff --git a/scoundrel-jim.tcl b/scoundrel-jim.tcl new file mode 100755 index 0000000..9f8b319 --- /dev/null +++ b/scoundrel-jim.tcl @@ -0,0 +1,172 @@ +#!/usr/bin/env jimsh +# Scoundrel roguelike solitaire game port to Jim Tcl +# Created by Luxferre in 2026, released into the public domain + +# helpers + +proc shuffle list { + set n [llength $list] + loop i 1 $n { + set j $(int(rand() * $n)) + set temp [lindex $list $i] + lset list $i [lindex $list $j] + lset list $j $temp + } + return $list +} + +proc get_valid_choice {prompt pat} { + set c -- + puts -nonewline [format "%s " $prompt] + stdout flush + stdin tty input raw + while {$c ni $pat} { + set c [string tolower [stdin read 1]] + } + stdin tty input cooked + return $c +} + +# variables and states +set score -208 +set hp 20 +set can_run 1 +set engaged 0 +set drank 0 +set weapon 0 +set durability 0 +set rooms_clrd 0 +set last_potion_val 0 + +# deck and room +set deck [shuffle [concat [range 1 14] [range 1 32]]] +set room {} + +# game logic functions + +proc game_field_display {} { + global hp weapon durability room deck can_run engaged + puts "--------------------------" + puts [format "H%02d | W%02d | D%02d | Deck: %02d" $hp $weapon $durability [llength $deck]] + puts "--------------------------" + foreach x $room { + if $($x < 14) { + puts -nonewline [format "M%02d " $($x + 1)] + } elseif $($x < 23) { + puts -nonewline [format "W%02d " $($x - 12)] + } else { + puts -nonewline [format "H%02d " $($x - 21)] + } + } + puts -nonewline "\n 1 2 3 4" + if {$can_run == 1 && $engaged == 0} {puts -nonewline " r)un->"} +} + +# monster handling logic (adds monster value to the score) +proc handle_monster {val} { + global weapon durability hp score + set healthlost $val + puts [format "You encounter a monster of strength %d." $val] + if {$weapon > 0 && $durability >= $val} { + if {[get_valid_choice "Use weapon? (y/n)" "y n"] == "y"} { + set healthlost $($val - $weapon) + if {$healthlost < 0} {set healthlost 0} + puts [format "\nYou fight the monster with your %d-level weapon." $weapon] + set durability $($val - 1) + if {$durability < 2} { + set healthlost 0 + set weapon 0 + puts "Your weapon fully breaks!" + } + } else {puts "\nYou fight the monster barehanded."} + } else {puts "You fight the monster barehanded."} + set hp $($hp - $healthlost) + if {$hp < 0} {set hp 0} + puts [format "Lost %d HP. New HP: %d" $healthlost $hp] + incr score $val +} + +# weapon handling logic +proc handle_weapon {val} { + global weapon durability + set weapon $val + set durability 14 + puts [format "Equipped a %d-level weapon." $weapon] +} + +# potion handling logic +proc handle_potion {val} { + global hp drank deck room + if {$drank > 0} { + puts "Already drank a potion here! Discarding..." + } else { + if {[llength $deck] == 0 && [llength $room] == 0} {set last_potion_val $val} + incr hp $val + if {$hp > 20} {set hp 20} + set drank 1 + puts [format "Drank a %d-level potion. New HP: %d" $val $hp] + } +} + +# game banner +puts "----- Scoundrel 2026 -----" +puts " Jim Tcl port by Luxferre" +puts " Good luck!" + +# main game loop +while {([llength $deck] + [llength $room]) > 0 && $hp > 0} { + if {$engaged == 0} { + while {[llength $deck] > 0 && [llength $room] < 4} { + lappend room [lindex $deck 0] + set deck [lreplace $deck 0 0] + } + } + game_field_display + set chs {1 2 3 4 q} + if {$can_run == 1 && $engaged == 0} {set chs {1 2 3 4 q r}} + set ch [get_valid_choice {} $chs] + puts {} + if {$ch == "q"} { + if {[get_valid_choice "Really quit? (y/n)" "y n"] == "y"} { + puts "\nQuitting!" + exit + } + } elseif {$ch == "r"} { + set can_run 0 + while {[llength $room] > 0} { + set idx [rand 0 [llength $room]] + lappend deck [lindex $room $idx] + set room [lreplace $room $idx $idx] + } + puts "You run from the room." + } else { + incr ch -1 + set item [lindex $room $ch] + if {$item > 0} { + set engaged 1 + set room [lreplace $room $ch $ch] + if {$item < 14} { + handle_monster $($item + 1) + } elseif {$item < 23} { + handle_weapon $($item - 12) + } else {handle_potion $($item - 21)} + if {[llength $deck] > 0 && [llength $room] == 1} { + set can_run 1 + set engaged 0 + set drank 0 + incr rooms_clrd + } + } + } +} + +# now score and output the game result +if {$hp > 0} { + set score $hp + if {$hp == 20} {incr score $last_potion_val} + incr rooms_clrd + puts -nonewline "You win!" +} else { + puts -nonewline "You lose!" +} +puts [format " Your score: %d. Rooms cleared: %d" $score $rooms_clrd]