Kaynağa Gözat

Work on support for tile maps.

Beoran 7 yıl önce
ebeveyn
işleme
063e2bdd2b

+ 8 - 0
ebs.go

@@ -3,6 +3,8 @@ package main
 import "fmt"
 import "fmt"
 import "flag"
 import "flag"
 import "gitlab.com/beoran/ebsgo/engine/global"
 import "gitlab.com/beoran/ebsgo/engine/global"
+import "gitlab.com/beoran/ebsgo/engine/fif"
+
 // import "path/filepath"
 // import "path/filepath"
 // import "math/rand"
 // import "math/rand"
 
 
@@ -16,6 +18,12 @@ var fullscreen_flag = flag.Bool("fullscreen", false, "Run fullscreen or not")
 
 
 func main() {
 func main() {
     state := global.State{}
     state := global.State{}
+    if ! fifi.Initialize() {        
+        fmt.Printf("Could not find data file directory.")
+        return
+    }
+
+    
     if ! state.InstallAllegro() {
     if ! state.InstallAllegro() {
         fmt.Printf("Could not initialize allegro.")
         fmt.Printf("Could not initialize allegro.")
         return
         return

+ 77 - 0
engine/fifi/fifi.go

@@ -0,0 +1,77 @@
+package fifi
+
+import "os"
+import "path/filepath"
+
+import al "gitlab.com/beoran/al5go/al"
+
+
+// Find files 
+
+
+var DataPath string = ""
+
+func isDataPathOK(path string) bool {
+    fn := filepath.Join(path, "eruta.cfg")
+    file, err := os.Open(fn) 
+    file.Close()
+    return err == nil
+}
+
+
+func Initialize() bool {
+    DataPath = os.Getenv("EBS_DATA")
+    if isDataPathOK(DataPath) {
+        return true
+    }
+
+    wd, err := os.Getwd()
+    if err == nil { 
+        DataPath = filepath.Join(wd, "data")
+        if isDataPathOK(DataPath) {
+            return true
+        }
+    }
+    return false
+}
+
+func Map(name string) string {
+    return filepath.Join(DataPath, name)
+}
+
+
+/* Fifi contain functionality that helps finding back the file resouces,
+such as images, music, etc that theengine needs.
+
+An important concept here is the "virtual path", that is the path under the 
+location of the data directory. So, for example, if the data of the 
+app is installed on Linux in /usr/share/app/data,
+then a vpath of 
+font/my_nice_font.ttf 
+will be resolved as 
+/usr/share/app/data/font/my_nice_font.ttf.  
+Or, if, on another OS, the data of the app is installed in 
+C:\Program Files\App\data
+then the same vpath will refer to
+C:\Program Files\App\data\font\my_nice_font.ttf.
+
+So Fifi is a way to get OS-independence and location-independence of 
+the data files all at once. 
+
+For save files or scores, the vpath is similar, but relative to the
+"writeable" directory of the application.
+
+*/
+
+func LoadBitmap(filename string) * al.Bitmap {
+    return al.LoadBitmap(Map(filename))
+}
+
+
+
+
+
+
+
+
+

+ 1 - 1
engine/geometry/point/point.go

@@ -6,7 +6,7 @@ type P struct {
 }
 }
 
 
 
 
-func New(x, y Float64) P {
+func New(x, y float64) P {
     p := P{ x, y } 
     p := P{ x, y } 
     return p
     return p
 }
 }

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

@@ -0,0 +1,23 @@
+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
+}
+

+ 420 - 0
engine/tile/io.go

@@ -0,0 +1,420 @@
+package tile
+
+import "fmt"
+import "strconv"
+import "io/ioutil"
+import "compress/gzip"
+import "compress/zlib"
+import "encoding/csv"
+import "encoding/binary"
+
+
+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/fifi"
+
+import "github.com/beevik/etree"
+
+
+// Tile flipping constants
+const (
+    TMX_FLIPPED_HORIZONTALLY = 0x80000000
+    TMX_FLIPPED_VERTICALLY   = 0x40000000
+    TMX_FLIPPED_DIAGONALLY   = 0x20000000
+    TMX_FLIPPED_FILTER       = (~0xE0000000)
+)
+
+
+func AttrInt(e * etree.Element, string key) (int, error) {
+    return strconv.Atoi(e.SelectAttrValue(key, ""))
+}
+
+func AttrFloat64(e * etree.Element, string key) (float64, error) {
+    return strconv.ParseFloat(e.SelectAttrValue(key, ""), 64)
+}
+
+
+func Property(e * etree.Element, propname string) string, bool {
+  properties := e.FindElements('//properties/property')
+  
+  for property := range properties {
+        name := e.SelectAttrValue("name", "")
+        if name == propname {
+            return e.SelectAttrValue("value", ""), true
+        }
+  }
+  
+  return "", false
+}
+
+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, ""))
+} 
+
+
+func LoadFrame(xml_frame * etree.Element, tile * Tile, set * Set) * Tile {
+    tile_id, err := AttrInt(xml_frame, "tileid")
+    if err != nil {
+        return tile;
+    }
+    
+    // the tile id obtained here is correct and doesn't need to be 
+    // lessened by firstgid
+    
+    duration, err := AttrFloat64(xml_frame, "duration")
+    
+    if err != nil {
+        return tile;
+    }
+    
+    /* Convert ms to s. */ 
+    duration /= 1000.0
+    
+    tile.AddAnimationFrame(tile_id, duration)
+    
+    return tile
+}
+
+
+func LoadAnimation(xml_tile * etree.Element, tile * Tile, set * Set) * Tile {
+    xml_anim := xml_tile.FindElement("//animation")
+    if xml_anim == nil {
+        return tile;
+    }
+    
+    xml_frames := xml_anim.FindElements("//frame")
+    for xml_frame := range xml_frames {
+        LoadFrame(xml_frame, tile, set)
+    }
+    return tile
+}
+
+func XmlToTileType(xml_tile * etree.Element) int, error {
+    
+    value , ok := Property(e, "type")
+    if (!ok) {
+        return 0, fmt.Errorf("no type property")
+    }
+    
+    result, ok  := FlagNames[value]
+    
+    if ! ok {
+        return 0, fmt.Errorf("unknown type property")
+    }
+    
+    return result, nil
+} 
+
+
+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)
+    }
+  
+    tile := set.Tile(id)
+    if tile == nil { 
+        return nil, fmr.Errorf("Tile id not found in tile set: %d", id)
+    }
+    
+    if tiletype, err := XmlToTileType(xml_tile) ; err == nil {
+        tile.Type = tiletype
+    }
+    
+    // ianim       , err := PropertyInt(xml_tile, "anim"       )
+    // iwait       , err := PropertyInt(xml_tile, "wait"       )
+    ilight      , err := PropertyInt(xml_tile, "light"      )
+    iblend      , err := PropertyInt(xml_tile, "blend"      )
+    ishadow     , err := PropertyInt(xml_tile, "shadow"     )
+    ilightmask  , err := PropertyInt(xml_tile, "lightmask"  )
+    iblendmask  , err := PropertyInt(xml_tile, "blendmask"  )
+    ishadowmask , err := PropertyInt(xml_tile, "shadowmask" )
+    
+    // No support anymore for "classic" animations since TMX now 
+    // supports animations.
+    tile.Light      = ilight
+    tile.Blend      = iblend
+    tile.Shadow     = ishadow
+    tile.LightMask  = ilightmask
+    tile.BlendMask  = iblendmask
+    tile.ShadowMask = ishadowmask
+
+    /* Support tiled-style animations. */
+    return LoadAnimation(xml_tile, tile, set)
+}
+
+
+/** Calculates index and the Allegro draw flags and rotation for a tile. */
+
+func TmxTileIndexFlagsAndRotation(tmx_index int32) int, int, float32 { 
+    index := int(tmx_index & TMX_FLIPPED_FILTER) 
+    flags := 0
+    rotate := 0.0
+
+    if (tmx_index & TMX_FLIPPED_HORIZONTALLY) != 0 {
+        flags |= al.FLIP_HORIZONTAL
+    }
+   
+    if (tmx_index & TMX_FLIPPED_VERTICALLY) != 0 {
+        flags |= al.FLIP_VERTICAL
+    } 
+  
+    if (tmx_index & TMX_FLIPPED_DIAGONALLY) {
+        rotate := al.PI / 2.0
+    }
+
+  return index, flags, rotate
+}
+
+
+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)
+    }
+    
+    xml_image := xml_set.FindElement("//image")
+    
+    if xml_image == nil {
+        return nil, fmt.Errorf("Could not find image data for tile set %d", index)
+    }
+    
+    tile_w, err := AttrInt("tilewidth")
+    tile_h, err := AttrInt("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)
+    }
+    
+    iname   := xml_image.SelectAttrValue("source", "tile_set_default.png")    
+    bmp     := fifi.LoadBitmap("map/" + iname)
+    set     := NewSet(bmp, tile_w, tile_h, firstgid)
+    
+    xml_tiles := xml_set.FindElements("//tile")
+    
+    for xml_tile, tile_index := range xml_tiles {
+        LoadTile(xml_tile, tile_index, set, tm)
+    }
+    
+    return set, nil
+}
+
+type Expander(in []byte) []byte, error 
+
+type Decoder(data string, w, h int, expand Expander) [][]uint32, error 
+
+
+func CsvDecoder(data string, w, h int, expand Expander) [][]uint32, error {
+    
+    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)
+            }
+        }
+    }
+    return result
+}
+
+func GzipExpander(in []byte) []byte, error {
+    zr, err := gzip.NewReader(&in)
+    if err != nil {
+        return nil, err
+    }
+    return ioutil.ReadAll(zr)
+}
+
+func ZlibExpander(in []byte) []byte, error {
+    zr, err := lib.NewReader(&in)
+    if err != nil {
+        return nil, err
+    }
+    return ioutil.ReadAll(zr)
+}
+
+
+func Base64Decoder(data string, w, h int, expand Expander) [][]uint32, error {
+    bytes, err := base64.StdEncoding.DecodeString()
+    if err != nil {
+        return nil, err
+    }
+    
+    if expand != nil {
+        bytes, err = expand(bytes)
+        if err != nil {
+            return nil, err
+        }
+    }
+    
+    result := make([][]uint32, h) 
+    for i := 0 ; i < h; i++ { 
+        result[i] := make([]uint32, w) 
+        for j := 0 ; i < w ; j++ { 
+            index := (i * w + j) * 4
+            value := binary.LittleEndian.Uint32(bytes[index:4])
+            result[i][j] = value
+        }
+    }
+
+    return result, nil
+}
+
+var Decoders map[string]Decoder = map[string]Decoder {
+    "csv"   : CsvDecoder,
+    "base64": Base64Decoder,
+}
+
+var Expanders map[string]Expander = map[string]Expander {
+    "gzip"   : GzipExpander,
+    "zlib": ZlibExpander,
+}
+
+
+
+// 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")
+  
+    if (width < 1) || (height < 1) {
+        return nil, 
+        fmr.Errorf("Layer %i size too small: %d x %d, ", index, width, height)
+    }
+    
+    name := xml_pane.SelectAttrValue("name", "")
+    
+    iset, err := PropertyInt(xml_pane, "tileset")
+    
+    if err != nil {
+        iset = 0
+    }
+    
+    set := tm.Set(iset)
+    
+    if set == nil {
+      return nil, 
+      fmt.Errorf("No tile set found for layer %d: %d", index, iset)
+    }
+    
+    pane := NewPane(set, width, height, name)
+    
+    xml_data := xml_pane.FindElement('//data')
+    
+    if xml_data == nil {
+        return nil, fmr.Errorf("Cannot find layer data for layer %d", index)
+    }
+    
+    encoding    := xml_data.SelectAttrValue("encoding"   , "csv")
+    compression := xml_data.SelectAttrValue("compression", "")
+    
+    decoder     := Decoders[encoding]
+    
+    if decoder == nil {
+        return nil, fmt.Errorf("Unknown encoding %s", encoding)
+    }
+    
+    expander    := Expanders[compression]
+    
+    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 )
+    }
+    
+    for y := 0 : y < height, y ++ {
+        for x := 0 ; x < width ; x++ {
+            tmx_index = grid[y][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)
+                }
+                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 
+            }
+        }
+    }
+    return pane, nil
+}
+
+
+// Load a TMX map file from the given XML document.
+func LoadMapXml(doc * etree.Document) (* Map, error) {
+    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 {
+        return nil, fmt.Errorf("Width not a number: %s", err)
+    }
+    
+    if high, err := AttrInt(root, "height"); err != nil {
+        return nil, fmt.Errorf("Height not a number: %s", err)
+    }
+    
+    if (wide < 1)  || (high < 1) {
+        return nil, fmt.Errorf("Map size too smalll: %d x %d",wide, high)
+    }
+    
+    tm := NewMap(wide, high)
+    
+    xml_sets := root.FindElements('//tileset')
+    
+    for xml_set, i := range xml_sets {
+        set, err := LoadSet(xml_set, i)
+        if err != nil {
+            return nil, fmt.Errorf("Could not load tile set: %s", err)
+        } else {
+            set := tm.AddSet(set)
+        }
+    }
+    
+    xml_panes := root.FindElements('//layer')
+    
+    for xml_pane, i := range xml_panes {
+        pane, err := LoadLayer(xml_pane, i, tm.Sets)
+        if err != nil {
+            return nil, fmt.Errorf("Could not load tile layer: %s", err)
+        } else {
+            tm.AddPane(pane)
+        }
+    }
+      
+  return tm, nil;          
+}
+
+// Loads a TMX tile map from the named file.
+// Return nil on error.
+func LoadMap(filename string) (* Map, error) {
+    doc := etree.NewDocument()
+    err = doc.ReadFromFile(filename)
+    if err != nil {
+        return nil, err
+    }
+    return LoadMapXml(doc)
+}
+

