290 lines
7.2 KiB
Nim
290 lines
7.2 KiB
Nim
|
import std/[json, jsonutils, os, parseopt, random, strutils, unicode]
|
||
|
|
||
|
|
||
|
type
|
||
|
GiraffeNames = tuple
|
||
|
adjs: seq[string]
|
||
|
names: seq[string]
|
||
|
nouns: seq[string]
|
||
|
|
||
|
Entities = tuple
|
||
|
names: seq[string]
|
||
|
statuses: seq[string]
|
||
|
comps: seq[string]
|
||
|
|
||
|
Trees = tuple
|
||
|
names: seq[string]
|
||
|
maxLeaves: int
|
||
|
|
||
|
Verbs = tuple
|
||
|
ents: seq[string]
|
||
|
trees: seq[string]
|
||
|
eats: seq[string]
|
||
|
|
||
|
Data = tuple
|
||
|
giraffeNames: GiraffeNames
|
||
|
entities: Entities
|
||
|
trees: Trees
|
||
|
verbs: Verbs
|
||
|
|
||
|
Chara = tuple
|
||
|
giraffe: int
|
||
|
leaves: int
|
||
|
hasEntEnc: bool
|
||
|
lastEnc: string
|
||
|
gameEnded: bool
|
||
|
|
||
|
Outcome = tuple
|
||
|
eval: string
|
||
|
like: string
|
||
|
evolve: int
|
||
|
|
||
|
|
||
|
const
|
||
|
env = (
|
||
|
modName: "giraffe",
|
||
|
modVer: "0.1",
|
||
|
modDesc: "a minigame based on d o z e n s ' giraffe ttrpg",
|
||
|
our: (on: true, pathData: "/town/our/data/"),
|
||
|
fileData: "giraffeData.json",
|
||
|
fileChara: "giraffe.json"
|
||
|
)
|
||
|
|
||
|
|
||
|
var
|
||
|
chara: Chara
|
||
|
data: Data
|
||
|
|
||
|
|
||
|
proc resetChara(): void =
|
||
|
chara = (
|
||
|
giraffe: 3,
|
||
|
leaves: 0,
|
||
|
hasEntEnc: false,
|
||
|
lastEnc: "",
|
||
|
gameEnded: false
|
||
|
)
|
||
|
|
||
|
|
||
|
proc saveChara(): void =
|
||
|
var node = pretty(toJson(chara))
|
||
|
if env.our.on:
|
||
|
writeFile(unixToNativePath(env.our.pathData & env.fileChara), node)
|
||
|
else:
|
||
|
writeFile(unixToNativePath(env.fileChara), node)
|
||
|
|
||
|
|
||
|
proc loadData(): void =
|
||
|
if env.our.on:
|
||
|
data = to(parseFile(unixToNativePath(env.our.pathData & env.fileData)),
|
||
|
Data)
|
||
|
if not fileExists(unixToNativePath(env.our.pathData & env.fileChara)):
|
||
|
resetChara()
|
||
|
saveChara()
|
||
|
else:
|
||
|
chara = to(parseFile(unixToNativePath(env.our.pathData & env.fileChara)),
|
||
|
Chara)
|
||
|
else:
|
||
|
data = to(parseFile(unixToNativePath(env.fileData)), Data)
|
||
|
if fileExists(unixToNativePath(env.fileChara)):
|
||
|
chara = to(parseFile(unixToNativePath(env.fileChara)), Chara)
|
||
|
else:
|
||
|
resetChara()
|
||
|
saveChara()
|
||
|
|
||
|
|
||
|
proc getGiraffe(showStatus = false): string =
|
||
|
var gStatus: string
|
||
|
if showStatus:
|
||
|
gStatus = " (giraffe)"
|
||
|
if chara.giraffe == 1:
|
||
|
gStatus = " (human)"
|
||
|
return "GIRAFFE: " & intToStr(chara.giraffe) & gStatus
|
||
|
|
||
|
|
||
|
proc randName(): string =
|
||
|
randomize()
|
||
|
return join(@[
|
||
|
capitalize(sample(data.giraffeNames.adjs)), " ",
|
||
|
capitalize(sample(data.giraffeNames.names)), " ",
|
||
|
capitalize(sample(data.giraffeNames.adjs)), " ",
|
||
|
capitalize(sample(data.giraffeNames.nouns))
|
||
|
])
|
||
|
|
||
|
|
||
|
proc randEncounter(ty = "any"): string =
|
||
|
randomize()
|
||
|
var
|
||
|
encTy = ty
|
||
|
roll = rand(1..100)
|
||
|
if ty == "any":
|
||
|
case roll
|
||
|
# Probability of showing yearning message: 5%
|
||
|
of 1, 10, 50, 90, 100:
|
||
|
encTy = "tree"
|
||
|
else:
|
||
|
encTy = "ent"
|
||
|
case encTy
|
||
|
# Meet entities
|
||
|
of "ent":
|
||
|
chara.hasEntEnc = true
|
||
|
chara.lastEnc = join(["You ", sample(data.verbs.ents), " ",
|
||
|
sample(data.entities.names), " ", sample(data.entities.statuses),
|
||
|
sample(data.entities.comps)])
|
||
|
return join([chara.lastEnc, getGiraffe()], " ")
|
||
|
# Get/eat leaves
|
||
|
else:
|
||
|
if chara.giraffe == 6:
|
||
|
var
|
||
|
leaves = rand(1..data.trees.maxLeaves)
|
||
|
leavesEaten = sample([true, false])
|
||
|
leavesDiff = ""
|
||
|
# Don't eat leaves if there are none
|
||
|
if leavesEaten and chara.leaves == 0:
|
||
|
leavesEaten = false
|
||
|
# Eat leaves
|
||
|
if leavesEaten:
|
||
|
if chara.leaves >= leaves:
|
||
|
chara.leaves -= leaves
|
||
|
leavesDiff = join([" (-", intToStr(leaves), ")"])
|
||
|
return join(["You ", sample(data.verbs.eats),
|
||
|
" some of the best leaves. Yum! Leaves: ", intToStr(chara.leaves),
|
||
|
leavesDiff])
|
||
|
else:
|
||
|
chara.leaves = 0
|
||
|
return join(["You ", sample(data.verbs.eats),
|
||
|
" all the best leaves. Yum! Leaves: ", intToStr(chara.leaves)])
|
||
|
else:
|
||
|
chara.leaves += leaves
|
||
|
return join(["You ", sample(data.verbs.trees), " ",
|
||
|
sample(data.trees.names), ". Leaves: ", intToStr(chara.leaves),
|
||
|
" (+", intToStr(leaves), ")"])
|
||
|
else:
|
||
|
return join(["You ", sample(data.verbs.trees), " ",
|
||
|
sample(data.trees.names), " with the best leaves, but your neck ",
|
||
|
"is too short and you cannot reach them. Sorrow! Hunger! Yearning!"])
|
||
|
|
||
|
|
||
|
proc randOutcome(): Outcome =
|
||
|
randomize()
|
||
|
var
|
||
|
roll = rand(1..6)
|
||
|
eval = "a failure."
|
||
|
evolve = 0
|
||
|
isLikeGir = sample([true, false])
|
||
|
like = "giraffe"
|
||
|
if not isLikeGir:
|
||
|
like = "human"
|
||
|
if roll == chara.giraffe:
|
||
|
eval = "… GIRAFFE?!! You have an epiphany."
|
||
|
elif (isLikeGir and roll == 1) or
|
||
|
(not isLikeGir and roll == 6):
|
||
|
eval = "an exceptional success! (+1 G)"
|
||
|
evolve = 1
|
||
|
elif (isLikeGir and roll < chara.giraffe) or
|
||
|
(not isLikeGir and roll > chara.giraffe):
|
||
|
eval = "a success."
|
||
|
elif (isLikeGir and roll == 6) or
|
||
|
(not isLikeGir and roll == 1):
|
||
|
eval = "an exceptional failure! (-1 G)"
|
||
|
evolve = -1
|
||
|
return (eval: eval, like: like, evolve: evolve)
|
||
|
|
||
|
|
||
|
proc doOutcome(action: string): string =
|
||
|
if chara.hasEntEnc:
|
||
|
var oc = randOutcome()
|
||
|
chara.giraffe += oc.evolve
|
||
|
chara.hasEntEnc = false
|
||
|
chara.lastEnc = ""
|
||
|
return join(["You ", action, " like a ", oc.like, ". It was ", oc.eval,
|
||
|
" ", getGiraffe()])
|
||
|
else:
|
||
|
return "You haven't encountered anyone yet. " & getGiraffe()
|
||
|
|
||
|
|
||
|
proc endGame(): string =
|
||
|
if chara.giraffe == 6:
|
||
|
chara.gameEnded = true
|
||
|
return "You evolve into the platonic ideal of a giraffe. " &
|
||
|
"Your neck is so long. You can easily reach all the best leaves."
|
||
|
elif chara.giraffe == 1:
|
||
|
chara.gameEnded = true
|
||
|
return "You devolve into a miserable human. " &
|
||
|
"You never get the best leaves. Instead you have to go get a job and " &
|
||
|
"work for the rest of your life."
|
||
|
|
||
|
|
||
|
proc handleLook(): string =
|
||
|
# Remind of existing encounter
|
||
|
if not chara.gameEnded and chara.hasEntEnc:
|
||
|
return join([chara.lastEnc, getGiraffe()], " ")
|
||
|
# Show new encounter
|
||
|
elif not chara.gameEnded and not chara.hasEntEnc:
|
||
|
return randEncounter()
|
||
|
# Get best leaves
|
||
|
else:
|
||
|
return randEncounter("tree")
|
||
|
|
||
|
|
||
|
proc run(): void =
|
||
|
var
|
||
|
p = initOptParser(quoteShellCommand(commandLineParams()))
|
||
|
args: seq[string]
|
||
|
|
||
|
for kind, k, _ in p.getopt():
|
||
|
case kind
|
||
|
of cmdArgument:
|
||
|
add(args, k)
|
||
|
of cmdShortOption, cmdLongOption, cmdEnd:
|
||
|
discard
|
||
|
|
||
|
# When running within `our` bot, the args sequence received is
|
||
|
# @[input, nick!username@cloak, channel], with input being the string to
|
||
|
# parse. Reset the args sequence.
|
||
|
if env.our.on:
|
||
|
var input = args[0]
|
||
|
args = unicode.split(input)
|
||
|
|
||
|
# Command run without args
|
||
|
if len(args) == 0:
|
||
|
loadData()
|
||
|
# Show ending only once per game session
|
||
|
if (chara.giraffe == 6 or chara.giraffe == 1) and not chara.gameEnded:
|
||
|
echo endGame()
|
||
|
else:
|
||
|
echo handleLook()
|
||
|
saveChara()
|
||
|
|
||
|
# Command run with args
|
||
|
elif len(args) == 1:
|
||
|
case args[0]
|
||
|
of "g", "giraffe":
|
||
|
loadData()
|
||
|
echo getGiraffe(true)
|
||
|
|
||
|
of "n", "name", "nick":
|
||
|
loadData()
|
||
|
echo randName()
|
||
|
|
||
|
of "r", "reset":
|
||
|
resetChara()
|
||
|
saveChara()
|
||
|
echo "Game reset."
|
||
|
|
||
|
of "h", "help", "halp":
|
||
|
echo "Options: get a giraffe [n]ame, show this [h]elp message," &
|
||
|
" [r]eset, [v]ersion"
|
||
|
|
||
|
of "v", "version":
|
||
|
echo join([env.modName, " ", env.modVer, " ~ ", env.modDesc])
|
||
|
|
||
|
else:
|
||
|
loadData()
|
||
|
if not chara.gameEnded:
|
||
|
echo doOutcome(join(args[0..^1], " "))
|
||
|
saveChara()
|
||
|
|
||
|
|
||
|
run()
|