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()