+ 193 - 0
engine/tile/map.go

@@ -0,0 +1,193 @@
+package tile
+
+// 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/fifi"
+
+
+/** A tile map is a tile based  map, roughly equivalent to a 
+ * "level" that uses tiled panes for it's display. 
+ */
+type Map struct {
+    Background    *al.Bitmap
+    Panes       []*Pane
+    Sets        []*Set
+    GridW          int
+    GridH          int
+}
+
+const ( 
+    TEXTURE_TILE    = "tile"
+    TEXTURE_PLAYER  = "play"
+    TEXTURE_GEAR    = "gear"
+    TEXTURE_WEAPON  = "arms"
+    TEXTURE_FOE     = "foes"
+    TEXTURE_ITEM    = "item"
+)
+
+/** Initializes a tile map */
+func (tm * Map) Init(tw, th int) {
+    tm.GridW = tw
+    tm.GridH = th
+    tm.Panes = nil
+}
+
+func NewMap(tw, th int) * Map{
+    tm := &Map{}
+    tm.Init(tw, th)
+    return tm
+}
+
+
+/** Returns a pointer to the pane at index or NULL if out of range. */
+func (tm * Map) Pane(index int) * Pane {
+    if index < 0                { return nil; }
+    if index >= len(tm.Panes)   { return nil; }
+    return tm.Panes[index]
+}
+
+
+func (tm * Map) AddPane(pane * Pane) * Pane {
+    tm.Panes = append(tm.Panes, pane)
+    return pane
+}
+
+func (tm * Map) NewPane(set * Set, tw, th int, name string) * Pane {
+    pane := NewPane(set, tw, th, name)
+    return tm.AddPane(pane)
+}
+
+/** Returns a pointer to the pane at index or NULL if out of range. */
+func (tm * Map) Set(index int) * Set {
+    if index < 0                { return nil; }
+    if index >= len(tm.Sets)   { return nil; }
+    return tm.Sets[index]
+}
+
+func (tm * Map) AddSet(set * Set) * Set {
+    tm.Panes = append(tm.Panes, set)
+    return set
+}
+
+func (tm * Map) NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) {
+    set := NewSet(sheet, tile_w, tile_h, firstgid)
+    return tm.AddSet(set)
+}
+
+
+/* Looks up the tile set to use for the tile, based on it's 
+ * TMX index. this is done based on the firstgid varables of the tile set.
+ * 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 {
+        if  tmx_index >= set.FirstGID {
+            return set
+        }
+    }
+    return nil
+}
+
+
+/** Returns the tile in the tile map in the given layer at the given tile coords. */
+func (tm * Map) Tile(l, x, y int) * Tile {
+    pane   := tm.Pane(l)
+    if pane == nil { return nil; }
+    return pane.Tile(x, y)
+}
+
+/** Sets a tile in the tile map to the given tile. */
+func (tm * Map) SetTile(l, x, y int, tile * Tile) * Tile {
+    pane   := tm.Pane(l)
+    if pane == nil { return nil }
+    return pane.SetTile(x, y, tile)
+}
+/** Sets a tile in the tile map to the tile with the given index. */
+func (tm * Map) SetTileIndex(l, x, y, index int) * Tile {
+    pane   := tm.Pane(l)
+    if pane == nil { return nil }
+    return pane.SetTileIndex(x, y, index)
+}
+
+
+/** Draws a tile map. */
+func (tm * Map) Draw(camera * camera.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
+            } else if (i % 2) == 1 {
+                pane.DrawShadowsOn(floor, camera)
+            }
+        }
+    }
+}
+
+/** Updates the tile map. Currently this animates the tiles. */
+func (tm * Map) Update(dt float64) {
+    for i := 0 ; i < len(tm.Panes) ; i ++ {
+        pane := tm.Panes[i]
+        if pane != nil {
+            pane.Update(dt)
+        }
+    }
+}    
+
+
+/** Sets up the camera so it will stay locked in to the 
+given layer of the tile map */
+
+/*
+Lockin * tilepane_lockin(Tilepane * pane, Camera * camera) {
+  float x, y, w, h;
+  if(!pane) return NULL;
+  x = 0.0;
+  y = 0.0;
+  w = tilepane_wide(pane);  
+  h = tilepane_high(pane);  
+  return camera_newlockin(camera, x, y, w, h);
+} 
+
+
+Lockin * tilemap_layer_lockin(Tilemap * map, 
+                              int layer, 
+                              Camera * camera) {
+  Tilepane * pane;
+  Lockin * result;
+  float x, y, w, h;
+  if (!map) return NULL;
+  pane = tilemap_pane(map, layer);
+  return tilepane_lockin(pane, camera);  
+}
+
+
+
+Area * tilemap_area(Tilemap * self) {
+  if(!self) return NULL;
+  return self->area;
+}
+
+Thing * tilemap_thing(Tilemap * self, int index) {
+  Area * area = tilemap_area(self);
+  return area_thing(area, index);
+}
+*/
+
+
+func (tm * Map) SetupAfterLoad() {
+    for i := 0 ; i < len(tm.Panes) ; i ++ {
+        pane := tm.Panes[i]
+        if pane != nil {
+            pane.InitBlend(i)
+        }
+    }    
+}
+
+

