package persona import "fmt" // ValidateHumanoidMorphology validates morphology features for humanoid characters. // Returns errors if morphology features are present without sufficient MorphLevel. func ValidateHumanoidMorphology(c *Character) ValidationErrors { var errs ValidationErrors if c == nil || c.Species != SpeciesHumanoid { return errs } morphology := c.Morphology if morphology == nil || !morphology.HasFeatures() { return errs // No morphology features, nothing to validate } // Check if morph level allows features if c.MorphLevel < 25 { errs.Add("morph_level", fmt.Sprintf("morph level %d is too low for morphology features (requires >= 25)", c.MorphLevel), ErrMorphLevelRequired) return errs } // Validate individual feature types if morphology.EarType != "" && !IsValidEarType(morphology.EarType) { errs.AddValue("morphology.ear_type", morphology.EarType, fmt.Sprintf("invalid ear type: %s", morphology.EarType), ErrInvalidFeatureType) } if morphology.TailType != "" && !IsValidTailType(morphology.TailType) { errs.AddValue("morphology.tail_type", morphology.TailType, fmt.Sprintf("invalid tail type: %s", morphology.TailType), ErrInvalidFeatureType) } if morphology.FangType != "" && !IsValidFangType(morphology.FangType) { errs.AddValue("morphology.fang_type", morphology.FangType, fmt.Sprintf("invalid fang type: %s", morphology.FangType), ErrInvalidFeatureType) } if morphology.WingType != "" && !IsValidWingType(morphology.WingType) { errs.AddValue("morphology.wing_type", morphology.WingType, fmt.Sprintf("invalid wing type: %s", morphology.WingType), ErrInvalidFeatureType) } if morphology.HornType != "" && !IsValidHornType(morphology.HornType) { errs.AddValue("morphology.horn_type", morphology.HornType, fmt.Sprintf("invalid horn type: %s", morphology.HornType), ErrInvalidFeatureType) } if morphology.ClawType != "" && !IsValidClawType(morphology.ClawType) { errs.AddValue("morphology.claw_type", morphology.ClawType, fmt.Sprintf("invalid claw type: %s", morphology.ClawType), ErrInvalidFeatureType) } // Validate morph level vs feature intensity if err := validateMorphLevelFeatureConsistency(c.MorphLevel, morphology); err != nil { errs.Add("morphology", err.Error(), ErrInconsistentAttributes) } return errs } // validateMorphLevelFeatureConsistency ensures features match the morph level band. func validateMorphLevelFeatureConsistency(morphLevel int, m *MorphologyHints) error { if m == nil { return nil } band := MorphLevelBand(morphLevel) // Count features featureCount := 0 hasExtremeFeatures := false if m.EarType != "" { featureCount++ } if m.TailType != "" { featureCount++ } if m.FangType != "" { featureCount++ } if m.WingType != "" { featureCount++ hasExtremeFeatures = true // Wings are extreme } if m.HornType != "" { featureCount++ if m.HornType == "demon" || m.HornType == "antlers" { hasExtremeFeatures = true } } if m.ClawType != "" { featureCount++ if m.ClawType == "talons" || m.ClawType == "prominent" { hasExtremeFeatures = true } } if len(m.FurPatterns) > 0 { featureCount++ if len(m.FurPatterns) > 2 { hasExtremeFeatures = true } } if m.ScalePattern != "" { featureCount++ hasExtremeFeatures = true // Scales are extreme } // Validate based on band switch band { case "subtle": // At subtle level (25-49), only minor features allowed if hasExtremeFeatures { return fmt.Errorf("extreme features (wings, scales, heavy fur) require morph level >= 75") } if featureCount > 3 { return fmt.Errorf("too many features (%d) for subtle morph level (max 3)", featureCount) } case "demi_human": // At demi-human level (50-74), moderate features allowed if hasExtremeFeatures { return fmt.Errorf("extreme features (wings, scales, heavy fur) require morph level >= 75") } case "hybrid", "creature": // At hybrid (75-99) and creature (100), all features allowed // No restrictions } return nil } // RecommendMorphLevel recommends a minimum morph level for given morphology. func RecommendMorphLevel(m *MorphologyHints) int { if m == nil || !m.HasFeatures() { return 0 } // Check for extreme features hasExtreme := m.WingType != "" || m.ScalePattern != "" || m.ClawType == "talons" || m.ClawType == "prominent" || m.HornType == "demon" || m.HornType == "antlers" || len(m.FurPatterns) > 2 if hasExtreme { return 75 // Hybrid level } // Check for moderate features featureCount := 0 if m.EarType != "" { featureCount++ } if m.TailType != "" { featureCount++ } if m.FangType != "" { featureCount++ } if m.HornType != "" { featureCount++ } if m.ClawType != "" { featureCount++ } if len(m.FurPatterns) > 0 { featureCount++ } if featureCount > 3 { return 50 // Demi-human level } return 25 // Subtle level }