diff --git a/jdr-desc/wasteland.xml b/jdr-desc/wasteland.xml new file mode 100644 index 0000000..62a8b3b --- /dev/null +++ b/jdr-desc/wasteland.xml @@ -0,0 +1,140 @@ + + + + + humain + nain + kobold + + + male + female + + + Base + Actuelle + + + Predilection + + Relance utilisée + + + + + + + Nom + BM + Cap. Off + Def. + D + + + + + + + + + nom + joueur + peuple + sexe + métier + origine + héritage + + + Bonne Aventure + Éclat + Expérience + + + + ADResse + PUIssance + CLAirvoyance + PRÉsence + TREmpe + + + + Armes à distance + Coercition + Commerce + Discrétion + Filouterie + Mêlée + Monte + Mouvements + Nage + Navigation + Perception + Persuasion + Soins + Survie + Savoir [hier] + Savoir [Malroyaume] + Savoir [Wasteland] + Savoir [] + Savoir [] + Savoir [] + Savoir [] + + + + + Niveau de base + + Dégats non létaux + Incommodé (-2) + Sonné (-5) + Affaibli (-5) + + + Dégats létaux + Blessé (-2) + Gravement Blessé (-5) + Inconscient + Séquelles + + + + Niveau de base + Psyché + Destabilisé (-2) + Choqué (-5) + Fou + Traumatismes + + + + + + + Initiative + Bonus Dégats + Vitesse + Défense (base) + + + + + + Armes naturelles + + + + 1d4 + + + + + + + + + + + diff --git a/jdr/sheet.go b/jdr/sheet.go new file mode 100644 index 0000000..bc14005 --- /dev/null +++ b/jdr/sheet.go @@ -0,0 +1,200 @@ +package jdr + +import ( + //"html/template" + "encoding/xml" + + "errors" + "os" +) + +type Page struct { + Items []SheetItem +} + +type SheetItem interface{ +} + +type Block struct { + Items []SheetItem +} + +type Variable struct { + Type VariableType + Name string +} + +type CharacterSheet struct { + types map[string]VariableType + Pages []*Page +} + +func (self *CharacterSheet) AddType(t VariableType) { + if self.types == nil { + self.types = make(map[string]VariableType) + } + + name := t.Name() + self.types[name] = t +} + +func (self *CharacterSheet) AddDefaultTypes() { + self.AddType(&VariableType_bool{}) + self.AddType(&VariableType_int{}) + self.AddType(&VariableType_float{}) + self.AddType(&VariableType_string{}) + self.AddType(&VariableType_d{}) +} + +func (self *CharacterSheet) getType(typename string) (VariableType, error) { + t, ok := self.types[typename] + if !ok { + return &VariableType_badType{}, errors.New("unknown type \"" + typename + "\"") + } + + return t, nil +} + +type xmlNode struct { + XMLName xml.Name + Attrs []xml.Attr `xml:"-"` + Content []byte `xml:",innerxml"` + Nodes []xmlNode `xml:",any"` +} + +func (n *xmlNode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + n.Attrs = start.Attr + type node xmlNode + + return d.DecodeElement((*node)(n), &start) +} + +func parseVariable(root xmlNode, sheet *CharacterSheet) (*Variable, error) { + ret := &Variable{} + + typeOk := false + nameOk := false + for i := 0; i < len(root.Attrs); i++ { + attrName := root.Attrs[i].Name.Local + attrVal := root.Attrs[i].Value + + switch attrName { + case "type": + t, err := sheet.getType(attrVal) + if err != nil { + return nil, err + } + ret.Type = t + typeOk = true + case "name": + ret.Name = attrVal + nameOk = true + } + } + + if !nameOk { + return nil, errors.New("Unnamed variable") + } + if !typeOk { + return nil, errors.New("No type provided for variable \"" + ret.Name + "\"") + } + + return ret, nil +} + +func parseBlock(root xmlNode, sheet *CharacterSheet) (*Block, error) { + ret := &Block{} + + for i := 0; i < len(root.Nodes); i++ { + item, err := parseItem(root.Nodes[i], sheet) + if err != nil { + return nil, err + } + + ret.Items = append(ret.Items, item) + } + + return ret, nil +} + +func parseItem(root xmlNode, sheet *CharacterSheet) (SheetItem, error) { + switch root.XMLName.Local { + case "block": + return parseBlock(root, sheet) + case "variable": + return parseVariable(root, sheet) + default: + return nil, errors.New("Unknown item: " + root.XMLName.Local) + } +} + +func parsePage(root xmlNode, sheet *CharacterSheet) (*Page, error) { + ret := &Page{} + + for i := 0; i < len(root.Nodes); i++ { + item, err := parseItem(root.Nodes[i], sheet) + if err != nil { + return nil, err + } + + ret.Items = append(ret.Items, item) + } + + return ret, nil +} + +func parseSheet(root xmlNode) (*CharacterSheet, error) { + if root.XMLName.Local != "jdr-desc" { + return nil, errors.New("Wrong root node") + } + + sheet := &CharacterSheet{} + sheet.AddDefaultTypes() + for i := 0; i < len(root.Nodes); i++ { + node := root.Nodes[i] + + switch node.XMLName.Local { + case "custom-types": + types, err := parseCustomTypes(node) + if err != nil { + return nil, err + } + for j := 0; j < len(types); j++ { + sheet.AddType(types[j]) + } + + case "page": + page, err := parsePage(node, sheet) + if err != nil { + return nil, err + } + sheet.Pages = append(sheet.Pages, page) + + default: + return nil, errors.New("Wrong format") + } + } + + return sheet, nil +} + +func ReadCharacterSheet(filename string) (*CharacterSheet, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := xml.NewDecoder(file) + var n xmlNode + err = decoder.Decode(&n) + if err != nil { + return nil, err + } + + sheet, err := parseSheet(n) + if err != nil { + return nil, err + } + return sheet, nil +} diff --git a/jdr/type.go b/jdr/type.go new file mode 100644 index 0000000..8364d4e --- /dev/null +++ b/jdr/type.go @@ -0,0 +1,242 @@ +package jdr + +import( + "log" + "errors" + "strings" + "regexp" +) + +var regexp_int *regexp.Regexp +var regexp_float *regexp.Regexp +var regexp_d *regexp.Regexp + +func init() { + regexp_int = regexp.MustCompile(`^[0-9]+$`) + regexp_float = regexp.MustCompile(`^[+-]?[0-9]*\.[0-9]+$`) + regexp_d = regexp.MustCompile(`^[1-9][0-9]*d([468]|1[02]|20|100)$`) +} + +type VariableType interface { + Name() string + Validate(s string) bool +} + +type VariableType_badType struct {} + +func (*VariableType_badType) Name() string { + return "unknown type" +} + +func (*VariableType_badType) Validate(s string) bool { + return false +} + +type VariableType_bool struct {} + +func (*VariableType_bool) Name() string { + return "bool" +} + +func (*VariableType_bool) Validate(s string) bool { + switch strings.ToLower(s) { + case "true": + return true + case "false": + return true + default: + return false + } +} + +type VariableType_int struct {} + +func (*VariableType_int) Name() string { + return "int" +} + +func (*VariableType_int) Validate(s string) bool { + return regexp_int.MatchString(s) +} + +type VariableType_float struct {} + +func (*VariableType_float) Name() string { + return "float" +} + +func (*VariableType_float) Validate(s string) bool { + return regexp_float.MatchString(s) +} + +type VariableType_string struct {} + +func (*VariableType_string) Name() string { + return "string" +} + +func (*VariableType_string) Validate(s string) bool { + return true +} + +type VariableType_d struct {} + +func (*VariableType_d) Name() string { + return "d" +} + +func (*VariableType_d) Validate(s string) bool { + return regexp_d.MatchString(s) +} + +func makeVariableType(s string) VariableType { + switch(strings.ToLower(s)) { + case "bool": + return &VariableType_bool{} + case "int": + return &VariableType_int{} + case "float": + return &VariableType_float{} + case "string": + return &VariableType_string{} + case "d": + return &VariableType_d{} + default: + log.Println("Unknown type:", s) + return &VariableType_badType{} + } +} + +type CustomEnum struct { + name string + BaseType VariableType + Values []string +} + +func (self *CustomEnum) Name() string { + return self.name +} + +func (self *CustomEnum) Validate(s string) bool { + if !self.BaseType.Validate(s) { + return false + } + + for i := 0; i < len(self.Values); i++ { + if self.Values[i] == s { + return true + } + } + + return false +} + +type CustomStruct struct { + name string + Variables []Variable +} + +func (self *CustomStruct) Name() string { + return self.name +} + +func (self *CustomStruct) Validate(s string) bool { + log.Println("Custom struct Validate is not (yet) implemented") + return false +} + +func parseCustomEnum(root xmlNode) (*CustomEnum, error) { + customEnum := CustomEnum {} + + nameOk := false; + typeOk := false; + for i := 0; i < len(root.Attrs); i++ { + attrName := root.Attrs[i].Name.Local + attrVal := root.Attrs[i].Value + + switch attrName { + case "name": + nameOk = true + customEnum.name = attrVal + case "base-type": + typeOk = true + customEnum.BaseType = makeVariableType(attrVal) + switch customEnum.BaseType.(type) { + case *VariableType_badType: + typeOk = false + } + } + } + + if !nameOk { + return nil, errors.New("Unnamed custom enum") + } + if !typeOk { + return nil, errors.New("Bad base type for " + customEnum.Name()) + } + + for i := 0; i < len(root.Nodes); i++ { + node := root.Nodes[i] + + switch node.XMLName.Local { + case "value": + value := string(node.Content) + if customEnum.BaseType.Validate(value) { + customEnum.Values = append(customEnum.Values, value) + } else { + log.Println("Cannot validate value enum \"" + value + "\" with type \"" + + customEnum.BaseType.Name() + "\". Discarded") + } + } + } + + return &customEnum, nil +} + +func parseCustomStruct(root xmlNode) (*CustomStruct, error) { + customType := CustomStruct{} + + nameOk := false + for i := 0; i < len(root.Attrs); i++ { + attrName := root.Attrs[i].Name.Local + attrVal := root.Attrs[i].Value + + switch attrName { + case "name": + nameOk = true + customType.name = attrVal + } + } + if !nameOk { + return nil, errors.New("Unnamed custom type") + } + + return &customType, nil +} + +func parseCustomTypes(root xmlNode) ([]VariableType, error) { + types := []VariableType{} + + for i := 0; i < len(root.Nodes); i++ { + node := root.Nodes[i] + + switch node.XMLName.Local { + case "enum": + customEnum, err := parseCustomEnum(node) + if err != nil { + return []VariableType{}, err + } + types = append(types, customEnum) + case "type": + customType, err := parseCustomStruct(node) + if err != nil { + return []VariableType{}, err + } + types = append(types, customType) + default: + return []VariableType{}, errors.New("Unknown CustomType: " + root.Nodes[i].XMLName.Local) + } + } + + return types, nil +} diff --git a/main.go b/main.go index ce056bf..02f9c18 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,8 @@ import ( "log" "net/http" "html/template" - "github.com/gorilla/mux" + //"github.com/gorilla/mux" + "homnomnom.fr/jdr-server/jdr" ) var templates = template.Must(template.ParseGlob("templates/*.html")) @@ -18,13 +19,14 @@ func mainPageHandler(w http.ResponseWriter, r *http.Request) { } func playerPageHandler(w http.ResponseWriter, r *http.Request) { - err := templates.ExecuteTemplate(w, "player.html", nil) + err := templates.ExecuteTemplate(w, "player.html", 1) if err != nil { log.Println(err) } } func main() { + /* ParseArguments() LoadConfig() @@ -32,4 +34,11 @@ func main() { r.HandleFunc("/", mainPageHandler) r.HandleFunc("/player", playerPageHandler) log.Fatal(http.ListenAndServe(":8080", r)) + */ + + _, err := jdr.ReadCharacterSheet("jdr-desc/wasteland.xml") + if err != nil { + log.Fatal("error: ", err) + } + //log.Println(sheet.CustomTypes.Enums[0].Name) }