+ 655 - 0
engine/tile/pane.go

@@ -0,0 +1,655 @@
+package tile
+
+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/fifi"
+
+
+
+/* Tile blending direction constants. */
+
+const (
+    BLEND_NORTHWEST            = 0 
+    BLEND_NORTH                = 1
+    BLEND_NORTHEAST            = 2
+    BLEND_WEST                 = 3
+    BLEND_EAST                 = 4
+    BLEND_SOUTHWEST            = 5
+    BLEND_SOUTH                = 6
+    BLEND_SOUTHEAST            = 7
+    BLEND_DIRECTION_MAX        = 8
+)
+
+/* Tile blending shapes */
+const (
+    BLEND_CORNER               = 0
+    BLEND_SIDE                 = 1
+    BLEND_SHAPE_MAX            = 2
+)
+
+/* Tile blending types. */
+const (
+    BLEND_SHARP                = 0
+    BLEND_GRADUAL              = 1
+    BLEND_FUZZY                = 2
+    BLEND_FUZZY_GRADUAL        = 3
+    BLEND_TYPE_MAX             = 4
+)
+
+const BLEND_BITMAPS_MAX = 255 
+
+/* A cache of generated generic blending masks. */
+var BlendMasks [BLEND_TYPE_MAX][BLEND_SHAPE_MAX]*al.Bitmap 
+
+
+/* Lookup array with info on how the mask should be applied, i. e. flipped. */
+var BlendFlags [BLEND_DIRECTION_MAX]int = [BLEND_DIRECTION_MAX] int{
+  0,
+  0,
+  al.FLIP_HORIZONTAL,
+  0,
+  al.FLIP_HORIZONTAL,
+  al.FLIP_VERTICAL,
+  al.FLIP_VERTICAL,
+  al.FLIP_HORIZONTAL | al.FLIP_VERTICAL,
+}
+
+/* Lookup array with info on which "side" mask should be used. */
+var BlendSides [BLEND_DIRECTION_MAX]int = [BLEND_DIRECTION_MAX]int {
+  BLEND_CORNER,
+  BLEND_SIDE  ,
+  BLEND_CORNER,
+  BLEND_SIDE  ,
+  BLEND_SIDE  ,
+  BLEND_CORNER,
+  BLEND_SIDE  ,
+  BLEND_CORNER,
+}
+
+/* Lookup array with info on how the mask should be turned, i. e. rotated. */
+var BlendAngles [BLEND_DIRECTION_MAX]float32 = [BLEND_DIRECTION_MAX]float32 {
+  0.0,
+  0.0,
+  0.0,
+  3 * al.PI / 2.0,
+  al.PI / 2.0,
+  0.0,
+  0.0,
+  0.0,
+}
+
+/* 
+ 
+ ## Implementation of automatic overlapping ##
+ 
+ In the simple case where there are only 2 different kinds of tiles, 
+ then for a tile somewhere in the map, that tile has 8 neighbours, and thus 
+ there are 8**2 or 256 different possible layouts of these neighbours around that 
+ single tile, and 256 different blends would be needed.
+ 
+ Of course, this is a staggering amount for hand drawn graphics, and even for 
+ automatically generated mask images, this takes up a hefty chunk of memory,
+ so some kind of simplification is needed. 
+ 
+ The first simplification is the use of the tile's blend value as a blend *priority*
+ value. The tile with the lower blend priority will be blended with the 
+ tile with the higher blend priority blending over it. Tiles with equal blend 
+ priority will simply not blend. This reduces the possibilities because in 
+ cases where 3 or more tyle types must blend, without priorities, 
+ the number of possible blends would become even larger. 
+ 
+ Let's consider the possibilities takng symmetry in account. If only 2 tiles 
+ that will blend are adjacent, there are 8 possibilities. However there are 4 
+ symmetric rotations of the 2 cases of the 2 tiles being either side-to-side 
+ or corner to corner. In case of side-to side, the blend should go rougmly like 
+ this: (scaled down to 8x8
+ 
+ ..........OOOOOOOO     ................OO
+ ..........OOOOOOOO     ..............OOOO
+ ..........OOOOOOOO     ............OOOOOO
+ ..........OOOOOOOO     ............OOOOOO
+ ..........OOOOOOOO =>  ............OOOOOO
+ ..........OOOOOOOO     ............OOOOOO
+ ..........OOOOOOOO     ...........OOOOOOO
+ ..........OOOOOOOO     ..........OOOOOOOO
+ 
+ And corner to corner:
+ 
+           OOOOOOOO               OOOOOOOO
+           OOOOOOOO               OOOOOOOO
+           OOOOOOOO               OOOOOOOO
+           OOOOOOOO               OOOOOOOO
+           OOOOOOOO =>            OOOOOOOO
+           OOOOOOOO               OOOOOOOO
+           OOOOOOOO               .OOOOOOO
+           OOOOOOOO               ..OOOOOO
+ ..........             ..........        
+ ..........             ..........        
+ ..........             ..........        
+ ..........             ..........        
+ ..........         =>  ..........        
+ ..........             ..........        
+ ..........             ..........        
+ ..........             ..........        
+ 
+ If the masks are judiciouly chosen, and all align correctly, 
+ then it will suffice to have just 2 mask images which get rotated as needed,
+ one mask for the side to side, and one for corner to corner. 
+ Each of the masks follows a strict pattern, that is, the side by side 
+ has a \__/ - like shape were the depth of the overlap is at most 1/4th of the 
+ tile height. (Or is it 1/3, let's check it out). 
+ 
+ Then, for every overlapping tile, an overlap bitmap can be generated 
+ when loading the tile layer it is in, and that bitmap can then be used to 
+ draw the tile in stead of the original bitmap. 
+
+
+## Implementation of automatic shadows ##
+
+Every cell in the tile pane contains a slice of shadow polygons,
+that is set up before rendering. This should in the future be used in stead of 
+the current live polygons generated at draw time but this will require 
+an approach where a camera transform is applied whilst drawing in stead of
+the current situation where we use the camera info directly to calculate
+the draw positions. 
+
+*/
+
+func PrepareBlend(blend * al.Bitmap, tile * Tile, blentile * Tile, direction int ) * al.Bitmap {
+    side  := BlendSides[direction]
+    angle := BlendAngles[direction]
+    flags := BlendFlags[direction]
+    maskid:= tile.BlendMask
+    mask  := BlendMasks[maskid][side]
+    if mask == nil {
+        return nil
+    } else {
+        tile.DrawMaskedTo(blend, mask, angle, flags)
+        return blend
+    }
+}
+
+/** 
+ * A Cell is an individual part of a pane. It contains a tile,
+ * a blend bitmap, two shadow polygons, and rendering flags.
+ */
+type Cell struct {
+    * Tile
+    Blend * al.Bitmap
+    Flags   int
+    Shadows [][8]float32 // Slice of shadow polygons.
+}
+
+
+/**
+* A Pane is a layer of tiles for use in a tile map or tiled
+* background. A pane consists of individual tiles from the same
+* tile set. Different panes may have different tile sets.
+*/
+type Pane struct  {
+  Tileset   *    Set
+  Cells      [][]Cell
+  GridW          int
+  GridH          int
+  PixelW         int
+  PixelH         int
+  // This bitmap is the atlas used for blends. XXX: Not implemented yet.
+  Blends         * al.Bitmap
+  Name           string  
+};
+
+
+/** Initializes a tile pane.
+* The pane will not clean up the tile set itself!
+*/
+func (pane * Pane) Init(set * Set, gridw, gridh int, name string) (* Pane) {
+    pane.Tileset = set
+    pane.GridW   = gridw
+    pane.GridH   = gridh
+    pane.PixelW  = pane.GridW * set.TileW
+    pane.PixelH  = pane.GridW * set.TileH
+    pane.Name    = name
+    pane.Cells   = make([][]Cell, pane.GridH)
+    for i := range (pane.Cells) {
+        pane.Cells[i]  = make([]Cell, pane.GridW)
+    }
+    return pane
+}
+
+func NewPane(set * Set, gridw, gridh int, name string) (pane * Pane) {
+    pane = &Pane{}
+    return pane.Init(set, gridw, gridh, name)
+}
+ 
+
+func (pane * Pane) Outside(gridx, gridy int) bool {
+    return ((gridx < 0) || (gridy < 0)) || 
+    ((gridx >= pane.GridW) || (gridy >= pane.GridH))
+}
+
+func (pane * Pane) Cell(gridx, gridy int) * Cell {
+    if pane.Outside(gridx, gridy) {
+        return nil
+    }
+    return & pane.Cells[gridy][gridx]
+}
+
+func (pane * Pane) Tile(gridx, gridy int) * Tile {
+    if pane.Outside(gridx, gridy) {
+        return nil
+    }
+    return pane.Cells[gridy][gridx].Tile
+}
+
+func (pane * Pane) Blend(gridx, gridy int) * al.Bitmap {
+    if pane.Outside(gridx, gridy) {
+        return nil
+    }
+    return pane.Cells[gridy][gridx].Blend
+}
+
+
+func (pane * Pane) SetTile(gridx, gridy int, tile * Tile) * Tile {
+    if pane.Outside(gridx, gridy) {
+        return nil
+    }
+    pane.Cells[gridy][gridx].Tile = tile
+    return tile
+}
+
+func (pane * Pane) SetTileIndex(gridx, gridy, index int) * Tile {
+    return pane.SetTile(gridx, gridy, pane.Tileset.Tile(index))
+}
+
+
+func (pane * Pane) SetBlend(gridx, gridy int, blend * al.Bitmap) * al.Bitmap {
+    if pane.Outside(gridx, gridy) {
+        return nil
+    }
+    if nil != pane.Cells[gridy][gridx].Blend {
+        pane.Cells[gridy][gridx].Blend.Destroy()
+    }
+    pane.Cells[gridy][gridx].Blend = blend
+    return blend
+}
+
+
+func (pane * Pane) SetFlags(gridx, gridy, flags int) int {
+    if pane.Outside(gridx, gridy) {
+        return -1
+    }
+    pane.Cells[gridy][gridx].Flags = flags
+    return flags
+}
+
+
+func (pane * Pane) SetTileRect(gridx, gridy, gridw, gridh int, tile * Tile) * Tile {
+    for jj := gridy ; jj < (gridy + gridh) ; jj++ {
+        for ii := gridx ; ii < (gridx + gridh) ; ii++ {
+            pane.SetTile(ii, jj, tile)
+        }
+    }
+    return tile
+}
+
+func (pane * Pane) Fill(tile * Tile) * Tile {
+    return pane.SetTileRect(0, 0, pane.GridW, pane.GridH, nil)
+}
+
+
+func (cell * Cell) AddShadowPolygon(polygon [8]float32) {
+    cell.Shadows = append(cell.Shadows, polygon)
+}
+
+func (cell * Cell) DeleteShadowPolygons() {
+    cell.Shadows = nil
+}
+
+type DrawIterator func (cell * Cell, x, y float32, tx, ty int)  
+
+/* Draws the tiles of the pane using an iterator function. 
+ * 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) { 
+    gridwide := pane.GridW
+    gridhigh := pane.GridH
+    tilewide := pane.Tileset.TileW
+    tilehigh := pane.Tileset.TileH
+    x        := int(camera.X)
+    y        := int(camera.Y)
+    tx_start := x / tilewide
+    ty_start := y / tilehigh
+    tx_delta := 1 + (int(camera.W) / tilewide)
+    ty_delta := 1 + (int(camera.H) / tilehigh)
+    tx_stop  := tx_start + tx_delta
+    ty_stop  := ty_start + ty_delta
+    tx_index := 0
+    ty_index := 0
+    // realwide := pane.PixelW
+    // realhigh := pane.PixelH
+    // drawflag := 0
+    if tx_start < 0 { tx_start = 0; }
+    if ty_start < 0 { ty_start = 0; }
+    if tx_stop  > gridwide { tx_stop = gridwide; }
+    if ty_stop  > gridhigh { ty_stop = gridhigh; }
+    y_draw    :=  float32(-y + (ty_start * tilehigh))
+    for ty_index = ty_start; ty_index < ty_stop; ty_index++ { 
+        y_draw   += float32(tilehigh)
+        x_draw   := float32(-x + (tx_start * tilewide))
+        row      := pane.Cells[ty_index]
+        for tx_index = tx_start ; tx_index < tx_stop; tx_index++ {
+            x_draw += float32(tilewide)
+            cell := &row[tx_index]
+            iter(cell, x_draw, y_draw, tx_index, ty_index)
+        }
+    }
+}
+
+
+
+/* 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) { 
+    gridwide := pane.GridW
+    gridhigh := pane.GridH
+    tilewide := pane.Tileset.TileW
+    tilehigh := pane.Tileset.TileH
+    x        := int(camera.X)
+    y        := int(camera.Y)
+    tx_start := x / tilewide
+    ty_start := y / tilehigh
+    tx_delta := 1 + (int(camera.W) / tilewide)
+    ty_delta := 1 + (int(camera.H) / tilehigh)
+    tx_stop  := tx_start + tx_delta
+    ty_stop  := ty_start + ty_delta
+    tx_index := 0
+    ty_index := 0
+    // realwide := pane.PixelW
+    // realhigh := pane.PixelH
+    // drawflag := 0
+    al.HoldBitmapDrawing(true)
+    if tx_start < 0 { tx_start = 0; }
+    if ty_start < 0 { ty_start = 0; }
+    if tx_stop  > gridwide { tx_stop = gridwide; }
+    if ty_stop  > gridhigh { ty_stop = gridhigh; }
+    y_draw    :=  float32(-y + (ty_start * tilehigh))
+    for ty_index = ty_start; ty_index < ty_stop; ty_index++ { 
+        y_draw   += float32(tilehigh)
+        x_draw   := float32(-x + (tx_start * tilewide))
+        row      := pane.Cells[ty_index]
+        for tx_index = tx_start ; tx_index < tx_stop; tx_index++ {
+            x_draw += float32(tilewide)
+            cell := row[tx_index]
+            if (cell.Tile != nil) {
+                cell.Tile.Draw(x_draw, y_draw, cell.Flags)
+            }
+        }
+    }
+    // Let go of hold 
+    al.HoldBitmapDrawing(false)  
+}
+
+
+/* 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) { 
+    gridwide := pane.GridW
+    gridhigh := pane.GridH
+    tilewide := pane.Tileset.TileW
+    tilehigh := pane.Tileset.TileH
+    x        := int(camera.X)
+    y        := int(camera.Y)
+    tx_start := x / tilewide
+    ty_start := y / tilehigh
+    tx_delta := 1 + (int(camera.W) / tilewide)
+    ty_delta := 1 + (int(camera.H) / tilehigh)
+    tx_stop  := tx_start + tx_delta
+    ty_stop  := ty_start + ty_delta
+    tx_index := 0
+    ty_index := 0
+    // realwide := pane.PixelW
+    // realhigh := pane.PixelH
+    // drawflag := 0
+    // al.HoldBitmapDrawing(true)
+    if tx_start < 0 { tx_start = 0; }
+    if ty_start < 0 { ty_start = 0; }
+    if tx_stop  > gridwide { tx_stop = gridwide; }
+    if ty_stop  > gridhigh { ty_stop = gridhigh; }
+    y_draw    :=  float32(-y + (ty_start * tilehigh))
+    for ty_index = ty_start; ty_index < ty_stop; ty_index++ { 
+        y_draw   += float32(tilehigh)
+        x_draw   := float32(-x + (tx_start * tilewide))
+        row      := pane.Cells[ty_index]
+        for tx_index = tx_start ; tx_index < tx_stop; tx_index++ {
+            x_draw += float32(tilewide)
+            cell := row[tx_index]
+            if (cell.Blend != nil) {
+                cell.Blend.Draw(x_draw, y_draw, cell.Flags)
+            }
+        }
+    }
+    // Let go of hold 
+    // al.HoldBitmapDrawing(false)  
+}
+
+
+func (pane * Pane) DrawOneShadowOn(cell * Cell, pane_below * Pane, 
+                                   tx, ty int, x, y float32) {
+
+    tile      :=  cell.Tile
+    if (tile == nil) { return; }
+    if (tile.Shadow == 0) { return; }
+    shadow_color := al.MapRGBA(0, 0, 0, 128)
+
+    edge_tile :=  pane.Tile(tx + 1, ty - 1)
+    aid_tile  :=  pane.Tile(tx + 1, ty)
+    shadow_trapezium := false
+    if (edge_tile != nil) && edge_tile.IsWall() {
+        shadow_trapezium = true
+        // here the shadow is at trapezium, not a parallelogram.
+    }
+    
+    /* Tile sizes... */
+    tw := float32(pane.Tileset.TileW)
+    th := float32(pane.Tileset.TileH)
+    
+      
+    /* Only cast a shadow to the east if no solid tile next to self.
+     * Shadow is a parallelogram to simplify overlaps.
+     */
+     if (aid_tile == nil) || !aid_tile.IsWall() { 
+        low_tile  := pane_below.Tile(tx + 1, ty)
+        if (low_tile != nil) && (low_tile.ShadowMask != 1) {
+            polygon := [8]float32 {
+                x + tw          , y             , 
+                x + tw          , y + th        ,
+                x + tw * 1.5    , y + th * 0.5  ,
+                x + tw * 1.5    , y - th * 0.5  ,
+            }
+            
+            if (shadow_trapezium) {
+                polygon[7] = y
+            }
+            al.DrawFilledPolygon(polygon[0:8], 0, shadow_color)
+        }
+      }
+      
+      aid_tile = pane.Tile(tx, ty-1)
+
+      /* Only cast a shadow to the north if no solid tile above to self.
+       * Shadow is a parallelogram to simplify overlaps.
+       */
+       
+      if (aid_tile == nil) || !aid_tile.IsWall() { 
+        low_tile  := pane_below.Tile(tx, ty - 1)
+        if (low_tile != nil) && (low_tile.ShadowMask != 1) {
+            polygon := [8]float32 {
+                x + tw          , y             , 
+                x + tw          , y             ,
+                x + tw * 1.5    , y - th * 0.5  ,
+                x + tw * 0.5    , y - th * 0.5  ,
+            }
+            
+            if (shadow_trapezium) {
+                polygon[4] = x + tw
+            }
+            al.DrawFilledPolygon(polygon[0:8], 0, shadow_color)
+        }
+      }
+}
+
+/** Draws the shadows that tile pane pane casts onto the pane pane_below 
+ * with the camera delimiting the view.
+ * On a classic tile map the bottom is to the south, so this function draws
+ * "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) { 
+    gridwide := pane.GridW
+    gridhigh := pane.GridH
+    tilewide := pane.Tileset.TileW
+    tilehigh := pane.Tileset.TileH
+    x        := int(camera.X)
+    y        := int(camera.Y)
+    tx_start := x / tilewide
+    ty_start := y / tilehigh
+    tx_delta := 1 + (int(camera.W) / tilewide)
+    ty_delta := 1 + (int(camera.H) / tilehigh)
+    tx_stop  := tx_start + tx_delta
+    ty_stop  := ty_start + ty_delta
+    tx_index := 0
+    ty_index := 0
+    // realwide := pane.PixelW
+    // realhigh := pane.PixelH
+    // drawflag := 0
+    // al.HoldBitmapDrawing(true)
+    if tx_start < 0 { tx_start = 0; }
+    if ty_start < 0 { ty_start = 0; }
+    if tx_stop  > gridwide { tx_stop = gridwide; }
+    if ty_stop  > gridhigh { ty_stop = gridhigh; }
+    y_draw    :=  float32(-y + (ty_start * tilehigh))
+    for ty_index = ty_start; ty_index < ty_stop; ty_index++ { 
+        y_draw   += float32(tilehigh)
+        x_draw   := float32(-x + (tx_start * tilewide))
+        row      := pane.Cells[ty_index]
+        for tx_index = tx_start ; tx_index < tx_stop; tx_index++ {
+            x_draw += float32(tilewide)
+            cell := &row[tx_index]
+            pane.DrawOneShadowOn(cell, pane_below, tx_index, ty_index, x_draw, y_draw) 
+        }
+    }
+    // Let go of hold 
+    // al.HoldBitmapDrawing(false)  
+}
+
+
+
+/** Updates the tile pane. Curently does nothing, but this may change. */
+func (pane * Pane) Update(dt float64) {
+    if pane.Tileset != nil { 
+        pane.Tileset.Update(dt)
+    }
+}
+
+/** Gets a tile from a the tilepane's tile set by it's tile id. **/
+func (pane * Pane)  TileFromSet(index int) * Tile {
+    set := pane.Tileset 
+    if nil == set { 
+        return nil;
+    }
+    return set.Tile(index)
+}
+
+
+type BlendOffset struct {
+  tx int
+  ty int
+}
+
+var BlendOffsets [8]BlendOffset = [8]BlendOffset{
+  {-1, -1}, /* TILE_BLEND_NORTHWEST 0 */ 
+  { 0, -1}, /* TILE_BLEND_NORTH     1 */
+  { 1, -1}, /* TILE_BLEND_NORTHEAST 2 */
+  {-1,  0}, /* TILE_BLEND_WEST      3 */
+  { 1,  0}, /* TILE_BLEND_EAST      4 */
+  {-1,  1}, /* TILE_BLEND_SOUTHWEST 5 */
+  { 0,  1}, /* TILE_BLEND_SOUTH     6 */
+  { 1,  1}, /* TILE_BLEND_SOUTHEAST 7 */
+}
+
+func (pane * Pane) InitBlendTile(index, tx, ty int, tile * Tile) bool {
+    blend := pane.Blend(tx, ty)
+    if blend != nil {
+        blend.Destroy()
+        pane.SetBlend(tx, ty, nil)
+    }
+    
+    target          := al.TargetBitmap()
+    tile_prio       := tile.Blend
+    for i:= 0; i < len(BlendOffsets) ; i++ {
+        offset      := BlendOffsets[i]
+        txn         := tx + offset.tx
+        tyn         := ty + offset.ty
+        aid_tile    := pane.Tile(txn, tyn)
+        if aid_tile == nil { continue; }
+        aid_prio    := aid_tile.Blend
+        if aid_prio <= tile_prio { continue; }
+        blend_bmp   := al.CreateBitmap(pane.Tileset.TileW, pane.Tileset.TileH /* , al.CONVERT_BITMAP */)
+        if blend_bmp == nil { return false; }
+        al.SetTargetBitmap(blend_bmp)
+        al.ClearToColor(al.MapRGBA(0,0,0,0))
+        PrepareBlend(blend_bmp, tile, aid_tile, i);
+    }
+    
+    al.SetTargetBitmap(target)
+    return true
+}
+
+
+
+
+const (
+    MASK_SIDE_W     = 16
+    MASK_SIDE_H     = 16
+    MASK_CORNER_W   = 16
+    MASK_CORNER_H   = 16
+    MASK_W          = 8
+    MASK_H          = 8
+)
+
+func (pane * Pane) InitMasks() {
+    // load the masks into the pane
+    for blend_type := 0; blend_type < BLEND_TYPE_MAX ; blend_type++ {
+        fin1 := fmt.Sprintf("/image/masks/corner_mask_%d.png", blend_type)
+        fin2 := fmt.Sprintf("/image/masks/side_mask_%d.png", blend_type)
+ 
+        bmp1 := fifi.LoadBitmap(fin1)
+        bmp2 := fifi.LoadBitmap(fin2)
+        BlendMasks[blend_type][BLEND_CORNER]    = bmp1
+        BlendMasks[blend_type][BLEND_SIDE]      = bmp2
+    }
+}
+
+func (pane * Pane) InitBlend(index int) bool {
+    if pane == nil        { return false }
+    if pane.Blends == nil { return false }
+    if index > 4          { return false }
+    tx := 0
+    ty := 0
+    w  := pane.GridW
+    h  := pane.GridH
+    for ty = 0 ; ty < h; ty++ { 
+        for tx = 0; tx < w; tx++ {
+            tile := pane.Tile(tx, ty)
+            if tile == nil { continue; }
+            if tile.Blend < 1 { continue; }
+            pane.InitBlendTile(index, tx, ty, tile)
+        }
+    }
+    return true
+}
+

