package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "os" "github.com/fatih/color" "gitlab.com/EternalWanderer/dice-roller/Dice" ) var ( path, skillString, saveString, statString string exampleFile string = "/etc/sheet-parser/example.json" 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 ) func parseFlags() { flag.StringVar(&path, "file", exampleFile, "Used to point to character sheet") flag.StringVar(&path, "f", exampleFile, "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") flag.BoolVar(&advantage, "advantage", advantage, "Roll with advantage") flag.BoolVar(&disadvantage, "disadvantage", disadvantage, "Roll with disadvantage") 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") 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") flag.BoolVar(&trivia, "t", false, "Print character name, level and proficiency") flag.BoolVar(&trivia, "trivia", false, "Print character name, level and proficiency") flag.Parse() } func isError(err error) bool { if err != nil { fmt.Println(err.Error()) } return (err != nil) } func readJson() { fmt.Println("Opening file:", path) var file, err = os.Open(path) if isError(err) { return } defer file.Close() byteValue, _ := ioutil.ReadAll(file) json.Unmarshal(byteValue, &char) } 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 } } func main() { parseFlags() readJson() initMaps() switch { case trivia: fmt.Printf("Name: %s\tLevel: %d\tProficiency: %d\n", char.Misc.Name, char.Misc.Level, getProficiency()) fmt.Printf("Race: %s\tClass: %s\tBackground: %s\n", char.Misc.Race, char.Misc.Class, char.Misc.Background) case stat_list && 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 fmt.Printf("Stat: %s\t%s\tStat score: %d\tStat modifier: %d\n", name, proficiency, score, getModifier(char.Stats[i])) } case skill_list && verbose: var proficiency string var expertise string for i := 0; i < len(char.Skills); i++ { name := char.Skills[i].SkillName localModifier := getModifier(getStat(char.Skills[i].BaseStat)) if char.Skills[i].Proficient { proficiency = "Proficient" localModifier += getProficiency() } else { proficiency = "Not proficient" } if char.Skills[i].Expertise { expertise = "Has expertise" localModifier += getProficiency() } else { expertise = "Doesn't have expertise" } fmt.Printf("Skill: %s\tSkill modifier: %d\t%s\t%s\n", name, localModifier, proficiency, expertise) } case stat_list: for i := 0; i < len(char.Stats); i++ { fmt.Println(char.Stats[i].StatName) } case skill_list: for i := 0; i < len(char.Skills); i++ { fmt.Println(char.Skills[i].SkillName) } case advantage && disadvantage: fmt.Println("You can't roll with both advantage and disadvantage") os.Exit(1) case len(saveString) > 0: result := savingThrow(getStat(saveString)) if advantage { color.Yellow("Rolling %s saving throw with advantage...", saveString) fmt.Printf("x: %d\ty: %d\n", X, Y) 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) color.Red("%d\n", result) } else { color.Yellow("Rolling %s saving throw...", saveString) color.Green("%d\n", result) } case len(skillString) > 0: result := skillCheck(getSkill(skillString)) if advantage { color.Yellow("Rolling %s saving throw with advantage...", skillString) fmt.Printf("x: %d\ty: %d\n", X, Y) color.Green("%d\n", result) } else if disadvantage { color.Yellow("Rolling %s saving throw with disadvantage...", skillString) fmt.Printf("x: %d\ty: %d\n", X, Y) color.Red("%d\n", result) } else { color.Yellow("Rolling %s check...", skillString) color.Green("%d\n", result) } case len(statString) > 0: result := statCheck(getStat(statString)) if advantage { color.Yellow("Rolling %s check with advantage...", statString) fmt.Printf("x: %d\ty: %d\n", X, Y) 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) color.Red("%d\n", result) } else { color.Yellow("Rolling %s check...", statString) color.Green("%d\n", result) } default: flag.Usage() } } func getProficiency() int { // https://worldbuildersjunction.com/what-is-proficiency-bonus-in-dd-5e-how-it-works-calculated/ return (char.Misc.Level-1)/4 + 2 } func getSkill(skillName string) Skill { return char.Skills[skillMap[skillName]] } func getStat(statName string) Stat { return char.Stats[statMap[statName]] } func getModifier(stat Stat) int { // https://worldbuildersjunction.com/dungeon-and-dragons-ability-scores-explained-for-beginners/ return (stat.Score - 10) / 2 } func rollDice() int { var die int switch { case advantage: die, X, Y = Dice.Advantage() case disadvantage: die, X, Y = Dice.Disadvantage() default: die = Dice.SimpleCast() } switch die { case 20: color.Magenta("Natural 20!\n") case 1: color.Magenta("Natural 1!\n") } return die } func statCheck(stat Stat) int { return rollDice() + getModifier(stat) } func savingThrow(stat Stat) int { die := rollDice() if stat.Proficient { die += getProficiency() } return die + getModifier(stat) } func skillCheck(skill Skill) int { var die int die = rollDice() die += getModifier(getStat(skill.BaseStat)) switch { case skill.Expertise: die += getProficiency() * 2 case skill.Proficient: die += getProficiency() } return die }