sheet-parser/main.go

356 lines
10 KiB
Go
Raw Normal View History

2022-08-19 18:09:05 +02:00
package main
import (
"encoding/json"
"errors"
2022-08-19 18:09:05 +02:00
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
2022-08-19 18:09:05 +02:00
"github.com/fatih/color"
2022-08-19 18:09:05 +02:00
"gitlab.com/EternalWanderer/dice-roller/Dice"
)
2022-08-28 15:45:47 +02:00
const exampleFile string = "/etc/sheet-parser/example.json"
2022-08-19 18:09:05 +02:00
var (
2022-08-28 15:45:47 +02:00
path, pathFlag, skillString, saveString, statString string
2022-08-22 15:17:58 +02:00
modifier, diceThrows, surfaces int
X, Y int
char Character
skillMap = make(map[string]int)
statMap = make(map[string]int)
advantage, disadvantage, stat_list, skill_list, verbose, trivia bool
2022-08-19 18:09:05 +02:00
)
func parseFlags() {
2022-08-28 15:45:47 +02:00
flag.StringVar(&pathFlag, "file", pathFlag, "Used to point to character sheet")
flag.StringVar(&pathFlag, "f", pathFlag, "Used to point to character sheet")
flag.StringVar(&skillString, "skill", "", "Skill to parse")
flag.StringVar(&statString, "stat", "", "Stat check")
flag.StringVar(&saveString, "save", "", "Saving throw to... throw")
2022-08-20 22:13:12 +02:00
2022-08-20 16:32:50 +02:00
flag.BoolVar(&advantage, "advantage", advantage, "Roll with advantage")
flag.BoolVar(&disadvantage, "disadvantage", disadvantage, "Roll with disadvantage")
2022-08-20 22:13:12 +02:00
flag.BoolVar(&stat_list, "stat-list", false, "Print list of stats, can also be used with -save flag")
flag.BoolVar(&skill_list, "skill-list", false, "Print list of skills to be used in combination with the -skill flag")
2022-08-21 23:07:43 +02:00
flag.BoolVar(&verbose, "verbose", false, "Print stat numbers, to be used in combination with -stat-list or -skill-list")
flag.BoolVar(&verbose, "v", false, "Print stat numbers, to be used in combination with -stat-list or -skill-list")
2022-08-20 22:13:12 +02:00
2022-08-22 15:17:58 +02:00
flag.BoolVar(&trivia, "t", false, "Print character name, level and proficiency")
flag.BoolVar(&trivia, "trivia", false, "Print character name, level and proficiency")
2022-08-19 18:09:05 +02:00
flag.Parse()
}
2022-09-01 12:41:48 +02:00
func handleError(err error) {
2022-08-20 17:07:03 +02:00
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
2022-08-20 17:07:03 +02:00
}
}
2022-08-19 18:09:05 +02:00
func readJson() {
2022-08-28 15:45:47 +02:00
envPath, isSet := os.LookupEnv("CHARSHEET")
switch {
2022-08-28 16:45:30 +02:00
case len(pathFlag) > 0:
2022-08-28 15:45:47 +02:00
path = pathFlag
case isSet:
path = envPath
default:
path = exampleFile
}
2022-08-20 16:32:50 +02:00
fmt.Println("Opening file:", path)
2022-08-19 18:09:05 +02:00
var file, err = os.Open(path)
2022-09-01 12:41:48 +02:00
handleError(err)
2022-08-19 18:09:05 +02:00
defer file.Close()
byteValue, _ := ioutil.ReadAll(file)
2022-09-01 12:41:48 +02:00
err = json.Unmarshal(byteValue, &char)
handleError(err)
2022-08-19 18:09:05 +02:00
}
2022-08-20 16:32:50 +02:00
func initMaps() {
for i := 0; i < len(char.Skills); i++ {
skillMap[char.Skills[i].SkillName] = i
}
for i := 0; i < len(char.Stats); i++ {
statMap[char.Stats[i].StatName] = i
}
}
2022-08-19 18:09:05 +02:00
func main() {
parseFlags()
readJson()
2022-08-20 16:32:50 +02:00
initMaps()
switch {
2022-08-21 23:07:43 +02:00
2022-08-22 15:17:58 +02:00
case trivia:
2022-12-29 17:46:23 +01:00
fmt.Printf("Name: %s\tLevel: %d\tProficiency: %d\n", char.Misc.Name, char.Misc.Level, GetProficiency())
2022-08-22 15:17:58 +02:00
fmt.Printf("Race: %s\tClass: %s\tBackground: %s\n", char.Misc.Race, char.Misc.Class, char.Misc.Background)
// passive perception
var passivePerception int
switch {
2022-12-29 17:46:23 +01:00
case GetSkill("perception").Proficient && GetSkill("perception").Expertise:
passivePerception = 10 + GetModifier(GetStat("wisdom")) + GetProficiency()*2
case GetSkill("perception").Proficient:
passivePerception = 10 + GetModifier(GetStat("wisdom")) + GetProficiency()
default:
2022-12-29 17:46:23 +01:00
passivePerception = 10 + GetModifier(GetStat("wisdom"))
}
fmt.Printf("Passive perception: %d\n", passivePerception)
2022-08-27 17:39:32 +02:00
case stat_list && skill_list:
printStatList(verbose)
printSkillList(verbose)
2022-08-20 22:13:12 +02:00
case stat_list:
2022-08-27 17:39:32 +02:00
printStatList(verbose)
2022-08-20 22:13:12 +02:00
case skill_list:
2022-08-27 17:39:32 +02:00
printSkillList(verbose)
2022-08-21 23:07:43 +02:00
case advantage && disadvantage:
fmt.Println("You can't roll with both advantage and disadvantage")
os.Exit(1)
case len(saveString) > 0:
2022-12-29 17:46:23 +01:00
result, plainResult, err := savingThrow(GetStat(saveString))
badCheck(saveString, err)
if advantage {
color.Yellow("Rolling %s saving throw with advantage...", saveString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Green("%d\n", result)
} else if disadvantage {
color.Yellow("Rolling %s saving throw with disadvantage...", saveString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Red("%d\n", result)
} else {
color.Yellow("Rolling %s saving throw...", saveString)
2022-09-22 17:24:48 +02:00
fmt.Printf("Without modifier: %d\tModifier: %d\n", plainResult, result-plainResult)
color.Green("%d\n", result)
}
case len(skillString) > 0 && char.Misc.IsKurthog && strings.Contains(skillString, "performance"):
2022-12-29 17:46:23 +01:00
result, plainResult, err := skillCheck(GetSkill(skillString))
badCheck(skillString, err)
if advantage {
color.Yellow("Rolling %s check with advantage...", skillString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
fmt.Printf("Modifier: %d\n", result-plainResult)
fmt.Printf("Kurthogifier: %d\n", -1)
color.Green("%d\n", result*-1)
} else if disadvantage {
color.Yellow("Rolling %s check with disadvantage...", skillString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
fmt.Printf("Modifier: %d\n", result-plainResult)
fmt.Printf("Kurthogifier: %d\n", -1)
color.Red("%d\n", result*-1)
} else {
color.Yellow("Rolling %s check...", skillString)
fmt.Printf("Without modifier: %d\tModifier: %d\n", plainResult, result-plainResult)
fmt.Printf("Kurthogifier: %d\n", -1)
color.Green("%d\n", result*-1)
}
case len(skillString) > 0:
2022-12-29 17:46:23 +01:00
result, plainResult, err := skillCheck(GetSkill(skillString))
badCheck(skillString, err)
if advantage {
2022-08-23 22:19:25 +02:00
color.Yellow("Rolling %s check with advantage...", skillString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Green("%d\n", result)
} else if disadvantage {
2022-08-23 22:19:25 +02:00
color.Yellow("Rolling %s check with disadvantage...", skillString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Red("%d\n", result)
} else {
color.Yellow("Rolling %s check...", skillString)
2022-09-22 17:24:48 +02:00
fmt.Printf("Without modifier: %d\tModifier: %d\n", plainResult, result-plainResult)
color.Green("%d\n", result)
}
case len(statString) > 0:
2022-12-29 17:46:23 +01:00
result, plainResult, err := statCheck(GetStat(statString))
badCheck(statString, err)
if advantage {
color.Yellow("Rolling %s check with advantage...", statString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Green("%d\n", result)
} else if disadvantage {
color.Yellow("Rolling %s check with disadvantage...", statString)
fmt.Printf("x: %d\ty: %d\n", X, Y)
2022-09-22 17:24:48 +02:00
fmt.Printf("Modifier: %d\n", result-plainResult)
color.Red("%d\n", result)
} else {
color.Yellow("Rolling %s check...", statString)
2022-09-22 17:24:48 +02:00
fmt.Printf("Without modifier: %d\tModifier: %d\n", plainResult, result-plainResult)
color.Green("%d\n", result)
}
default:
2022-08-20 22:13:12 +02:00
flag.Usage()
}
2022-08-20 17:56:01 +02:00
}
2022-12-29 17:46:23 +01:00
func GetProficiency() int {
// https://worldbuildersjunction.com/what-is-proficiency-bonus-in-dd-5e-how-it-works-calculated/
2022-08-20 17:56:01 +02:00
return (char.Misc.Level-1)/4 + 2
2022-08-20 16:32:50 +02:00
}
2022-12-29 17:46:23 +01:00
func GetSkill(skillName string) Skill {
2022-08-20 16:32:50 +02:00
return char.Skills[skillMap[skillName]]
}
2022-08-19 18:09:05 +02:00
2022-12-29 17:46:23 +01:00
func GetStat(statName string) Stat {
2022-08-20 16:32:50 +02:00
return char.Stats[statMap[statName]]
}
2022-12-29 17:46:23 +01:00
func GetModifier(stat Stat) int {
// https://worldbuildersjunction.com/dungeon-and-dragons-ability-scores-explained-for-beginners/
2022-08-20 16:44:47 +02:00
return (stat.Score - 10) / 2
}
2022-08-20 17:56:01 +02:00
func rollDice() int {
var die int
2022-08-20 17:56:01 +02:00
switch {
case advantage:
die, X, Y = Dice.Advantage()
2022-08-20 17:56:01 +02:00
case disadvantage:
die, X, Y = Dice.Disadvantage()
2022-08-20 17:56:01 +02:00
default:
die = Dice.SimpleCast()
}
switch die {
case 20:
color.Magenta("Natural 20!\n")
case 1:
color.Magenta("Natural 1!\n")
}
2022-08-20 17:56:01 +02:00
return die
}
func badCheck(errorMessage string, err error) {
if err == nil {
return
}
fmt.Println(err)
fmt.Println(errorMessage)
os.Exit(1)
}
func statErrorCheck() error {
for i := 0; i < len(char.Stats); i++ {
if statString == char.Stats[i].StatName || saveString == char.Stats[i].StatName {
return nil
}
}
return errors.New("Unknown stat:")
}
2022-08-20 17:56:01 +02:00
func skillErrorCheck() error {
for i := 0; i < len(char.Skills); i++ {
if skillString == char.Skills[i].SkillName {
return nil
}
}
return errors.New("Unknown skill:")
}
func statCheck(stat Stat) (int, int, error) {
die := rollDice()
2022-12-29 17:46:23 +01:00
return die + GetModifier(stat), die, statErrorCheck()
2022-08-20 17:56:01 +02:00
}
func savingThrow(stat Stat) (int, int, error) {
2022-08-20 17:56:01 +02:00
die := rollDice()
2022-09-22 17:24:48 +02:00
plainDie := die
2022-08-20 17:56:01 +02:00
if stat.Proficient {
2022-12-29 17:46:23 +01:00
die += GetProficiency()
2022-08-20 17:56:01 +02:00
}
2022-12-29 17:46:23 +01:00
return die + GetModifier(stat), plainDie, statErrorCheck()
2022-08-20 17:56:01 +02:00
}
func skillCheck(skill Skill) (int, int, error) {
2022-08-27 16:57:54 +02:00
die := rollDice()
2022-08-28 16:03:07 +02:00
if char.Misc.ReliableTalent && skill.Proficient && die < 10 {
die = 10
}
2022-09-22 17:24:48 +02:00
plainDie := die
2022-08-20 17:07:03 +02:00
2022-12-29 17:46:23 +01:00
die += GetModifier(GetStat(skill.BaseStat))
2022-08-20 17:07:03 +02:00
2022-08-20 16:32:50 +02:00
switch {
case skill.Expertise:
2022-12-29 17:46:23 +01:00
die += GetProficiency() * 2
2022-08-20 16:32:50 +02:00
case skill.Proficient:
2022-12-29 17:46:23 +01:00
die += GetProficiency()
2022-08-28 16:03:07 +02:00
case !skill.Proficient && char.Misc.JackOfAllTrades:
2022-12-29 17:46:23 +01:00
die += (GetProficiency() / 2)
2022-08-19 18:09:05 +02:00
}
2022-09-22 17:24:48 +02:00
return die, plainDie, skillErrorCheck()
2022-08-19 18:09:05 +02:00
}
2022-08-27 17:39:32 +02:00
func printStatList(verbose bool) {
color.Magenta("Listing stats...")
if verbose {
var proficiency string
for i := 0; i < len(char.Stats); i++ {
name := char.Stats[i].StatName
isProficient := char.Stats[i].Proficient
if isProficient {
proficiency = "Proficient"
} else {
proficiency = "Not proficient"
}
score := char.Stats[i].Score
2022-12-29 17:46:23 +01:00
fmt.Printf("Stat: %13s\t%s\tStat score: %2d\tStat modifier: %2d\n", name, proficiency, score, GetModifier(char.Stats[i]))
2022-08-27 17:39:32 +02:00
}
} else {
for i := 0; i < len(char.Stats); i++ {
fmt.Println(char.Stats[i].StatName)
}
}
}
func printSkillList(verbose bool) {
color.Magenta("Listing skills...")
var proficiency string
var expertise string
if verbose {
for i := 0; i < len(char.Skills); i++ {
name := char.Skills[i].SkillName
2022-12-29 17:46:23 +01:00
localModifier := GetModifier(GetStat(char.Skills[i].BaseStat))
2022-08-27 17:39:32 +02:00
if char.Skills[i].Proficient {
proficiency = "Proficient"
2022-12-29 17:46:23 +01:00
localModifier += GetProficiency()
2022-08-27 17:39:32 +02:00
} else {
proficiency = "Not proficient"
}
if char.Skills[i].Expertise {
expertise = "Has expertise"
2022-12-29 17:46:23 +01:00
localModifier += GetProficiency()
2022-08-27 17:39:32 +02:00
} else {
expertise = "Doesn't have expertise"
}
fmt.Printf("Skill:%16s\tSkill modifier:%3d\t%s\t%s\n", name, localModifier, proficiency, expertise)
2022-08-27 17:39:32 +02:00
}
} else {
for i := 0; i < len(char.Skills); i++ {
fmt.Println(char.Skills[i].SkillName)
}
}
}