+ 171 - 280
engine/tile/tile.go

@@ -1,11 +1,16 @@
 package tile
 package tile
 
 
-import import "log"
+// import "log"
 // import "os"
 // import "os"
 // import "math"
 // import "math"
 
 
-import al "gitlab.com/beoran/al5go/al"
-import al "gitlab.com/beoran/ebsgo/engine/geometry/point"
+import "gitlab.com/beoran/al5go/al"
+import "gitlab.com/beoran/ebsgo/engine/geometry/point"
+
+const TILE_W = 32
+const TILE_H = 32
+
+var showSolid = false;
 
 
 
 
 /** A tile set */
 /** A tile set */
@@ -33,7 +38,7 @@ type Frame struct {
 * pointer and offset which points to the next tile to be drawn in the tileset.
 * pointer and offset which points to the next tile to be drawn in the tileset.
 */
 */
 type Tile struct {
 type Tile struct {
-  Tileset         Set   /* Tileset this tile belongs to */
+  Tileset       * Set   /* Tileset this tile belongs to */
   Index           int   /* Index in the tile set. */  
   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
   Kind            int
@@ -58,7 +63,7 @@ type Tile struct {
   /* Automatic blending activation and priority. */
   /* Automatic blending activation and priority. */
   Blend         int
   Blend         int
   /* Mask number to use for automatic blending, if any. */
   /* Mask number to use for automatic blending, if any. */
-  BlendMAsk     int
+  BlendMask     int
   /* Automatic lighting activation flag. */
   /* Automatic lighting activation flag. */
   Light         int
   Light         int
   LightMAsk     int
   LightMAsk     int
@@ -79,20 +84,20 @@ type Tile struct {
 */
 */
 
 
 
 
-func NewTileset(sheet * al.Bitmap, tile_w, tile_h, firstgid int) Set {
+func NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) * Set {
     set := &Set{}
     set := &Set{}
     set.Sheet       = sheet
     set.Sheet       = sheet
     set.TileW       = tile_w
     set.TileW       = tile_w
     set.TileH       = tile_h
     set.TileH       = tile_h
     set.FirstGID    = firstgid
     set.FirstGID    = firstgid
     set.W           = sheet.Width() / set.TileW
     set.W           = sheet.Width() / set.TileW
-    set.H           = shet.Height() / set.TileH
+    set.H           = sheet.Height()/ set.TileH
     size           := set.W * set.H
     size           := set.W * set.H
     set.Tiles       = make([]Tile, size)
     set.Tiles       = make([]Tile, size)
-    set.Last        = 0
-    for := 0 ; i < size; i ++ {
+    for i := 0 ; i < size; i ++ {
         set.Tiles[i].Init(set, i)
         set.Tiles[i].Init(set, i)
     }
     }
+    return set
 }
 }
 
 
 func (tile * Tile) Init(set * Set, index int) {
 func (tile * Tile) Init(set * Set, index int) {
@@ -102,31 +107,31 @@ func (tile * Tile) Init(set * Set, index int) {
     tile.Recalculate()
     tile.Recalculate()
 } 
 } 
 
 
-func (tile Tile) SheetY(set Tileset) int {
-    (tile->Active * set->TileW) / (set->W * set->TileH)
+func (tile Tile) SheetY(set * Set) int {
+    return (tile.Active * set.TileW) / (set.W * set.TileH)
 }
 }
 
 
-func (tile Tile) SheetX(set Tileset) int {
-    (tile->Active * set->TileW) % (set->W)
+func (tile Tile) SheetX(set * Set) int {
+    return (tile.Active * set.TileW) % (set.W)
 }
 }
 
 
 
 
 /** Recalculates the tile's position (now) in it's tile set. */
 /** Recalculates the tile's position (now) in it's tile set. */
 func (tile * Tile) Recalculate() {
 func (tile * Tile) Recalculate() {
-    if !tile.Tileset {
+    if nil == tile.Tileset {
         return
         return
     }
     }
     
     
     x := float64(tile.SheetX(tile.Tileset))
     x := float64(tile.SheetX(tile.Tileset))
     y := float64(tile.SheetY(tile.Tileset))
     y := float64(tile.SheetY(tile.Tileset))
         
         
-    tile.now = point.New(x, y)
+    tile.Position = point.New(x, y)
 }
 }
 
 
 
 
-func (set * Set) Get(index int) * Tile {
+func (set * Set) Tile(index int) * Tile {
     if nil != set && index >= 0 && index <= len(set.Tiles) {
     if nil != set && index >= 0 && index <= len(set.Tiles) {
-        return set.Tiles[index]
+        return &set.Tiles[index]
     } 
     } 
     return nil
     return nil
 }
 }
@@ -144,6 +149,8 @@ const (
     TILE_SOUTH  = iota
     TILE_SOUTH  = iota
     TILE_EAST   = iota
     TILE_EAST   = iota
     TILE_WEST   = iota
     TILE_WEST   = iota
+    TILE_UP     = iota
+    TILE_DOWN   = iota
     TILE_ICE    = iota
     TILE_ICE    = iota
  )
  )
  
  
@@ -151,17 +158,19 @@ const (
 
 
 
 
 /* Helper lookup table for the tile flag names */
 /* Helper lookup table for the tile flag names */
-var FlagNames := map[string]int {
-  "wall" , TILE_WALL  ,
-  "water", TILE_WATER ,
-  "ledge", TILE_LEDGE ,
-  "stair", TILE_STAIR ,
-  "push" , TILE_PUSH  ,
-  "north", TILE_NORTH ,
-  "south", TILE_SOUTH ,
-  "east" , TILE_EAST  ,
-  "west" , TILE_WEST  ,
-  "ice"  , TILE_ICE   ,
+var FlagNames map[string]uint = map[string]uint {
+  "wall" : TILE_WALL  ,
+  "water": TILE_WATER ,
+  "ledge": TILE_LEDGE ,
+  "stair": TILE_STAIR ,
+  "push" : TILE_PUSH  ,
+  "north": TILE_NORTH ,
+  "south": TILE_SOUTH ,
+  "east" : TILE_EAST  ,
+  "west" : TILE_WEST  ,
+  "up"   : TILE_UP    ,
+  "down" : TILE_DOWN  ,
+  "ice"  : TILE_ICE   ,
 }
 }
 
 
 
 
@@ -175,6 +184,14 @@ func (tile * Tile) SetProperty(property string) {
     }
     }
 }
 }
 
 
+func (tile * Tile) HasFlag(flag uint) bool {
+    return (tile.Flags & (1 << flag)) == (1 << flag)
+}
+
+func (tile * Tile) IsWall() bool {
+    return tile.HasFlag(TILE_WALL)
+}
+
 /** Initializes a tile's frame of animation. */
 /** Initializes a tile's frame of animation. */
 func (frame * Frame) Init(index int, duration float64) {
 func (frame * Frame) Init(index int, duration float64) {
     frame.Index     = index
     frame.Index     = index
@@ -188,162 +205,130 @@ func (tile Tile) Frame(index int)  * Frame {
     if nil == tile.Frames{ 
     if nil == tile.Frames{ 
        return nil
        return nil
     } else { 
     } else { 
-        return tiles.Frames[index]
+        return &tile.Frames[index]
     }
     }
 } 
 } 
 
 
 /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
 /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
-int tile_frame_count(Tile * tile) {
+func (tile Tile) FrameCount() int {
   if nil == tile.Frames { 
   if nil == tile.Frames { 
-       return nil
+       return 0
     } else { 
     } else { 
-        return len(tiles.Frames)
+        return len(tile.Frames)
     }
     }
 }
 }
 
 
 /** Adds a Tiled-style animation frame to the tile. */
 /** Adds a Tiled-style animation frame to the tile. */
-Tile * tile_add_animation_frame(Tile * tile, int index, double duration) {
-  if (tile->frames) {
-    int size;
-    size = dynar_size(tile->frames);
-    if (!dynar_size_(tile->frames, size + 1)) return NULL;
-    tileframe_init(dynar_getdata(tile->frames, size), index, duration);
-  } else {
-    tile->frames = dynar_new(1, sizeof(struct TileFrame_));
-    if (!tile->frames) return NULL;
-    tileframe_init(dynar_getdata(tile->frames, 0), index, duration);
-  }
-  return tile;
-} 
-
+func (tile * Tile) AddAnimationFrame(index int, duration float64) (frame * Frame) {
+    if (nil == tile.Frames) {
+        tile.Frames = make([]Frame, 1)
+        frame       = &tile.Frames[0] 
+    } else {
+        frame       = &Frame{}
+        tile.Frames = append(tile.Frames, *frame)
+    }
+    
+    frame.Init(index, duration)
+    return frame
+}
+  
 
 
 /** Rewinds a tile's animations. */
 /** Rewinds a tile's animations. */
-void tile_rewindanime(Tile * tile) {
-  if (!tile) return;
-  tile->active        = tile->index;
-  tile->active_frame  = 0;
+func (tile * Tile) RewindAnimations() {
+  tile.Active       = tile.Index
+  tile.ActiveFrame  = 0
   // Finally recalculate tile position.
   // Finally recalculate tile position.
-  tile_recalculate(tile);
+  tile.Recalculate()
 }
 }
 
 
-/** Updates a tile to animate it using classic style animation.*/
-void tile_update_classic(Tile * tile, double dt) {
-  int active = 0;
-  Tile * aidtile = NULL;
-  Tile * nowtile = tileset_get(tile->set, tile->active);
-  // nowtile is the tile that is currently active, that is shown.
-  // in stead of ourself, but it also may be ourself.
-  if(!nowtile) return;
-  tile->time    += dt; // advance animation time of tile. 
-  // Don't animate if not enough time has passed
-  if(tile->time < tile->wait) return;
-  // if we get here, reset animation time.
-  tile->time     = 0.0;
-  // take the animation parameter and add it to the active  
-  active  = tile->active + nowtile->anim;
-  aidtile = tileset_get(tile->set, active);
-  // Check if there is such a tile.
-  if(!aidtile) return;
-  // If there is no such tile, don't change the active tile of this tile.
-  tile->active = active;  
-  // Finally recalculate tile position.
-  tile_recalculate(tile);
-}
 
 
-/**  Updates a tile to anmate it using TMX style animation. */
-void tile_update_tmx(Tile * tile, double dt) {
-  int active = 0;
-  TileFrame * frame = NULL;
-  Tile * aidtile = NULL;
-  
-  frame = tile_frame(tile, tile->active_frame);
-  if (!frame) { /* Animation overshoit itself somehow??? */
-    tile->active_frame = 0;
-    frame = tile_frame(tile, tile->active_frame);
-    if (!frame) return;
+/**  Updates a tile to animate it using TMX style animation. */
+func (tile * Tile) UpdateAnimation(dt float64) {
+  active := 0;
+
+  frame := tile.Frame(tile.ActiveFrame)
+  if nil == frame { /* Animation overshot itself */
+    tile.ActiveFrame = 0
+    frame = tile.Frame(tile.ActiveFrame)
+    if nil == frame {
+         return
+    }
   }
   }
   
   
-  tile->time    += dt; // advance animation time of tile. 
+  tile.Time += dt // advance animation time of tile. 
   // Don't animate if not enough time has passed
   // Don't animate if not enough time has passed
-  if(tile->time < frame->duration) return;
+  if tile.Time < frame.Duration {
+      return
+  }
   // advance the animation frame, loop it around if needed. 
   // advance the animation frame, loop it around if needed. 
-  tile->active_frame++;
-  if (tile->active_frame >= tile_frame_count(tile)) {
-    tile->active_frame = 0;
+  tile.ActiveFrame++
+  if tile.ActiveFrame >= tile.FrameCount() {
+    tile.ActiveFrame = 0
   }
   }
   // Get new tile frame
   // Get new tile frame
-  frame = tile_frame(tile, tile->active_frame);
+  frame = tile.Frame(tile.ActiveFrame);
   // If we get here, reset animation time.
   // If we get here, reset animation time.
-  tile->time     = 0.0;
-  if (!frame) return;
-  
-  // Get the active tile t use from the animation frame
-  active  = frame->index;
-  aidtile = tileset_get(tile->set, active);
+  tile.Time     = 0.0
+  if nil == frame {
+       return
+  }
+  // Get the active tile to use from the animation frame
+  active   = frame.Index
+  aidtile := tile.Tileset.Tile(active);
   // Check if there is such a tile.
   // Check if there is such a tile.
-  if(!aidtile) return;
+  if nil == aidtile {
+      return
+  }
   // If there is no such tile, don't change the active tile of this tile.
   // If there is no such tile, don't change the active tile of this tile.
-  tile->active = active; 
+  tile.Active = active 
   // Finally recalculate tile position.
   // Finally recalculate tile position.
-  tile_recalculate(tile);
-  // tile->now = aidtile->now;
-  // LOG_NOTE("TMX Anim: %d (%d: set(%d, %d)): (x,y)=(%lf, %lf)\n", tile->index, tile->active, tile->set->w, tile->set->h, tile->now.x, tile->now.y);
+  tile.Recalculate();
 }
 }
 
 
-/* Animates the tile. If it has a TMX style animation, that takes 
- * precedence, otherwise, use the classic style animation. */
-void tile_update(Tile * tile, double dt) {
-  if (tile->frames) {
-    tile_update_tmx(tile, dt);
-  } else {
-    tile_update_classic(tile, dt);
-  }
+/* Animates the tile. Animates the tile if it has animation frames. */
+func (tile * Tile) Update(dt float64) {
+  if nil != tile.Frames {
+      tile.UpdateAnimation(dt)
+  } 
 } 
 } 
 
 
 
 
 /** Updates all tiles in a tile set so they all get animated. */
 /** Updates all tiles in a tile set so they all get animated. */
-void tileset_update(Tileset * set, double dt) {
-  int index, size;
-  if (!set) return;
-  if (!set->tiles) return;
-  size = tileset_size(set);
-  for (index = 0; index <  size; index++) {
-    Tile * tile = tileset_get(set, index); 
-    tile_update(tile, dt);
-  }  
+func (set * Set) Update(dt float64) {
+  if nil == set.Tiles {
+       return
+  }
+  
+  for i := 0 ; i < len(set.Tiles); i ++ {
+        tile := &set.Tiles[i]
+        tile.Update(dt)
+  }
 } 
 } 
 
 
 
 
 /** Draw a tile to the current active drawing target at the
 /** Draw a tile to the current active drawing target at the
 given coordinates. Does nothing if tile is NULL.  */
 given coordinates. Does nothing if tile is NULL.  */
-void tile_draw(Tile * tile, int x, int y, int drawflags) {  
-  Tileset * set;
-  Image * sheet;
-  Color dcolor = al_map_rgb(0xee, 0xee, 0x00);
-  if (!tile) return;
-  set   =  tile->set;
-  sheet = set->sheet;
-  float dx      = (float) x;
-  float dy      = (float) y; 
-  float sx      = (float) tile->now.x;
-  float sy      = (float) tile->now.y;
-  float sw      = (float) TILE_W;
-  float sh      = (float) TILE_H;
-  al_draw_bitmap_region(sheet, sx, sy, sw, sh, dx, dy, drawflags);
+func (tile Tile) Draw(x, y float32, drawflags int) {  
+  set   := tile.Tileset
+  sheet := set.Sheet
+  dx    := float32(x)
+  dy    := float32(y) 
+  sx    := float32(tile.Position.X)
+  sy    := float32(tile.Position.Y)
+  sw    := float32(set.TileW)
+  sh    := float32(set.TileH)
+  sheet.DrawRegion(sx, sy, sw, sh, dx, dy, drawflags);
   // debugging solid tiles
   // debugging solid tiles
-#ifdef TILE_SHOW_SOLID  
-  if (tile_isflag(tile, TILE_WALL)) {
-    al_draw_rectangle(dx, dy, dx+TILE_W, dy+TILE_H, dcolor, 2);
-  } 
-#endif // TILE_SHOW_SOLID
-  
-  // al_draw_bitmap(sheet, dx, dy, 0);
+  if showSolid { 
+    if (tile.Flags & (1<<TILE_WALL)) == (1<<TILE_WALL) {
+        dcolor := al.MapRGB(0xee, 0xee, 0x00)
+        al.DrawRectangle(dx, dy, dx+sw, dy+sh, dcolor, 2);
+    }
+  }
 }
 }
 
 
-
 /* Used for drawing masked tiles. */
 /* Used for drawing masked tiles. */
-static Image  * tile_mask_buffer = NULL;   
-
+var tileMaskBuffer * al.Bitmap
 
 
 /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H 
 /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H 
  * applying the given mask bitmap, where the mask will 
  * applying the given mask bitmap, where the mask will 
@@ -352,162 +337,68 @@ should be white, but with different alpha levels on the white
 which will be applied as the mask. Does nothing if tile is NULL.  
 which will be applied as the mask. Does nothing if tile is NULL.  
 This requires al_hold_bitmap_drawing to be turned off!
 This requires al_hold_bitmap_drawing to be turned off!
 */
 */
-void tile_draw_masked_to
-(Image * result, Tile * tile, Image * mask, float angle, int mask_flags) {  
+func (tile * Tile) DrawMaskedTo(result * al.Bitmap, mask * al.Bitmap, angle float32, mask_flags int) {
   /* This function need a mask buffer. */
   /* This function need a mask buffer. */
-  Tileset * set;
-  Image * sheet;
-  ALLEGRO_BITMAP * target;
-  Color dcolor = al_map_rgb(0xee, 0x00, 0xee);
-  float dx, dy, sx, sy, sw, sh;
-  int bmpflags;
-  
-  if (!tile) return;
   
   
   /* Create a 32x32 tile bitmap that will be reused thanks to 
   /* Create a 32x32 tile bitmap that will be reused thanks to 
    it being static. And leaked at program shutdown, but I don't care :p. */
    it being static. And leaked at program shutdown, but I don't care :p. */
-  if (!tile_mask_buffer) { 
-    bmpflags         = al_get_new_bitmap_flags();
-    al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);
-    tile_mask_buffer = al_create_bitmap(TILE_W, TILE_H);
-    al_set_new_bitmap_flags(bmpflags);
+  if nil == tileMaskBuffer { 
+    bmpflags      := al.NewBitmapFlags()
+    al.SetNewBitmapFlags(al.CONVERT_BITMAP)    
+    tileMaskBuffer = al.CreateBitmap(TILE_W, TILE_H)
+    al.SetNewBitmapFlags(bmpflags)
   } 
   } 
   
   
   /* Keep the target bitmap. */
   /* Keep the target bitmap. */
-  target = al_get_target_bitmap();
+  target := al.TargetBitmap()
   
   
   /* Copy the tile into the buffer.  */
   /* Copy the tile into the buffer.  */
-  al_set_target_bitmap(tile_mask_buffer);
-  set     = tile->set;
-  sheet   = set->sheet;
-  dx      = 0.0;
-  dy      = 0.0; 
-  sx      = (float) tile->now.x;
-  sy      = (float) tile->now.y;
-  sw      = (float) TILE_W;
-  sh      = (float) TILE_H;
+  al.SetTargetBitmap(tileMaskBuffer)
+  set    := tile.Tileset
+  sheet  := set.Sheet
+  dx     := float32(0.0)
+  dy     := float32(0.0)
+  sx     := float32(tile.Position.X)
+  sy     := float32(tile.Position.Y)
+  sw     := float32(set.TileW )
+  sh     := float32(set.TileH )
   /* Set blender to copy mode. */
   /* Set blender to copy mode. */
-  // al_clear_to_color(al_map_rgba_f(0,0,0,0));
-
-  al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);  
-  al_draw_bitmap_region(sheet, sx, sy, sw, sh, 0, 0, 0);
+  al.SetBlender(al.ADD, al.ONE, al.ZERO) 
+  sheet.DrawRegion(sx, sy, sw, sh, 0, 0, 0);
   
   
   /* Draw the mask over the tile, taking the alpha of the mask  */
   /* Draw the mask over the tile, taking the alpha of the mask  */
-  al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);
-  al_draw_bitmap(mask, 0, 0, mask_flags);
-
-  /* Restore normal Allegro blending. */
-  al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
+  al.SetBlender(al.ADD, al.ZERO, al.ALPHA) 
+  mask.Draw(0, 0, mask_flags)
   
   
-  sx = 0.0;
-  sy = 0.0;
+  /* Restore normal Allegro blending. */
+  al.SetBlender(al.ADD, al.ONE, al.INVERSE_ALPHA) 
+
+  sx = 0.0
+  sy = 0.0
   if (angle != 0.0) {
   if (angle != 0.0) {
-    sx = TILE_H / 2.0;
-    sy = TILE_W / 2.0;
-    dx += sx;
-    dy += sy;
-  } 
+    sx = float32(set.TileW) / 2.0
+    sy = float32(set.TileH) / 2.0
+    dx += sx
+    dy += sy
+  }
   
   
   /* Draw the tile mask buffer to the result bitmap */
   /* Draw the tile mask buffer to the result bitmap */
-  al_set_target_bitmap(result);
-  al_draw_rotated_bitmap(tile_mask_buffer, sx, sy, dx, dy, angle, 0);
+  al.SetTargetBitmap(result)
+  tileMaskBuffer.DrawRotated(sx, sy, dx, dy, angle, 0)
   /* And restore the target bitmap. */ 
   /* And restore the target bitmap. */ 
-  al_set_target_bitmap(target);
-}
-
-/** Tile's index. Returns -1 if tile is NULL; */
-int tile_index(Tile * tile) { 
-  if (!tile) return -1;
-  return tile->index;
+  al.SetTargetBitmap(target)
 }
 }
 
 
