jungle/jungle2.nim

561 lines
17 KiB
Nim

# Lost in the Jungle: a silly little survival game.
# 2021-03-30 Felix Pleşoianu <https://felix.plesoianu.ro/>
# Based on the Lua edition. Use as you like, and enjoy!
import random, strutils, terminal, std/exitprocs
addExitProc(resetAttributes)
randomize()
var color = false;
var leave = false;
var fatigue = 0.0
var health = 5.0
var bullets = 6
var skill = 0.15
var distance = 50.0
var hours = 0
var chances = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
proc startGame() =
fatigue = 0.0
health = 5.0
bullets = 6
skill = 0.15
distance = float(44 + rand(1..10))
hours = 0
chances = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
func tiredness(fatigue, health: float): string =
let energy = health - fatigue
if energy <= 1:
return "drained"
elif energy <= 3:
return "tired"
else:
return "fresh"
func healthLevel(health: float): string =
if health < 2:
return "bad"
elif health < 4:
return "decent"
else:
return "good"
proc status(): (string, string) =
var status1 =
"In " & health_level(health) & " health; " &
tiredness(fatigue, health) &
". Bullets: " & $bullets &
". Time: " & $hours & "h."
var status2 = ""
if distance >= 35:
status2 = "Can't see the sky for the forest canopy."
elif distance >= 15:
status2 = "Shafts of sunlight mark the path ahead."
else:
status2 = "The trees are growing farther apart now."
return (status1, status2)
proc drawStatus(line1, line2: string) =
resetAttributes()
if color:
setBackgroundColor bgBlack
eraseScreen()
if color:
setForegroundColor fgYellow, true
setBackgroundColor bgGreen
else:
setStyle {styleBright}
setCursorPos 0, 0
eraseLine()
echo line1
eraseLine()
echo line2
proc menu(options: varargs[string]): Natural =
resetAttributes()
if color:
setForegroundColor fgWhite, true
setBackgroundColor bgBlack
else:
setStyle {styleBright}
for i, j in options:
echo i + 1, ") ", j
while result < 1 or result > len(options):
stdout.write "\n> "
let c = getch(); echo c
if c >= '1' and c <= '9':
result = parseInt($c)
elif c == '0' or c.int == 27:
break
resetAttributes()
proc doWalk()
proc doRest()
proc noEncounter() =
echo "Around you, the jungle looms."
case menu("March on", "Get some rest")
of 0: leave = true
of 1: doWalk()
of 2: doRest()
else: raiseAssert("Logic error")
proc doWalk() =
if fatigue >= health:
echo "You can't take another step."
doRest()
else:
let walked = health - fatigue
distance -= walked
fatigue += 1
hours += 1
if walked <= 1:
echo "You crawl along tiredly."
elif walked <= 3:
echo "You march on, making steady progress."
else:
echo "You advance quickly for now..."
proc itsVenomous()
proc fightMonkeys()
proc doRest() =
hours += 1
if health < 5:
health += 0.5
if health > 5:
health = 5
if fatigue >= health:
fatigue -= 1
else:
fatigue -= 0.5
if fatigue < 0:
fatigue = 0
echo "You rest and heal a little."
else:
if fatigue >= health:
fatigue -= 2
else:
fatigue -= 1
if fatigue < 0:
fatigue = 0
echo "You get some good rest."
let chance = rand(1.0)
if chance < 0.15:
echo "But while you were sleeping...\n"
fightMonkeys()
elif chance < 0.3:
echo "But while you were sleeping...\n"
itsVenomous()
proc drinkWater()
proc noDrinking()
proc findWater() =
echo "You find a pool of water."
case menu("Drink some", "Leave it")
of 0: leave = true
of 1: drinkWater()
of 2: noDrinking()
else: raiseAssert("Logic error.")
proc drinkWater() =
fatigue -= 2
if fatigue < 0:
fatigue = 0
echo "The water is cool. You feel refreshed."
if rand(1.0) >= skill:
echo "But drinking from the pool soon makes you ill."
echo "At least you learn the signs better."
health -= 1
skill += 0.05
proc noDrinking() =
echo "Better not chance taking a drink at this time."
proc eatFruit()
proc noEating()
proc findFruit() =
echo "You find strange fruit."
case menu("Eat some", "Leave it")
of 0: leave = true
of 1: eatFruit()
of 2: noEating()
else: raiseAssert("Logic error.")
proc eatFruit() =
health += 1
if health > 5:
health = 5
echo "The fruit is tasty. You recover some strength."
if rand(1.0) >= skill:
echo "But soon after eating it you feel drowsy."
echo "At least you learn the signs better."
fatigue += 2
skill += 0.05
proc noEating() =
echo "Better not chance taking a bite at this time."
const critter = ["A small herbivore", "Some large rodent", "A flightless bird"]
const action = [" hears your steps and bolts.", " stumbles out of the bushes."]
proc shootGame()
proc chaseGame()
proc ignoreGame()
proc huntGame() =
echo critter.sample, action.sample
case menu("Shoot it", "Run after it", "Just move on")
of 0: leave = true
of 1: shootGame()
of 2: chaseGame()
of 3: ignoreGame()
else: raiseAssert("Logic error.")
proc eatGame()
proc shootGame() =
if bullets < 1:
echo "Click! Click! No more bullets..."
echo "The lucky creature soon vanishes."
else:
bullets -= 1
echo "You carefully take aim and... BANG!"
eatGame()
proc chaseGame() =
if fatigue >= health:
echo "You're too tired to give chase."
elif rand(1.0) < skill:
fatigue += 1
echo "You hunt it down and catch it."
eatGame()
else:
fatigue += 1
skill += 0.05
echo "You chase after it, but it's too fast."
echo "At least you learn new tricks."
proc eatGame() =
hours += 1
health += 2
if health > 5:
health = 5
echo "Poor critter is tasty roasted on a tiny fire."
echo "You recover much of your strength."
proc ignore_game() =
echo "You decide against playing hunter right now."
proc shootMonkeys()
proc scareMonkeys()
proc runAway()
proc fightMonkeys() =
echo "Screaming monkeys come out of nowhere to harass you!"
case menu("Shoot at them", "Look scary", "Run away")
of 0: leave = true
of 1: shootMonkeys()
of 2: scareMonkeys()
of 3: runAway()
else: raiseAssert("Logic error.")
proc getMauled()
proc shootMonkeys() =
if bullets < 1:
echo "Click! Click! No more bullets..."
getMauled()
else:
bullets -= 1
echo "BANG! Your bullet goes crashing through the foliage."
echo "The monkeys scatter, shrieking even more loudly."
proc scareMonkeys() =
echo "You shout and wave a branch, trying to look bigger."
if rand(1.0) < skill:
echo "The monkeys laugh mockingly at you as they scatter."
else:
skill += 0.05
echo "It doesn't seem to be working very well at all."
getMauled()
proc getMauled() =
health -= 2
echo "A rain of kicks and bites descends upon you!"
echo "At long last, the monkeys scatter, shrieking."
proc runAway() =
hours += 1
fatigue += 1 # Should be less bad than what we're risking.
echo "You run away blindly, until your lungs burn."
echo "The chorus of shrieks slowly remains behind."
const crawlie = ["giant centipede", "big hairy spider", "colorful snake"]
proc removeCrawlie()
proc waitOutCrawlie()
proc itsVenomous() =
echo "A ", crawlie.sample, " falls on you from above!"
case menu("Remove it carefully", "Stand still")
of 0: leave = true
of 1: removeCrawlie()
of 2: waitOutCrawlie()
else: raiseAssert("Logic error.")
proc removeCrawlie() =
if rand(1.0) < skill:
echo "The crawlie wriggles wetly in your grasp. Yuck!"
else:
skill += 0.05
health -= 1.5
echo "You carefully try to pick up the crawlie, but... OW!"
echo "It bites! You're poisoned. Burns pretty badly, too."
echo "At least it's gone now. Hopefully."
proc waitOutCrawlie() =
hours += 1
fatigue += 1 # Should be less bad than what we're risking.
echo "You wait tensely for what seems like hours."
echo "In the end, it's gone, and you're sweating."
proc restAtRuins()
proc searchRuins()
proc leaveRuins()
proc findRuins() =
echo "You discover ancient ruins..."
case menu("Rest here", "Search the place", "Just move on")
of 0: leave = true
of 1: restAtRuins()
of 2: searchRuins()
of 3: leaveRuins()
else: raiseAssert("Logic error.")
proc restAtRuins() =
fatigue -= 1
if fatigue < 0:
fatigue = 0
health += 1
if health > 5:
health = 5
hours += 2
echo "You sleep undisturbed for once, before moving on."
proc searchRuins() =
hours += 1
let chance = rand(1.0)
if chance < 0.3:
skill += 0.05
echo "You find old inscriptions teaching about the jungle."
elif chance < 0.6:
echo "You find gold and diamonds. Not much use right now."
else:
echo "You find nothing of interest this time around."
proc leaveRuins() =
hours += 1
fatigue += 1
distance -= 3 # Not too much, because it's for free.
echo "You march on, emboldened, covering a good distance."
proc crossSwamp()
proc avoidSwamp()
proc reachSwamp() =
echo "A vast swamp bars your way."
case menu("Risk a crossing", "Go around it")
of 0: leave = true
of 1: crossSwamp()
of 2: avoidSwamp()
else: raiseAssert("Logic error.")
proc crossSwamp() =
if rand(1.0) < skill:
echo "Somehow you navigate the maze more or less safely."
else:
# Probably too harsh since you get tired either way.
# fatigue++;
health -= 1
skill += 0.05
echo "Mud pulls at your feet, and you nearly drown once."
echo "Mosquitos besiege you; their bites make you ill."
hours += 1
fatigue += 1
distance -= 5
echo "It's a scary shortcut to take, but it saves a lot of travel."
proc avoidSwamp() =
fatigue += 1.5 # Should be bad, but not too bad.
hours += 2
echo "A long, tiresome detour. Safe, but no closer to your goal."
proc shootPlant()
proc wrestlePlant()
proc cutPlant()
proc triggerPlant() =
echo "Creeping vines entangle your limbs and drag you down."
echo "Oh no! It's a man-eating mandragore, and it's hungry!"
case menu("Shoot it", "Wrestle free", "Cut the vines")
of 0: leave = true
of 1: shootPlant()
of 2: wrestlePlant()
of 3: cutPlant()
else: raiseAssert("Logic error.")
proc getChewedOn()
proc shootPlant() =
if bullets < 1:
echo "Click! Click! No more bullets..."
getChewedOn()
else:
bullets -= 1
echo "BANG! You hit the plant's smelly flower dead center."
echo "It wilts away with a horrible squelching sound."
proc wrestlePlant() =
let energy = (health - fatigue) * 2 / 10
if rand(1.0) < energy:
echo "You vigorously pull at the vines, breaking a few."
echo "The plant soon decides to wait for easier prey."
else:
echo "You pull tiredly at the vines. It's not enough."
getChewedOn()
fatigue += 1
proc cutPlant() =
fatigue += 1
if rand(1.0) < skill:
echo "You expertly hack at the vines with your knife."
echo "The plant soon decides to wait for easier prey."
else:
skill += 0.05
echo "You clumsily hack at the vines with your knife."
getChewedOn()
proc getChewedOn() =
health = health - 1
echo "The plants chews on you with its toothless maw,",
" burning you with digestive juices before you escape."
const encounters = [
find_water, find_fruit, hunt_game, fight_monkeys,
its_venomous, find_ruins, reach_swamp, trigger_plant]
proc pickEncounter(): proc() =
for i, ch in chances:
if rand(1.0) < chances[i]:
chances[i] /= 5
return encounters[i]
else:
chances[i] += 0.05
return noEncounter
proc setBodyTextStyle() =
resetAttributes()
if color:
setForegroundColor fgMagenta, true
setBackgroundColor bgBlack
proc playGame() =
while health > 0 and distance > 0:
let (status1, status2) = status()
drawStatus status1, status2
setBodyTextStyle()
echo ""
let encounter = pickEncounter()
encounter()
if leave: break
echo "\n(press any key)"
discard getch()
echo()
if health <= 0:
echo "You died in the jungle, after ",
hours, " hours of struggle."
echo "No more than ", distance, "km away from safety."
if bullets == 6:
echo "Without as much as firing a single bullet."
echo "Oh well, better luck next time."
elif distance <= 0:
echo "At last, the trees open up. You see a village. Saved!"
echo "Unless it's a hostile tribe? Just kidding. You win!"
echo "(In ", hours, " hours, with ",
bullets, " bullets left.)"
else:
echo "Game interrupted."
echo "\n(press any key)"
discard getch()
const help = """
The point of the game is to cross the 50Km or so separating you from safety.
The exact distance varies every time you play.
You advance towards the goal by marching on whenever you get the chance.
But you have to balance your health and fatigue.
The worse your health is, the more easily you get tired. It's not possible
to die from exhaustion though.
Being tired all the time will hold you up, allowing more dangers to catch up
with you and sap your health.
Hope this helps. Enjoy!
"""
proc showHelp() =
drawStatus "Lost in the Jungle", "How to play"
setBodyTextStyle()
echo "\n", help
if color:
setForegroundColor(fgWhite)
else:
resetAttributes()
echo "(press any key)"
discard getch()
const intro = """
You survived the plane crash.
With all your gear intact, too:
Gun, knife, compass, lighter.
But you have no food or water.
And a big bad jungle to cross.
"""
while true:
drawStatus "Lost in the Jungle", "A game by No Time To Play"
setBodyTextStyle()
echo "\n", intro
case menu("Play", "Resume", "Color", "Help", "Quit")
of 0: echo "\nSee you around!"; break
of 1: leave = false; startGame(); playGame()
of 2: leave = false; playGame()
of 3: color = not color
of 4: showHelp()
of 5: echo "\nSee you around!"; break
else: raiseAssert "Logic error."