Browse Source

Tile map finally loads and draws with stable 60fps. Still some rendering errors to fix.

Beoran 6 years ago
parent
commit
a33544a664

+ 36 - 5
ebs.go

@@ -1,9 +1,12 @@
 package main
 
-import "fmt"
+// import "fmt"
 import "flag"
+import "runtime"
 import "gitlab.com/beoran/ebsgo/engine/global"
-import "gitlab.com/beoran/ebsgo/engine/fif"
+import "gitlab.com/beoran/ebsgo/engine/fifi"
+import "gitlab.com/beoran/ebsgo/monolog"
+
 
 // import "path/filepath"
 // import "math/rand"
@@ -12,25 +15,53 @@ import "gitlab.com/beoran/ebsgo/engine/fif"
 const SCREEN_W = 640
 const SCREEN_H = 480
 const WINDOW_TITLE = "Eruta Blessed Saviour"
+
+// Log levels for monolog
+type logLevels []string
+
+var EbsLogLevels logLevels = logLevels{
+    "FATAL", "ERROR", "WARNING", "INFO",
+}
+
 // Command line flags
 var fullscreen_flag = flag.Bool("fullscreen", false, "Run fullscreen or not")
+var enable_logs = flag.String("el", "FATAL,ERROR,WARNING,INFO", "Log levels to enable")
+var disable_logs = flag.String("dl", "", "Log levels to disable")
+
+
+func enableDisableLogs() {
+    monolog.EnableLevels(*enable_logs)
+    monolog.DisableLevels(*disable_logs)
+    monolog.Info("Log levels: %v", monolog.DefaultLog.EnabledLevels())
+}
+
+func init() {
+    runtime.LockOSThread()
+}
 
 
 func main() {
+    flag.Parse()
+    monolog.Setup("ebsgo.log", true, false)
+    defer monolog.Close()
+    enableDisableLogs()
+    
     state := global.State{}
     if ! fifi.Initialize() {        
-        fmt.Printf("Could not find data file directory.")
+        monolog.Fatal("Could not find data file directory.")
         return
     }
 
     
     if ! state.InstallAllegro() {
-        fmt.Printf("Could not initialize allegro.")
+        monolog.Fatal("Could not initialize allegro.")
         return
     }
     
+    monolog.Info("Will open a screen, full screen: %B", *fullscreen_flag)
+    
     if state.OpenDisplay(SCREEN_W, SCREEN_H, WINDOW_TITLE, *fullscreen_flag) == nil {
-        fmt.Printf("Error creating display.")
+        monolog.Fatal("Error creating display.")
         return
     }
     

+ 1 - 1
engine/fifi/fifi.go

@@ -12,7 +12,7 @@ import al "gitlab.com/beoran/al5go/al"
 var DataPath string = ""
 
 func isDataPathOK(path string) bool {
-    fn := filepath.Join(path, "eruta.cfg")
+    fn := filepath.Join(path, "eruta.conf")
     file, err := os.Open(fn) 
     file.Close()
     return err == nil

+ 0 - 17
engine/geometry/point/point.go

@@ -1,17 +0,0 @@
-package point
-
-type P struct {
-    X   float64
-    Y   float64
-}
-
-
-func New(x, y float64) P {
-    p := P{ x, y } 
-    return p
-}
-
-
-
-
-

+ 23 - 0
engine/geometry/rectangle.go

@@ -0,0 +1,23 @@
+package geometry
+
+type Rectangle struct {
+    X   float32
+    Y   float32
+    W   float32
+    H   float32
+}
+
+
+func NewRectangle(x, y, w, h float32) Rectangle {
+    r := Rectangle{ x, y, w, h } 
+    return r
+}
+
+func (rect Rectangle) X2() float32 {
+    return rect.X + rect.W
+}
+
+func (rect Rectangle) Y2() float32 {
+    return rect.Y + rect.H
+}
+

+ 0 - 23
engine/geometry/rectangle/rectangle.go

@@ -1,23 +0,0 @@
-package rectangle
-
-type Rectangle struct {
-    X   float64
-    Y   float64
-    W   float64
-    H   float64
-}
-
-
-func New(x, y, w, h float64) Rectangle {
-    r := Rectangle{ x, y, w, h } 
-    return r
-}
-
-func (rect Rectangle) X2() float64 {
-    return rect.X + rect.W
-}
-
-func (rect Rectangle) Y2() float64 {
-    return rect.Y + rect.H
-}
-

+ 30 - 0
engine/geometry/vector.go

@@ -0,0 +1,30 @@
+package geometry
+
+type Vector struct {
+    X   float32
+    Y   float32
+}
+
+
+type V = Vector
+type P = Vector
+type Point = Vector
+
+func NewVector(x, y float32) Vector {
+    p := Vector{ x, y } 
+    return p
+}
+
+func (v1 * Vector) Add(v2 Vector) * Vector {
+    v1.X += v2.X
+    v1.Y += v2.Y
+    return v1
+} 
+
+func (v1 Vector) Sum(v2 Vector) Vector {
+   return NewVector(v1.X + v2.X, v1.Y + v2.Y)
+}
+
+
+
+

+ 46 - 19
engine/global/state.go

@@ -1,10 +1,12 @@
 package global
 
-import "log"
-import "os"
 import "math"
 
 import al "gitlab.com/beoran/al5go/al"
+import "gitlab.com/beoran/ebsgo/engine/tile"
+import "gitlab.com/beoran/ebsgo/engine/physics"
+import "gitlab.com/beoran/ebsgo/monolog"
+
 
 type FPS struct {
     FPS     float64
@@ -16,7 +18,6 @@ type FPS struct {
 
 
 type State struct {
-    Log         * log.Logger        
     Display     * al.Display
     Fullscreen    bool
     W             int
@@ -31,11 +32,12 @@ type State struct {
     Queue       * al.EventQueue
     FPS           
     Frames        int
+    LastUpdate    float64
     Joysticks    [] * al.Joystick
-//  Map           tile.Map
+    Map         * tile.Map
 //  MapID         store.ID
 //  area.Area
-//  camera.Camera
+    Camera        physics.Camera
 //  Sprites       sprite.List
 //  ScriptEngine  raku.Runtime
 //  zori.Console
@@ -53,11 +55,21 @@ func (ms * State) Wf() float32 {
 
 func (ms * State) LogIfFail(ok bool, text string) bool {
     if !ok { 
-        ms.Log.Printf("%s", text)
+        monolog.Error("%s", text)
     }
     return ok
 }
 
+func (ms * State) LoadMap(name string) {
+    tm, err := tile.LoadMapFifi(name)
+    if err != nil {
+        monolog.Error("Could not load tile map: %s", err)
+    } else {
+        ms.Map = tm
+    }
+}
+
+
 func (ms * State) SetupJoysticks() {
     if (!ms.HaveJoystick) {
         return 
@@ -68,28 +80,29 @@ func (ms * State) SetupJoysticks() {
     num := al.GetNumJoysticks()
     
     
-    ms.Log.Printf("Found %d joysticks.\n", num)
+    monolog.Printf("Found %d joysticks.\n", num)
     
     for i:= 0; i < num; i ++ {
         joy := al.GetJoystick(i)
         ms.Joysticks = append(ms.Joysticks, joy)
-        ms.Log.Printf("Joystick nr %d, name %s", i, joy.GetName())
+        monolog.Printf("Joystick nr %d, name %s", i, joy.GetName())
         snum := joy.GetNumSticks()
         bnum := joy.GetNumButtons()
-        ms.Log.Printf("%d sticks and %d buttons", snum, bnum)        
+        monolog.Printf("%d sticks and %d buttons", snum, bnum)        
     }
     
     ms.Queue.RegisterEventSource(al.JoystickEventSource())    
 }
 
 func (ms * State) InstallAllegro() bool {
-    ms.Log = log.New(os.Stdout, "ebsengine: ", log.Lshortfile)
+    
     res := ms.LogIfFail(al.InstallSystem(), "Could not install Allegro")
     res = res && ms.LogIfFail(al.InstallKeyboard(), "Could not install Keyboard")
     res = res && ms.LogIfFail(al.InstallMouse(), "Could not install mouse")
     al.InitFontAddon()
     res = res && ms.LogIfFail(al.InitTTFAddon(), "Could not init TTF addon")
     res = res && ms.LogIfFail(al.InitPrimitivesAddon(), "Could not init primitives addon")
+    res = res && ms.LogIfFail(al.InitImageAddon(), "Could not init image addon")
 
     if res {
         ms.HaveTouch    = al.InstallTouchInput()
@@ -102,7 +115,6 @@ func (ms * State) InstallAllegro() bool {
     
     ms.SetupJoysticks()
     
-    
     return res
 }
 
@@ -131,6 +143,9 @@ func (ms * State) OpenDisplay(w, h int, title string, fullscreen bool) * al.Disp
     
     ms.FPS.Time = al.Time()
     
+    ms.Camera.W = ms.Display.Widthf()
+    ms.Camera.H = ms.Display.Heightf()
+    
     return display
 }
 
@@ -171,9 +186,15 @@ func (ms * State) DrawFPS() {
     ms.DefaultFont.DrawTextf(white, 10.0, 10.0, al.ALIGN_LEFT, "FPS: %d", int(ms.FPS.FPS))
 }
 
-func (ms * State) UpdateFrames() {
+func (ms * State) Update() {
     ms.Frames++
     now := al.Time()
+    delta := now - ms.LastUpdate
+    ms.LastUpdate = now
+    if ms.Map != nil {
+        ms.Map.Update(delta)
+    }
+    
     if (now - ms.FPS.Time) >= 1.0 {
         realfps := float64(ms.Frames) / (now - ms.FPS.Time)
         /* Display and use a rounded value for FPS, the number after the comma is normally due to jitter anyway. */
@@ -186,14 +207,17 @@ func (ms * State) UpdateFrames() {
 
 
 func (ms * State) Draw() {
-    ms.DrawFPS()
+    if ms.Map != nil {
+            ms.Map.Draw(&ms.Camera)
+    }
     yellow := al.MapRGB(255, 255, 0)
     al.DrawFilledCircle(30.0, 40.0, 15.0, yellow) 
+    ms.DrawFPS()
     al.FlipDisplay()
 }
 
 func (ms * State) Load() {
-    
+    ms.LoadMap("map/map_0001_tiled11.tmx")
 }
 
 func (ms * State) Run() {
@@ -202,7 +226,7 @@ func (ms * State) Run() {
     for !ms.Done {
         ms.Draw()
         ms.HandleEvents()
-        ms.UpdateFrames()
+        ms.Update()
     }
 }
 
@@ -223,9 +247,12 @@ func (ms * State) HandleEvent(event al.Event) {
 }
 
 func (ms * State) HandleKeyboardEvent(event * al.KeyboardEvent ) {
-    if (event.Keycode() == al.KEY_ESCAPE) {
-        ms.Done = true
-    }
+    switch (event.Keycode()) {
+        case al.KEY_ESCAPE: ms.Done = true
+        case al.KEY_UP: ms.Camera.Y-= 10
+        case al.KEY_DOWN: ms.Camera.Y+= 10
+        case al.KEY_LEFT: ms.Camera.X-= 10
+        case al.KEY_RIGHT: ms.Camera.X+= 10
+    } 
 }
 
-

+ 17 - 0
engine/physics/body.go

@@ -0,0 +1,17 @@
+package physics
+
+import "gitlab.com/beoran/ebsgo/engine/geometry"
+
+// Body simulates a physical body in 2D
+type Body struct {
+    P geometry.Vector // Position
+    S geometry.Vector // Size of bounding box.
+    V geometry.Vector // Velocity
+    A geometry.Vector // Acceleration
+    M float32         // Mass
+}
+
+
+
+ 
+

+ 29 - 0
engine/physics/camera.go

@@ -0,0 +1,29 @@
+package physics
+
+
+import "gitlab.com/beoran/ebsgo/engine/geometry"
+
+type Camera struct {
+    geometry.Rectangle
+}
+
+/*
+func (cam * Camera) X() float64 {
+    return cam.Rectangle.X
+}
+
+func (cam * Camera) Y() float64 {
+    return cam.Rectangle.X
+}
+
+func (cam * Camera) W() float64 {
+    return cam.Rectangle.W
+}
+
+func (cam * Camera) H() float64 {
+    return cam.Rectangle.H
+}
+*/
+
+
+

+ 132 - 85
engine/tile/io.go

@@ -1,17 +1,20 @@
 package tile
 
+import "gitlab.com/beoran/ebsgo/monolog"
 import "fmt"
+import "strings"
+import "bytes"
 import "strconv"
 import "io/ioutil"
 import "compress/gzip"
 import "compress/zlib"
-import "encoding/csv"
 import "encoding/binary"
+import "encoding/base64"
 
 
 import "gitlab.com/beoran/al5go/al"
 // import "gitlab.com/beoran/ebsgo/engine/geometry/point"
-import "gitlab.com/beoran/ebsgo/engine/physics/camera"
+// import "gitlab.com/beoran/ebsgo/engine/physics/camera"
 import "gitlab.com/beoran/ebsgo/engine/fifi"
 
 import "github.com/beevik/etree"
@@ -22,39 +25,39 @@ const (
     TMX_FLIPPED_HORIZONTALLY = 0x80000000
     TMX_FLIPPED_VERTICALLY   = 0x40000000
     TMX_FLIPPED_DIAGONALLY   = 0x20000000
-    TMX_FLIPPED_FILTER       = (~0xE0000000)
+    TMX_FLIPPED_FILTER       = (^uint32(0xE0000000))
 )
 
 
-func AttrInt(e * etree.Element, string key) (int, error) {
+func AttrInt(e * etree.Element, key string) (int, error) {
     return strconv.Atoi(e.SelectAttrValue(key, ""))
 }
 
-func AttrFloat64(e * etree.Element, string key) (float64, error) {
+func AttrFloat64(e * etree.Element, key string) (float64, error) {
     return strconv.ParseFloat(e.SelectAttrValue(key, ""), 64)
 }
 
 
-func Property(e * etree.Element, propname string) string, bool {
-  properties := e.FindElements('//properties/property')
+func Property(e * etree.Element, propname string) (string, bool) {
+  properties := e.FindElements("//properties/property")
   
-  for property := range properties {
-        name := e.SelectAttrValue("name", "")
+  for _, property := range properties {
+        name := property.SelectAttrValue("name", "")
         if name == propname {
-            return e.SelectAttrValue("value", ""), true
+            return property.SelectAttrValue("value", ""), true
         }
   }
   
   return "", false
 }
 
-func PropertyInt(e * etree.Element, propname string) int, error {
+func PropertyInt(e * etree.Element, propname string) (int, error) {
     value , ok := Property(e, propname)
     if (!ok) {
         return 0, fmt.Errorf("no such property")
     }
     
-    return strconv.Atoi(e.SelectAttrValue(key, ""))
+    return strconv.Atoi(value)
 } 
 
 
@@ -76,7 +79,9 @@ func LoadFrame(xml_frame * etree.Element, tile * Tile, set * Set) * Tile {
     /* Convert ms to s. */ 
     duration /= 1000.0
     
-    tile.AddAnimationFrame(tile_id, duration)
+    frame := tile.AddAnimationFrame(tile_id, duration)
+    
+    monolog.Log("TILEIO", "Frame loaded: %d %f", frame.Index, frame.Duration)
     
     return tile
 }
@@ -89,15 +94,18 @@ func LoadAnimation(xml_tile * etree.Element, tile * Tile, set * Set) * Tile {
     }
     
     xml_frames := xml_anim.FindElements("//frame")
-    for xml_frame := range xml_frames {
+    for _, xml_frame := range xml_frames {
         LoadFrame(xml_frame, tile, set)
     }
+
+    
+    monolog.Log("TILEIO", "Loaded animation: %d", len(tile.Frames))
     return tile
 }
 
-func XmlToTileType(xml_tile * etree.Element) int, error {
+func XmlToTileType(xml_tile * etree.Element) (uint, error) {
     
-    value , ok := Property(e, "type")
+    value , ok := Property(xml_tile, "type")
     if (!ok) {
         return 0, fmt.Errorf("no type property")
     }
@@ -114,17 +122,27 @@ func XmlToTileType(xml_tile * etree.Element) int, error {
 
 func LoadTile(xml_tile * etree.Element, index int, set * Set, tm * Map) (*Tile, error) {
      
-    if id, err := AttrInt(xml_tile, "id") ; err != nil { 
-        return nil, fmr.Errorf("Tile id not found: %s", err)
+    id, err := AttrInt(xml_tile, "id") 
+ 
+    if err != nil { 
+        return nil, fmt.Errorf("Tile id not found: %s", err)
     }
   
     tile := set.Tile(id)
+    monolog.Log("TILEIO", "Got tile: %d <- %d", tile.Active, tile.Index)
+    
+    
     if tile == nil { 
-        return nil, fmr.Errorf("Tile id not found in tile set: %d", id)
+        return nil, fmt.Errorf("Tile id not found in tile set: %d", id)
     }
     
-    if tiletype, err := XmlToTileType(xml_tile) ; err == nil {
-        tile.Type = tiletype
+    value , ok := Property(xml_tile, "type")
+    if (ok) {
+        parts := strings.Split(value, ",") 
+        for _, part := range parts {
+            clean := strings.Trim(part, "")
+            tile.SetProperty(clean)
+        }
     }
     
     // ianim       , err := PropertyInt(xml_tile, "anim"       )
@@ -146,16 +164,18 @@ func LoadTile(xml_tile * etree.Element, index int, set * Set, tm * Map) (*Tile,
     tile.ShadowMask = ishadowmask
 
     /* Support tiled-style animations. */
-    return LoadAnimation(xml_tile, tile, set)
+    LoadAnimation(xml_tile, tile, set)
+    monolog.Log("TILEIO", "Loaded tile: (%d x %d)", tile.SheetX(tile.Tileset), tile.SheetY(tile.Tileset))
+    return tile, nil
 }
 
 
 /** Calculates index and the Allegro draw flags and rotation for a tile. */
 
-func TmxTileIndexFlagsAndRotation(tmx_index int32) int, int, float32 { 
+func TmxTileIndexFlagsAndRotation(tmx_index uint32) (int, int, float32) { 
     index := int(tmx_index & TMX_FLIPPED_FILTER) 
-    flags := 0
-    rotate := 0.0
+    flags := int(0)
+    rotate := float32(0.0)
 
     if (tmx_index & TMX_FLIPPED_HORIZONTALLY) != 0 {
         flags |= al.FLIP_HORIZONTAL
@@ -165,15 +185,15 @@ func TmxTileIndexFlagsAndRotation(tmx_index int32) int, int, float32 {
         flags |= al.FLIP_VERTICAL
     } 
   
-    if (tmx_index & TMX_FLIPPED_DIAGONALLY) {
-        rotate := al.PI / 2.0
+    if (tmx_index & TMX_FLIPPED_DIAGONALLY) != 0 {
+        rotate = float32(al.PI / 2.0)
     }
 
   return index, flags, rotate
 }
 
 
-func LoadSet(xml_set * etree.Element, index int, tm * Map) * Set, error { 
+func LoadSet(xml_set * etree.Element, index int, tm * Map) (* Set, error) { 
     firstgid, err := AttrInt(xml_set, "firstgid")
     if err != nil {
         return nil, fmt.Errorf("Could not find firstgid for tile set %d: %s", index, err)
@@ -185,8 +205,8 @@ func LoadSet(xml_set * etree.Element, index int, tm * Map) * Set, error {
         return nil, fmt.Errorf("Could not find image data for tile set %d", index)
     }
     
-    tile_w, err := AttrInt("tilewidth")
-    tile_h, err := AttrInt("tilewidth")
+    tile_w, err := AttrInt(xml_set, "tilewidth")
+    tile_h, err := AttrInt(xml_set, "tilewidth")
     
     if (tile_w < 1) || (tile_h < 1) {
         return nil, fmt.Errorf("Tile size too small for tile set %d: %d x %d", index, tile_w, tile_h)
@@ -194,55 +214,70 @@ func LoadSet(xml_set * etree.Element, index int, tm * Map) * Set, error {
     
     iname   := xml_image.SelectAttrValue("source", "tile_set_default.png")    
     bmp     := fifi.LoadBitmap("map/" + iname)
+    
+    if bmp == nil {
+        return nil, fmt.Errorf("Could not load tile set sheet: %s", iname)
+    }
+    
     set     := NewSet(bmp, tile_w, tile_h, firstgid)
+    monolog.Log("TILEIO", "Loaded tile set: (%d x %d) (%d x %d) %d", set.TileW, set.TileH, set.W, set.H, set.FirstGID)
     
     xml_tiles := xml_set.FindElements("//tile")
     
-    for xml_tile, tile_index := range xml_tiles {
+    for tile_index, xml_tile := range xml_tiles {
         LoadTile(xml_tile, tile_index, set, tm)
     }
     
+    set.Recalculate()
+    
     return set, nil
 }
 
-type Expander(in []byte) []byte, error 
+type Expander func(in []byte) ([]byte, error) 
 
-type Decoder(data string, w, h int, expand Expander) [][]uint32, error 
+type Decoder func(data string, w, h int, expand Expander) ([][]uint32, error) 
 
 
-func CsvDecoder(data string, w, h int, expand Expander) [][]uint32, error {
+func CsvDecoder(data string, w, h int, expand Expander) ([][]uint32, error) {
+    lines := strings.Split(data, "\n")
+    x := 0
+    y := 0
     
-    r := csv.NewReader(strings.NewReader(data))
-    records, err := r.ReadAll()
-    if err != nil {
-        return err
-    }
-    
-    result := make([][]uint32, h) 
-    for i := 0 ; i < h; i++ { 
-        result[i] := make([]uint32, w) 
-        for j := 0 ; i < w ; j++ { 
-            res, err := strconv.ParseUint(records[i][j], 10, 32) 
-            if err != nil {
-            result[i][j] = 0
-            } else {
-                result[i][j] = uint32(res)
+    result := make([][]uint32, h)     
+    for _, line := range lines {
+        if line != "" {
+            cells := strings.Split(line, ",")
+            result[y] = make([]uint32, w)
+            for x = 0 ; x < w ; x++ {
+                value := uint32(0)
+                if ( x >= len(cells)  ) {
+                    break;
+                }
+                res, err := strconv.ParseUint(cells[x], 10, 32) 
+                if err == nil {
+                    value = uint32(res)
+                }
+                result[y][x] = value
             }
+            y++    
+        }
+        if y >= h {
+            break;
         }
     }
-    return result
+    return result, nil
 }
 
-func GzipExpander(in []byte) []byte, error {
-    zr, err := gzip.NewReader(&in)
+func GzipExpander(in []byte) ([]byte, error) {
+    zr, err := gzip.NewReader(bytes.NewReader(in))
     if err != nil {
         return nil, err
     }
     return ioutil.ReadAll(zr)
 }
 
-func ZlibExpander(in []byte) []byte, error {
-    zr, err := lib.NewReader(&in)
+func ZlibExpander(in []byte) ([]byte, error) {
+    zr, err := zlib.NewReader(bytes.NewReader(in))
     if err != nil {
         return nil, err
     }
@@ -250,8 +285,8 @@ func ZlibExpander(in []byte) []byte, error {
 }
 
 
-func Base64Decoder(data string, w, h int, expand Expander) [][]uint32, error {
-    bytes, err := base64.StdEncoding.DecodeString()
+func Base64Decoder(data string, w, h int, expand Expander) ([][]uint32, error) {
+    bytes, err := base64.StdEncoding.DecodeString(data)
     if err != nil {
         return nil, err
     }
@@ -265,7 +300,7 @@ func Base64Decoder(data string, w, h int, expand Expander) [][]uint32, error {
     
     result := make([][]uint32, h) 
     for i := 0 ; i < h; i++ { 
-        result[i] := make([]uint32, w) 
+        result[i] = make([]uint32, w) 
         for j := 0 ; i < w ; j++ { 
             index := (i * w + j) * 4
             value := binary.LittleEndian.Uint32(bytes[index:4])
@@ -289,13 +324,13 @@ var Expanders map[string]Expander = map[string]Expander {
 
 
 // Loads a single tile pane of the tile map from xml (tmx). 
-func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {  
-    err, width := AttrInt(xml_pane, "width")
-    err, height := AttrInt(xml_pane, "height")
+func LoadPane(xml_pane * etree.Element, index int, tm * Map) (* Pane, error) {  
+    width, err := AttrInt(xml_pane, "width")
+    height, err := AttrInt(xml_pane, "height")
   
     if (width < 1) || (height < 1) {
         return nil, 
-        fmr.Errorf("Layer %i size too small: %d x %d, ", index, width, height)
+        fmt.Errorf("Layer %i size too small: %d x %d, ", index, width, height)
     }
     
     name := xml_pane.SelectAttrValue("name", "")
@@ -315,10 +350,10 @@ func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {
     
     pane := NewPane(set, width, height, name)
     
-    xml_data := xml_pane.FindElement('//data')
+    xml_data := xml_pane.FindElement("//data")
     
     if xml_data == nil {
-        return nil, fmr.Errorf("Cannot find layer data for layer %d", index)
+        return nil, fmt.Errorf("Cannot find layer data for layer %d", index)
     }
     
     encoding    := xml_data.SelectAttrValue("encoding"   , "csv")
@@ -335,25 +370,28 @@ func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {
     grid, err   := decoder(xml_data.Text(), width, height, expander) 
 
     if err != nil {
-        return nil, fmt.Errorf("Could not decode data for layer %d: %s %s: %s",
-         index, decoder, expander, err )
+        return nil, fmt.Errorf("Could not decode data for layer %d: %s %s: %s;\nData:>%s<",
+         index, encoding, compression, err, xml_data.Text())
     }
     
-    for y := 0 : y < height, y ++ {
-        for x := 0 ; x < width ; x++ {
-            tmx_index = grid[y][x]
+    monolog.Log("TILEIO", "Loaded grid size: (%d x %d) vs size (%d x %d)\n",len(grid), len(grid[0]), height, width)
+    
+    for y := 0 ; y < height ; y ++ {
+        row := grid[y]
+        for x := 0 ; x < width && x < len(row)  ; x++ {
+            tmx_index := row[x]
             if tmx_index == 0 { 
                 pane.SetTile(x, y, nil)
             } else {
                 index, flags, rotation := TmxTileIndexFlagsAndRotation(tmx_index)
-                if pane.Set == nil { 
-                    pane.Set = tm.LookupTmxTileset(index)
+                if pane.Tileset == nil {
+                    pane.Tileset = tm.LookupTmxTileset(index)
                 }
-                ebs_index   := index - pane.FirstGID + 1 
+                ebs_index   := index - pane.FirstGID() // + 1 
                 cell        := pane.Cell(x, y)
-                cell.Tile       = pane.Set.Tile(ebs_index)
-                cell.Tile.Flags = flags
-                cell.Tile.Rotate= rotation 
+                cell.Tile    = pane.Tileset.Tile(ebs_index)
+                cell.Flags   = flags
+                cell.Rotate  = rotation
             }
         }
     }
@@ -363,16 +401,19 @@ func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {
 
 // Load a TMX map file from the given XML document.
 func LoadMapXml(doc * etree.Document) (* Map, error) {
-    root ::= doc.Root()
+    root := doc.Root()
     if root.Tag != "map" {
         return nil, fmt.Errorf("File is not a TMX map file: %s", root.Tag)
     }
     
-    if wide, err := AttrInt(root, "width"); err != nil {
+    wide, err := AttrInt(root, "width")
+    
+    if err != nil {
         return nil, fmt.Errorf("Width not a number: %s", err)
     }
     
-    if high, err := AttrInt(root, "height"); err != nil {
+    high, err := AttrInt(root, "height"); 
+    if err != nil {
         return nil, fmt.Errorf("Height not a number: %s", err)
     }
     
@@ -382,21 +423,21 @@ func LoadMapXml(doc * etree.Document) (* Map, error) {
     
     tm := NewMap(wide, high)
     
-    xml_sets := root.FindElements('//tileset')
+    xml_sets := root.FindElements("//tileset")
     
-    for xml_set, i := range xml_sets {
-        set, err := LoadSet(xml_set, i)
+    for i, xml_set := range xml_sets {
+        set, err := LoadSet(xml_set, i, tm)
         if err != nil {
             return nil, fmt.Errorf("Could not load tile set: %s", err)
         } else {
-            set := tm.AddSet(set)
+            _ = tm.AddSet(set)
         }
     }
     
-    xml_panes := root.FindElements('//layer')
+    xml_panes := root.FindElements("//layer")
     
-    for xml_pane, i := range xml_panes {
-        pane, err := LoadLayer(xml_pane, i, tm.Sets)
+    for i, xml_pane := range xml_panes {
+        pane, err := LoadPane(xml_pane, i, tm)
         if err != nil {
             return nil, fmt.Errorf("Could not load tile layer: %s", err)
         } else {
@@ -411,10 +452,16 @@ func LoadMapXml(doc * etree.Document) (* Map, error) {
 // Return nil on error.
 func LoadMap(filename string) (* Map, error) {
     doc := etree.NewDocument()
-    err = doc.ReadFromFile(filename)
+    err := doc.ReadFromFile(filename)
     if err != nil {
         return nil, err
     }
     return LoadMapXml(doc)
 }
 
+
+func LoadMapFifi(relname string) (* Map, error) {
+    realname := fifi.Map(relname)
+    return LoadMap(realname)
+}
+

+ 10 - 10
engine/tile/map.go

@@ -1,10 +1,10 @@
 package tile
 
 // import "fmt"
-
+// import "log"
 import "gitlab.com/beoran/al5go/al"
 // import "gitlab.com/beoran/ebsgo/engine/geometry/point"
-import "gitlab.com/beoran/ebsgo/engine/physics/camera"
+import "gitlab.com/beoran/ebsgo/engine/physics"
 // import "gitlab.com/beoran/ebsgo/engine/fifi"
 
 
@@ -68,11 +68,11 @@ func (tm * Map) Set(index int) * Set {
 }
 
 func (tm * Map) AddSet(set * Set) * Set {
-    tm.Panes = append(tm.Panes, set)
+    tm.Sets = append(tm.Sets, set)
     return set
 }
 
-func (tm * Map) NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) {
+func (tm * Map) NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) * Set {
     set := NewSet(sheet, tile_w, tile_h, firstgid)
     return tm.AddSet(set)
 }
@@ -83,7 +83,7 @@ func (tm * Map) NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) {
  * In case there are several matches the first mathing tile set is returned
  */
 func (tm * Map) LookupTmxTileset(tmx_index int) * Set{
-    for set := range tm.Sets {
+    for _, set := range tm.Sets {
         if  tmx_index >= set.FirstGID {
             return set
         }
@@ -114,17 +114,17 @@ func (tm * Map) SetTileIndex(l, x, y, index int) * Tile {
 
 
 /** Draws a tile map. */
-func (tm * Map) Draw(camera * camera.Camera) { 
-    var floor * Pane
+func (tm * Map) Draw(camera * physics.Camera) {
+    // var floor * Pane
     for i := 0 ; i < len(tm.Panes) ; i ++ {
         pane := tm.Panes[i]
         if pane != nil {
             pane.DrawTiles(camera)
             if ( i % 2 ) == 0 {
-                pane.DrawBlends(camera)
-                floor =  pane
+                // pane.DrawBlends(camera)
+                // floor =  pane
             } else if (i % 2) == 1 {
-                pane.DrawShadowsOn(floor, camera)
+                // pane.DrawShadowsOn(floor, camera)
             }
         }
     }

+ 17 - 7
engine/tile/pane.go

@@ -4,7 +4,7 @@ import "fmt"
 
 import "gitlab.com/beoran/al5go/al"
 // import "gitlab.com/beoran/ebsgo/engine/geometry/point"
-import "gitlab.com/beoran/ebsgo/engine/physics/camera"
+import "gitlab.com/beoran/ebsgo/engine/physics"
 import "gitlab.com/beoran/ebsgo/engine/fifi"
 
 
@@ -180,6 +180,7 @@ type Cell struct {
     * Tile
     Blend * al.Bitmap
     Flags   int
+    Rotate  float32
     Shadows [][8]float32 // Slice of shadow polygons.
 }
 
@@ -198,9 +199,18 @@ type Pane struct  {
   PixelH         int
   // This bitmap is the atlas used for blends. XXX: Not implemented yet.
   Blends         * al.Bitmap
-  Name           string  
+  Name           string
 };
 
+/* First group id of the tile set of the plane. */
+func (pane * Pane) FirstGID() int {
+    if pane.Tileset == nil {
+        return 0
+    } else {
+        return pane.Tileset.FirstGID
+    }
+}
+
 
 /** Initializes a tile pane.
 * The pane will not clean up the tile set itself!
@@ -277,7 +287,7 @@ func (pane * Pane) SetBlend(gridx, gridy int, blend * al.Bitmap) * al.Bitmap {
 }
 
 
-func (pane * Pane) SetFlags(gridx, gridy, flags int) int {
+func (pane * Pane) SetFlags(gridx, gridy int, flags int) int {
     if pane.Outside(gridx, gridy) {
         return -1
     }
@@ -314,7 +324,7 @@ type DrawIterator func (cell * Cell, x, y float32, tx, ty int)
  * This must be done in one call because changing the source bitmap is less optimal. 
  * You must hold bitmap drawing yourself if applicable.
  */
-func (pane * Pane) DrawIterate(camera * camera.Camera, iter DrawIterator) { 
+func (pane * Pane) DrawIterate(camera * physics.Camera, iter DrawIterator) { 
     gridwide := pane.GridW
     gridhigh := pane.GridH
     tilewide := pane.Tileset.TileW
@@ -353,7 +363,7 @@ func (pane * Pane) DrawIterate(camera * camera.Camera, iter DrawIterator) {
 
 /* Draws the tiles of the pane. This must be done in one call because
  * changing the source bitmap is less optimal. */
-func (pane * Pane) DrawTiles(camera * camera.Camera) { 
+func (pane * Pane) DrawTiles(camera * physics.Camera) { 
     gridwide := pane.GridW
     gridhigh := pane.GridH
     tilewide := pane.Tileset.TileW
@@ -396,7 +406,7 @@ func (pane * Pane) DrawTiles(camera * camera.Camera) {
 
 /* Draws the blends of the pane. This must be done in one call because
  * changing the source bitmap is less optimal. */
-func (pane * Pane) DrawBlends(camera * camera.Camera) { 
+func (pane * Pane) DrawBlends(camera * physics.Camera) { 
     gridwide := pane.GridW
     gridhigh := pane.GridH
     tilewide := pane.Tileset.TileW
@@ -508,7 +518,7 @@ func (pane * Pane) DrawOneShadowOn(cell * Cell, pane_below * Pane,
  * "classic" shadows cast as if the sun were in the south-west,
  * with the shadows pointing north-east.
 */
-func (pane * Pane) DrawShadowsOn(pane_below * Pane, camera * camera.Camera) { 
+func (pane * Pane) DrawShadowsOn(pane_below * Pane, camera * physics.Camera) { 
     gridwide := pane.GridW
     gridhigh := pane.GridH
     tilewide := pane.Tileset.TileW

+ 44 - 30
engine/tile/tile.go

@@ -1,11 +1,11 @@
 package tile
 
-// import "log"
+import "gitlab.com/beoran/ebsgo/monolog"
 // import "os"
 // import "math"
 
 import "gitlab.com/beoran/al5go/al"
-import "gitlab.com/beoran/ebsgo/engine/geometry/point"
+import "gitlab.com/beoran/ebsgo/engine/geometry"
 
 const TILE_W = 32
 const TILE_H = 32
@@ -17,8 +17,8 @@ var showSolid = false;
 type Set struct { 
   Tiles     []Tile
   Sheet     * al.Bitmap
-  W         int
-  H         int
+  W         int // Width, in tiles
+  H         int // Height, in tiles
   TileW     int
   TileH     int
   FirstGID  int  /* Offset of tile set in TMX map file. Used to  correct tile offsets. */
@@ -40,7 +40,7 @@ type Frame struct {
 type Tile struct {
   Tileset       * Set   /* Tileset this tile belongs to */
   Index           int   /* Index in the tile set. */  
-  Flags           int   /* Information about the tile's properties. */
+  Flags           int  /* Information about the tile's properties. */
   Kind            int
 
   /* Offset to the tile to skip to when animating. If this is
@@ -59,14 +59,14 @@ type Tile struct {
   /* Time in s to wait before jumping to the next frame of this tile. */  
   Time          float64
   /* Time since last animation in s. */
-  Position      point.P
+  Position      geometry.Vector
   /* Automatic blending activation and priority. */
   Blend         int
   /* Mask number to use for automatic blending, if any. */
   BlendMask     int
   /* Automatic lighting activation flag. */
   Light         int
-  LightMAsk     int
+  LightMask     int
   /* Automatic shadow activation flag. */
   Shadow        int
   /* Automatic shadow mask number. */
@@ -85,13 +85,16 @@ type Tile struct {
 
 
 func NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) * Set {
+    if sheet == nil {
+        return nil
+    }
     set := &Set{}
     set.Sheet       = sheet
     set.TileW       = tile_w
     set.TileH       = tile_h
     set.FirstGID    = firstgid
     set.W           = sheet.Width() / set.TileW
-    set.H           = sheet.Height()/ set.TileH
+    set.H           = sheet.Height() / set.TileH
     size           := set.W * set.H
     set.Tiles       = make([]Tile, size)
     for i := 0 ; i < size; i ++ {
@@ -103,16 +106,17 @@ func NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) * Set {
 func (tile * Tile) Init(set * Set, index int) {
     tile.Tileset    = set
     tile.Index      = index
+    tile.Active     = index
     tile.Wait       = 0.25
     tile.Recalculate()
 } 
 
 func (tile Tile) SheetY(set * Set) int {
-    return (tile.Active * set.TileW) / (set.W * set.TileH)
+    return (tile.Active / set.W) * set.TileH
 }
 
 func (tile Tile) SheetX(set * Set) int {
-    return (tile.Active * set.TileW) % (set.W)
+    return (tile.Active % set.W) * set.TileW
 }
 
 
@@ -122,15 +126,15 @@ func (tile * Tile) Recalculate() {
         return
     }
     
-    x := float64(tile.SheetX(tile.Tileset))
-    y := float64(tile.SheetY(tile.Tileset))
+    x := float32(tile.SheetX(tile.Tileset))
+    y := float32(tile.SheetY(tile.Tileset))
         
-    tile.Position = point.New(x, y)
+    tile.Position = geometry.NewVector(x, y)
 }
 
 
-func (set * Set) Tile(index int) * Tile {
-    if nil != set && index >= 0 && index <= len(set.Tiles) {
+func (set Set) Tile(index int) * Tile {
+    if index >= 0 && index <= len(set.Tiles) {
         return &set.Tiles[index]
     } 
     return nil
@@ -255,6 +259,10 @@ func (tile * Tile) UpdateAnimation(dt float64) {
     }
   }
   
+  monolog.Log("TILE", "Animation for tile: %d %d %f %f\n", 
+    tile.ActiveFrame, frame.Index, frame.Duration, tile.Time)
+
+  
   tile.Time += dt // advance animation time of tile. 
   // Don't animate if not enough time has passed
   if tile.Time < frame.Duration {
@@ -268,10 +276,15 @@ func (tile * Tile) UpdateAnimation(dt float64) {
   // Get new tile frame
   frame = tile.Frame(tile.ActiveFrame);
   // If we get here, reset animation time.
+
   tile.Time     = 0.0
   if nil == frame {
        return
   }
+  
+  monolog.Log("TILE", "Animation for tile: %d %d %f %f %b\n", 
+    tile.ActiveFrame, frame.Index, frame.Duration, tile.Time)
+
   // Get the active tile to use from the animation frame
   active   = frame.Index
   aidtile := tile.Tileset.Tile(active);
@@ -279,10 +292,11 @@ func (tile * Tile) UpdateAnimation(dt float64) {
   if nil == aidtile {
       return
   }
-  // If there is no such tile, don't change the active tile of this tile.
-  tile.Active = active 
+  // If there is such a tile, change the active tile index of this tile.
+  tile.Active = active
   // Finally recalculate tile position.
-  tile.Recalculate();
+  tile.Recalculate()
+  monolog.Log("TILE", "Updated animation for tile: %d\n", tile.Active)
 }
 
 /* Animates the tile. Animates the tile if it has animation frames. */
@@ -305,6 +319,18 @@ func (set * Set) Update(dt float64) {
   }
 } 
 
+/** Recalculates all tiles in a tile set so they all get the rght position. */
+func (set * Set) Recalculate() {
+  if nil == set.Tiles {
+       return
+  }
+  
+  for i := 0 ; i < len(set.Tiles); i ++ {
+        tile := &set.Tiles[i]
+        tile.Recalculate()
+  }
+} 
+
 
 /** Draw a tile to the current active drawing target at the
 given coordinates. Does nothing if tile is NULL.  */
@@ -390,15 +416,3 @@ func (tile * Tile) DrawMaskedTo(result * al.Bitmap, mask * al.Bitmap, angle floa
 }
 
 
-func (set Set) Tile(index int) * Tile {
-    if set.Tiles == nil {
-        return nil
-    }
-    
-    if (index < 0) || (index > len(set.Tiles)) {
-        return nil
-    }
-    
-    return &set.Tiles[index]
-}
-

+ 289 - 0
monolog/monolog.go

@@ -0,0 +1,289 @@
+/* A package that provides advanced logging facilities. */
+package monolog
+
+import (
+    "fmt"
+    "os"
+    "path/filepath"
+    "runtime"
+    "strings"
+    "time"
+    "unicode"
+)
+
+type Logger interface {
+    Log(level string, file string, line int, format string, args ...interface{})
+    Close()
+}
+
+func GetCallerName(depth int) {
+    pc := make([]uintptr, depth+1)
+    runtime.Callers(depth, pc)
+    for i, v := range pc {
+        fun := runtime.FuncForPC(v)
+        if fun != nil {
+            fmt.Printf("GetCallerName %d %s\n", i, fun.Name())
+        } else {
+            fmt.Printf("GetCallerName %d nil\n", i)
+        }
+    }
+}
+
+func (me *FileLogger) WriteLog(depth int, level string, format string, args ...interface{}) {
+    _, file, line, ok := runtime.Caller(depth)
+
+    if !ok {
+        file = "unknown"
+        line = 0
+    }
+
+    me.Log(level, file, line, format, args...)
+}
+
+func (me *FileLogger) NamedLog(name string, format string, args ...interface{}) {
+    me.WriteLog(2, name, format, args...)
+}
+
+func (me *FileLogger) Info(format string, args ...interface{}) {
+    me.WriteLog(2, "INFO", format, args...)
+}
+
+func (me *FileLogger) Warning(format string, args ...interface{}) {
+    me.WriteLog(2, "WARNING", format, args...)
+}
+
+func (me *FileLogger) Error(format string, args ...interface{}) {
+    me.WriteLog(2, "ERROR", format, args...)
+}
+
+func (me *FileLogger) Fatal(format string, args ...interface{}) {
+    me.WriteLog(2, "FATAL", format, args...)
+}
+
+func (me *FileLogger) Debug(format string, args ...interface{}) {
+    me.WriteLog(2, "DEBUG", format, args...)
+}
+
+type FileLogger struct {
+    filename string
+    file     *os.File
+}
+
+func (me *FileLogger) Close() {
+    if me.file != nil {
+        me.file.Close()
+    }
+    me.file = nil
+}
+
+func (me *FileLogger) Log(level string, file string, line int, format string, args ...interface{}) {
+    fileshort := filepath.Base(file)
+    now := time.Now().Format(time.RFC3339)
+    fmt.Fprintf(me.file, "%s: %s: %s: %d: ", now, level, fileshort, line)
+    if args != nil && len(args) > 0 {
+        fmt.Fprintf(me.file, format, args...)
+    } else {
+        fmt.Fprint(me.file, format)
+    }
+    fmt.Fprint(me.file, "\n")
+}
+
+func NewFileLogger(filename string) (logger Logger, err error) {
+    file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
+
+    if err != nil {
+        return nil, err
+    }
+
+    return &FileLogger{filename, file}, nil
+}
+
+func NewStderrLogger() (logger Logger, err error) {
+    return &FileLogger{"/dev/stderr", os.Stderr}, nil
+}
+
+func NewStdoutLogger() (logger Logger, err error) {
+    return &FileLogger{"/dev/stderr", os.Stdout}, nil
+}
+
+type Logbook struct {
+    loggers []Logger
+    levels  map[string]bool
+}
+
+func NewLog() *Logbook {
+    loggers := make([]Logger, 32)
+    levels := make(map[string]bool)
+    return &Logbook{loggers, levels}
+}
+
+func (me *Logbook) AddLogger(logger Logger) {
+    me.loggers = append(me.loggers, logger)
+}
+
+func (me *Logbook) EnableLevel(level string) {
+    me.levels[level] = true
+}
+
+func (me *Logbook) DisableLevel(level string) {
+    me.levels[level] = false
+}
+
+func (me * Logbook) GetLevels() map[string] bool {
+    return me.levels
+}
+
+func enableDisableSplitter(c rune) bool {
+    ok := (!unicode.IsLetter(c))
+    ok = ok && (!unicode.IsNumber(c))
+    ok = ok && (c != '_')
+    return ok
+}
+
+func (me *Logbook) EnableLevels(list string) {
+    to_enable := strings.FieldsFunc(list, enableDisableSplitter)
+    for _, level := range to_enable {
+        me.EnableLevel(level)
+    }
+}
+
+func (me *Logbook) DisableLevels(list string) {
+    to_disable := strings.FieldsFunc(list, enableDisableSplitter)
+    for _, level := range to_disable {
+        me.DisableLevel(level)
+    }
+}
+
+func (me *Logbook) LogVa(name string, file string, line int, format string, args ...interface{}) {
+    enabled, ok := me.levels[name]
+
+    if (!ok) || (!enabled) {
+        return
+    }
+
+    for _, logger := range me.loggers {
+        if logger != nil {
+            logger.Log(name, file, line, format, args...)
+        }
+    }
+}
+
+func (me *Logbook) Close() {
+    for index, logger := range me.loggers {
+        if logger != nil {
+            logger.Close()
+            me.loggers[index] = nil
+        }
+    }
+}
+
+func (lb *Logbook) EnabledLevels() []string {
+    var res []string
+    for name, ll := range lb.levels {
+        if ll {
+            res = append(res, name)
+        }
+    }
+    return res
+}
+
+var DefaultLog *Logbook
+
+func init() {
+    DefaultLog = NewLog()
+    // runtime.SetFinalizer(DefaultLog, DefaultLog.Close)
+}
+
+func EnableLevel(level string) {
+    DefaultLog.EnableLevel(level)
+}
+
+func DisableLevel(level string) {
+    DefaultLog.DisableLevel(level)
+}
+
+func EnableLevels(list string) {
+    DefaultLog.EnableLevels(list)
+}
+
+func DisableLevels(list string) {
+    DefaultLog.DisableLevels(list)
+}
+
+func GetLevels() map[string] bool {
+    return DefaultLog.GetLevels();
+}
+
+func AddLogger(logger Logger, err error) {
+    if err == nil {
+        DefaultLog.AddLogger(logger)
+    }
+}
+
+func Setup(name string, stderr bool, stdout bool) {
+    if name != "" {
+        AddLogger(NewFileLogger(name))
+    }
+
+    if stderr {
+        AddLogger(NewStderrLogger())
+    }
+
+    if stdout {
+        AddLogger(NewStdoutLogger())
+    }
+
+    EnableLevel("INFO")
+    EnableLevel("WARNING")
+    EnableLevel("ERROR")
+    EnableLevel("FATAL")
+}
+
+func Close() {
+    DefaultLog.Close()
+}
+
+func WriteLog(depth int, name string, format string, args ...interface{}) {
+    _, file, line, ok := runtime.Caller(depth)
+    if !ok {
+        file = "unknown"
+        line = 0
+    }
+    DefaultLog.LogVa(name, file, line, format, args...)
+}
+
+func Log(name string, format string, args ...interface{}) {
+    WriteLog(2, name, format, args)
+}
+
+func Printf(format string, args ...interface{}) {
+    WriteLog(2, "INFO", format, args...)
+}
+
+func Info(format string, args ...interface{}) {
+    WriteLog(2, "INFO", format, args...)
+}
+
+func Warning(format string, args ...interface{}) {
+    WriteLog(2, "WARNING", format, args...)
+}
+
+func Error(format string, args ...interface{}) {
+    WriteLog(2, "ERROR", format, args...)
+}
+
+func Fatal(format string, args ...interface{}) {
+    WriteLog(2, "FATAL", format, args...)
+}
+
+func Debug(format string, args ...interface{}) {
+    WriteLog(2, "DEBUG", format, args...)
+}
+
+func エラ(err error) {
+    WriteLog(2, "ERROR", "%s", err.Error())
+}
+
+func WriteError(err error) {
+    WriteLog(2, "ERROR", "%s", err.Error())
+}