-/**  Information about the tile's properties. Return -1 if tile is NULL. */
-int tile_kind(Tile * tile) { 
-  if (!tile) return -1;
-  return tile->kind;
-}
-
-/** Information about tile's blending properties and priority.
- * Zero means no blending, positive is a blend priority.
- */
-int tile_blend(Tile * tile) {
-  if (!tile) return 0;
-  return tile->blend;
-}
-
-/** Set the tile's blending and priority */
-int tile_blend_(Tile * tile, int priority) {
-  if (!tile) return 0;
-  return tile->blend = priority;
-}
-
-/** Information about tile's blending mask
- * Returns 0 for the default mask if not set.
- */
-int tile_blend_mask(Tile * tile) {
-  if (!tile) return 0;
-  return tile->blend_mask;
-}
-
-/** Set the tile's blending mask */
-int tile_blend_mask_(Tile * tile, int mask) {
-  if (!tile) return 0;
-  if (mask < 0) mask = 0;
-  if (mask > 2) mask = 0;
-  return tile->blend_mask = mask;
-}
 
 
-/** Get the tile's light flag. Zero means no lighting. */
-int tile_light(Tile * tile) {
-  if (!tile) return 0;
-  return tile->light;
-}
-
-/** Set the tile's light flag */
-int tile_light_(Tile * tile, int value) {
-  if (!tile) return 0;
-  return tile->light = value;
-}
-
-/** Information about tile's lighting mask
- * Returns 0 for the default mask if not set.
- */
-int tile_light_mask(Tile * tile) {
-  if (!tile) return 0;
-  return tile->light_mask;
-}
-
-/** Set the tile's light mask */
-int tile_light_mask_(Tile * tile, int mask) {
-  if (!tile) return 0;
-  if (mask < 0) mask = 0;
-  return tile->light_mask = mask;
-}
-
-/** Get the tile's shadow flag. Zero means no autoshadow. */
-int tile_shadow(Tile * tile) {
-  if (!tile) return 0;
-  return tile->shadow;
-}
-
-/** Set the tile's light flag */
-int tile_shadow_(Tile * tile, int value) {
-  if (!tile) return 0;
-  return tile->shadow = value;
-}
-
-/** Information about tile's shadow mask
- * Returns 0 for the default mask if not set.
- */
-int tile_shadow_mask(Tile * tile) {
-  if (!tile) return 0;
-  return tile->shadow_mask;
-}
-
-/** Set the tile's shadow mask */
-int tile_shadow_mask_(Tile * tile, int mask) {
-  if (!tile) return 0;
-  if (mask < 0) mask = 0;
-  return tile->shadow_mask = mask;
+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]
 }
 }