185 lines
4.8 KiB
Go
185 lines
4.8 KiB
Go
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
|
|
}
|