Browse Source

⚗ Parser first version (no variable-group yet)

master
n0m1s 6 years ago
parent
commit
ffde823c2f
Signed by: nomis GPG Key ID: BC0454CAD76FE803
4 changed files with 593 additions and 2 deletions
  1. +140
    -0
      jdr-desc/wasteland.xml
  2. +200
    -0
      jdr/sheet.go
  3. +242
    -0
      jdr/type.go
  4. +11
    -2
      main.go

+ 140
- 0
jdr-desc/wasteland.xml View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<jdr-desc>
<custom-types>
<enum name="peuple" base-type="string">
<value>humain</value>
<value>nain</value>
<value>kobold</value>
</enum>
<enum name="sexe" base-type="string">
<value>male</value>
<value>female</value>
</enum>
<type name="bonne_aventure">
<variable type="int" name="base">Base</variable>
<variable type="int" name="actuelle">Actuelle</variable>
</type>
<type name="predilection" base-type="struct">
<variable type="string">Predilection</variable>
<list base-type="string" name="Compétence(s) associée(s)"></list>
<variable type="bool">Relance utilisée</variable>
</type>
<type name="bm" base-type="struct">
<variable type="int" name=""></variable>
<variable type="int" name=""></variable>
</type>
<type name="arme-melee" base-type="struct">
<variable type="string" name="nom">Nom</variable>
<variable type="bm" name="bm">BM</variable>
<variable type="int" name="cap_off">Cap. Off</variable>
<variable type="int" name="def">Def.</variable>
<variable type="d" name="d">D</variable>
</type>
<type name="arme-tir" base-type="struct">
<!--<variable -->
</type>
</custom-types>
<page>
<block>
<block>
<variable type="string" name="nom">nom</variable>
<variable type="string" name="joueur">joueur</variable>
<variable type="peuple" name="peuple">peuple</variable>
<variable type="sexe" name="sexe">sexe</variable>
<variable type="string" name="metier">métier</variable><!--TODO custom enum-->
<variable type="string" name="origine">origine</variable><!--TODO custom enum-->
<variable type="string" name="heritage">héritage</variable><!--TODO custom enum-->
</block>
<block>
<variable type="bonne_aventure" name="bonne_aventure">Bonne Aventure</variable>
<variable type="int" name="eclat">Éclat</variable>
<variable type="int" name="xp">Expérience</variable>
</block>
</block>
<block name="Attributs">
<variable type="int" name="adr">ADResse</variable>
<variable type="int" name="pui">PUIssance</variable>
<variable type="int" name="cla">CLAirvoyance</variable>
<variable type="int" name="pre">PRÉsence</variable>
<variable type="int" name="tre">TREmpe</variable>
</block>
<block name="Compétences">
<variable-group default-type="int">
<variable>Armes à distance</variable>
<variable>Coercition</variable>
<variable>Commerce</variable>
<variable>Discrétion</variable>
<variable>Filouterie</variable>
<variable>Mêlée</variable>
<variable>Monte</variable>
<variable>Mouvements</variable>
<variable>Nage</variable>
<variable>Navigation</variable>
<variable>Perception</variable>
<variable>Persuasion</variable>
<variable>Soins</variable>
<variable>Survie</variable>
<variable>Savoir [hier]</variable>
<variable>Savoir [Malroyaume]</variable>
<variable>Savoir [Wasteland]</variable>
<variable>Savoir [<generic />]</variable>
<variable>Savoir [<generic />]</variable>
<variable>Savoir [<generic />]</variable>
<variable>Savoir [<generic />]</variable>
</variable-group>
<list base-type="predilection" name="Prédilections"></list>
</block>
<block name="Santé">
<variable id="sante_base" type="int" value="(${pui}+${tre})*2+5">Niveau de base</variable>
<block name="Dégats non létaux">
<variable id="non_letal_deg" type="int" minvalue="0" maxvalue="${sante_base}" defaultvalue="max">Dégats non létaux</variable>
<variable type="bool" value="${non_letal_deg} \le 9">Incommodé (-2)</variable>
<variable type="bool" value="${non_letal_deg} \le 5">Sonné (-5)</variable>
<variable type="bool" value="${non_letal_deg} \eq 0">Affaibli (-5)</variable>
</block>
<block name="Dégats létaux">
<variable id="letal_deg" type="int" minvalue="0" maxvalue="${sante_base}" defaultvalue="max">Dégats létaux</variable>
<variable type="bool" value="${letal_deg} \le 9">Blessé (-2)</variable>
<variable type="bool" value="${letal_deg} \le 5">Gravement Blessé (-5)</variable>
<variable type="bool" value="${letal_deg} \eq 0">Inconscient</variable>
<variable type="int">Séquelles</variable><!--TODO check wasteland rule-->
</block>
</block>
<block name="Psyché">
<variable id="psyche_base" type="int" value="(${cla}+${tre})*2+5">Niveau de base</variable>
<variable id="psyche_deg" type="int" minvalue="0" maxvalue="${psyche_base}" defaultvalue="max">Psyché</variable>
<variable type="bool" value="${psyche_deg} \le 9">Destabilisé (-2)</variable>
<variable type="bool" value="${psyche_deg} \le 5">Choqué (-5)</variable>
<variable type="bool" value="${psyche_deg} \eq 0">Fou</variable>
<variable type="int">Traumatismes</variable><!--TODO check wasteland rule-->
</block>
<block name="Capacités Spéciales">
<list base-type="string"></list>
</block>
<block name="Combat">
<block name="combat_stats">
<variable type="int">Initiative</variable>
<variable type="int">Bonus Dégats</variable>
<variable type="int">Vitesse</variable>
<variable type="int">Défense (base)</variable>
</block>
<block name="Armes">
<block name="Mêlée">
<list base-type="arme-melee">
<value type="arme-melee">
<value type="string" name="nom">Armes naturelles</value>
<value type="int" name="bm"></value>
<value type="int" name="cap_off"></value>
<value type="int" name="def"></value>
<value type="d" name="d">1d4</value>
</value>
</list>
</block>
<block name="tir">
<list base-type="arme-tir">
</list>
</block>
</block>
</block>
</page>
</jdr-desc>

+ 200
- 0
jdr/sheet.go View File

@ -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
}

+ 242
- 0
jdr/type.go View File

@ -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
}

+ 11
- 2
main.go View File

@ -5,7 +5,8 @@ import (
"log" "log"
"net/http" "net/http"
"html/template" "html/template"
"github.com/gorilla/mux"
//"github.com/gorilla/mux"
"homnomnom.fr/jdr-server/jdr"
) )
var templates = template.Must(template.ParseGlob("templates/*.html")) 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) { 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 { if err != nil {
log.Println(err) log.Println(err)
} }
} }
func main() { func main() {
/*
ParseArguments() ParseArguments()
LoadConfig() LoadConfig()
@ -32,4 +34,11 @@ func main() {
r.HandleFunc("/", mainPageHandler) r.HandleFunc("/", mainPageHandler)
r.HandleFunc("/player", playerPageHandler) r.HandleFunc("/player", playerPageHandler)
log.Fatal(http.ListenAndServe(":8080", r)) 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)
} }

Loading…
Cancel
Save