package main
import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)
// Where the output will be stored
const filename = "CA.txt"
// getNextID reads existing data from the file and returns the next NPC ID
func getNextID() int {
	file, err := os.Open(filename)
	if err != nil {
		// No file exists, ID starts from 2
		return 2
	}
	defer file.Close()
	maxID := 0
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || !strings.HasPrefix(line, "[ca/:") {
			continue
		}
		line = strings.TrimPrefix(line, "[ca/:")
		parts := strings.Split(line, ",")
		if len(parts) == 0 {
			continue
		}
		idStr := parts[0]
		id, err := strconv.Atoi(idStr)
		if err == nil && id > maxID {
			maxID = id
		}
	}
	return maxID + 1
}
// C represents a color with R, G, B as int and A as float64
type C struct {
	R, G, B int
	A       float64
}
// rC generates a random color with RGBA channels within the specified ranges
func rC(rMin, rMax, gMin, gMax, bMin, bMax int, aMin, aMax float64) C {
	return C{
	R: rand.Intn(rMax-rMin+1) + rMin,
	G: rand.Intn(gMax-gMin+1) + gMin,
	B: rand.Intn(bMax-bMin+1) + bMin,
	A: aMin + rand.Float64()*(aMax-aMin),
	}
}
// Predefined color palettes: Morandi, Macaron, and none (transparent)
var (
	moC = rC(0, 255, 0, 255, 0, 255, 0.25, 0.5)
	maC = rC(0, 255, 0, 255, 0, 255, 1.0, 1.0)
	nC  = C{0, 0, 0, 0}
)
// habCeCtCwsbCwspCaCInts2Intf converts color struct to []interface{} containing R, G, B, and A*255 rounded
func habCeCtCwsbCwspCaCInts2Intf(c C) []interface{} {
	return []interface{}{c.R, c.G, c.B, c.A}
}
// dbyS determines body shape based on firstsexgenes, ((1,1),(2,2),others) = (op 1-3,op 7-9,op 4-6)
func dbyS(fsg1, fsg2 int) int {
	switch {
	case fsg1 == 1 && fsg2 == 1:
		return rand.Intn(3) + 1
	case fsg1 == 2 && fsg2 == 2:
		return rand.Intn(3) + 7
	default:
		return rand.Intn(3) + 4
	}
}
// dtlSthSwSaS determines tail, tooth, wing, antenna shape based on firstsexgenes (op 1-100, op 404 = no shape)
func dtlSthSwSaS(fsg1, fsg2, maxID int) (int, int, int, int) {
	switch {
	case fsg1 == 1 && fsg2 == 1:
		return maxID, maxID, 404, 404
	case fsg1 == 2 && fsg2 == 2:
		return 404, 404, 404, maxID
	default:
		return 404, 404, maxID, maxID
	}
}
// rBarP generates random bloodlineIDs and random percentage, with one random bloodlineID assigned 100% (1.0), rest 0%
func rBarP() ([]int, []float64) {
	t := 100
	rB := make([]int, t)
	rP := make([]float64, t)
	for i := range rB {
		rB[i] = i + 1
	}
	// Pick one random bloodlineID to have percentage 1
	hBID := rand.Intn(t)
	rP[hBID] = 1.0
	return rB, rP
}
// fhBID finds the bloodlineID with the highest percentage
func fhBID(rB []int, rP []float64) int {
	hBID := 0
	for i := 1; i < len(rP); i++ {
		if rP[i] > rP[hBID] {
			hBID = i
		}
	}
	return rB[hBID]
}
// gNPC generates a random NPC with all attributes
func gNPC() []interface{} {
	// Get the next sequential NPC ID
	playerID := getNextID()
	// Firstsexgene1, firstsexgene2 (op 1-2); secondsexgene1, secondsexgene2 (op 1-3)
	fsg1, fsg2 := rand.Intn(2)+1, rand.Intn(2)+1
	ssg1, ssg2 := rand.Intn(3)+1, rand.Intn(3)+1
	// Skin colors (predefined realistic RGBA range)
	sC := []int{rand.Intn(56) + 200, rand.Intn(51) + 100, rand.Intn(51) + 80, 255}
	// Determine color palettes for hairandbrow, eye, tail, wingsub, wingstripe and antenna color based on firstsexgenes combination
	var habC, eC, tC, wsbC, wspC, aC C
	switch {
		case fsg1 == 1 && fsg2 == 1:
			habC, eC, tC = moC, moC, moC
			wsbC, wspC, aC = nC, nC, nC
		case fsg1 == 2 && fsg2 == 2:
			habC, eC = moC, moC
			tC, wsbC, wspC = nC, nC, nC
			aC = maC
		default:
			habC, eC, wsbC, wspC, aC = maC, maC, maC, maC, maC
			tC = nC
	}
	// Random shape attributes for face, brow, eye, ear, nose, mouth, fronthair, backhair, body, tail, tooth, wing and antenna shape (op 1-10)
	rS := func() int { return rand.Intn(10) + 1 }
	fS, bwS, eeS, erS, nS, mS, fhS, bhS := rS(), rS(), rS(), rS(), rS(), rS(), rS(), rS()
	byS := dbyS(fsg1, fsg2)
	// Random bloodlineIDs and bloodline percentages
	BID, BP := rBarP()
	maxBID := fhBID(BID, BP)
	// Random shape attributes for tail, tooth, wing and antenna shape based on firstsexgenes combination
	tlS, thS, wS, aS := dtlSthSwSaS(fsg1, fsg2, maxBID)
	// Prepare output slice with preallocated capacity
	outLen := 5 + len(sC) + 6*4 + 10 + 2*len(BID) + 1
	output := make([]interface{}, 0, outLen)
	output = append(output, playerID, fsg1, fsg2, ssg1, ssg2)
	for _, v := range sC {
		output = append(output, v)
	}
	for _, c := range []C{habC, eC, tC, wsbC, wspC, aC} {
		output = append(output, habCeCtCwsbCwspCaCInts2Intf(c)...)
	}
	output = append(output, fS, bwS, eeS, erS, nS, mS, fhS, bhS, byS, tlS, thS, wS, aS)
	// Append bloodline percentages (float64)
	for _, p := range BP {
		output = append(output, p)
	}
	// Append maxBloodlineID
	output = append(output, maxBID)
	return output
}
// formatOutput formats the NPC data in the expected string format
func formatOutput(data []interface{}) string {
	parts := make([]string, len(data))
	for i, v := range data {
		switch val := v.(type) {
		case float64:
			// Format floats with one decimal place
			parts[i] = fmt.Sprintf("%.1f", val)
		default:
			parts[i] = fmt.Sprint(v)
		}
	}
	return "[ca/:" + strings.Join(parts, ",") + "]"
}
// appendToFile appends the string to the output file (creates it if not exist)
func appendToFile(text string) {
	f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer f.Close()
	_, err = f.WriteString(text + "\n")
	if err != nil {
		fmt.Println("Error writing to file:", err)
	}
}
// Start NPC generation
func main() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println("Press 'R' then Enter to generate a random NPC...")
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("> ")
		input, _ := reader.ReadString('\n')
		input = strings.TrimSpace(input)
		if strings.EqualFold(input, "r") {
			npc := gNPC()
			// parent ID = 1 means orphan - append for completeness
			npc = append(npc, 1, 1)
			output := formatOutput(npc)
			fmt.Println(output)
			appendToFile(output)
		} else {
			fmt.Println("Please press 'R' then Enter to generate a random NPC.")
		}
	}
}
The morandi color output should have A ranges between 0.25 and 0.5, but the output looks wrong like this:
[ca/:45,1,1,1,1,221,108,81,255,207,46,9,107,207,46,9,107,207,46,9,107,0,0,0,0,0,0,0,0,0,0,0,0,3,7,10,10,7,1,8,2,2,71,71,404,404,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,71,1,1]
Can you see the value beteen comma 12th and comma 13th? It's wrong.
Can you fix it and with //● to let me know where you fix it?