Browse Source

Start on GO port of Eruta Blessed Savior.

Beoran 6 years ago
commit
9eaecdfabf
4 changed files with 808 additions and 0 deletions
  1. 47 0
      ebs.go
  2. 17 0
      engine/geometry/point/point.go
  3. 231 0
      engine/global/state.go
  4. 513 0
      engine/tile/tile.go

+ 47 - 0
ebs.go

@@ -0,0 +1,47 @@
+package main
+
+import "fmt"
+import "flag"
+import "gitlab.com/beoran/ebsgo/engine/global"
+// import "path/filepath"
+// import "math/rand"
+
+// some parameters
+const SCREEN_W = 640
+const SCREEN_H = 480
+const WINDOW_TITLE = "Eruta Blessed Saviour"
+// Command line flags
+var fullscreen_flag = flag.Bool("fullscreen", false, "Run fullscreen or not")
+
+
+func main() {
+    state := global.State{}
+    if ! state.InstallAllegro() {
+        fmt.Printf("Could not initialize allegro.")
+        return
+    }
+    
+    if state.OpenDisplay(SCREEN_W, SCREEN_H, WINDOW_TITLE, *fullscreen_flag) == nil {
+        fmt.Printf("Error creating display.")
+        return
+    }
+    
+    state.Run()    
+        
+    /*    
+    blue := CreateColor(0.0, 0.0, 1.0, 1.0)
+    yellow := CreateColor(1.0, 1.0, 0.0, 1.0)
+    ClearToColor(blue)
+    DrawPixel(20.0, 10.0, yellow)
+    FlipDisplay()
+    Rest(1.0)
+    display.SetWindowPosition(50, 100)
+    ClearToColor(yellow)
+    DrawPixel(20.0, 10.0, blue)
+    FlipDisplay()
+    display.Destroy()
+    Rest(1.0)
+    **/
+
+
+}

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

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

+ 231 - 0
engine/global/state.go

@@ -0,0 +1,231 @@
+package global
+
+import "log"
+import "os"
+import "math"
+
+import al "gitlab.com/beoran/al5go/al"
+
+type FPS struct {
+    FPS     float64
+    Now     float64
+    Time    float64
+    Show    bool
+}
+
+
+
+type State struct {
+    Log         * log.Logger        
+    Display     * al.Display
+    Fullscreen    bool
+    W             int
+    H             int  
+    HaveAudio     bool
+    HaveTouch     bool
+    HaveJoystick  bool
+    Done          bool
+    DefaultFont * al.Font
+    TTF         * al.Font
+    Colors        map[string](al.Color)
+    Queue       * al.EventQueue
+    FPS           
+    Frames        int
+    Joysticks    [] * al.Joystick
+//  Map           tile.Map
+//  MapID         store.ID
+//  area.Area
+//  camera.Camera
+//  Sprites       sprite.List
+//  ScriptEngine  raku.Runtime
+//  zori.Console
+//  zori.State
+//  Actor area.Thing
+}
+
+func (ms * State) Hf() float32 {
+    return float32(ms.H)
+}
+
+func (ms * State) Wf() float32 {
+    return float32(ms.W)
+}
+
+func (ms * State) LogIfFail(ok bool, text string) bool {
+    if !ok { 
+        ms.Log.Printf("%s", text)
+    }
+    return ok
+}
+
+func (ms * State) SetupJoysticks() {
+    if (!ms.HaveJoystick) {
+        return 
+    }
+    
+    ms.Joysticks = make([]*al.Joystick, 0)
+    
+    num := al.GetNumJoysticks()
+    
+    
+    ms.Log.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())
+        snum := joy.GetNumSticks()
+        bnum := joy.GetNumButtons()
+        ms.Log.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")
+
+    if res {
+        ms.HaveTouch    = al.InstallTouchInput()
+        ms.HaveJoystick = al.InstallJoystick()
+        ms.HaveAudio    = al.InstallAudio()
+        ms.HaveAudio    = ms.HaveAudio && al.InitAcodecAddon()
+    }
+
+    ms.Queue = al.CreateEventQueue()
+    
+    ms.SetupJoysticks()
+    
+    
+    return res
+}
+
+func (ms * State) OpenDisplay(w, h int, title string, fullscreen bool) * al.Display {
+    ms.Fullscreen = fullscreen
+    ms.W          = w
+    ms.H          = h
+    flags := 0
+    // Use full screen mode if needed.
+    if fullscreen {
+        flags = al.FULLSCREEN // | GENERATE_EXPOSE_EVENTS
+    } else {
+        al.SetNewDisplayFlags(flags)
+    }
+    // Create a window to display things on: 640x480 pixels.
+    display := al.CreateDisplay(w, h)
+    display.Resize(w, h)
+    if !(fullscreen) {
+        display.SetWindowTitle(title)
+    }
+    ms.Display = display
+    
+    ms.Queue.RegisterEventSource(al.GetKeyboardEventSource())
+    ms.Queue.RegisterEventSource(al.GetMouseEventSource())
+    ms.DefaultFont = al.CreateBuiltinFont()    
+    
+    ms.FPS.Time = al.Time()
+    
+    return display
+}
+
+func (ms * State) ScaleDisplay() { 
+   real_w   := ms.Display.Width()
+   real_h   := ms.Display.Height()
+   scale_x  := real_w / ms.W
+   scale_y  := real_h / ms.H
+   scale    := scale_y
+   
+   if scale_x < scale_y {
+        scale = scale_x
+   }
+   offset_x := (real_w  - (ms.W * scale)) / 2
+   offset_y := (real_h  - (ms.H * scale)) / 2
+   
+   trans    := al.CreateIdentityTransform()   
+   /* Draw black bars to cover the usused areas. */
+   black := al.MapRGB(0,0,0)
+   
+   if (offset_y > 0) { 
+        al.DrawFilledRectangleInt(-offset_x , -offset_y, real_w, 0, black)
+        al.DrawFilledRectangleInt(-offset_x , ms.H, real_w, ms.H +offset_y, black)
+   }
+   if (offset_x > 0) { 
+        al.DrawFilledRectangleInt(-offset_x , -offset_y, 0, real_h, black)
+        al.DrawFilledRectangleInt(ms.W , -offset_y,  ms.W + offset_x, real_h, black)
+   }
+   
+   trans.ScaleInt(scale, scale).TranslateInt(offset_x, offset_y).Use()
+   /* al.SetClippingRectangle(offset_x, offset_y, ms.W, ms.H); */
+}
+
+func (ms * State) DrawFPS() {
+    white := al.MapRGB(255,255,255)
+    black := al.MapRGB(0,0,255)    
+    al.DrawFilledRectangleInt(10, 10, 100, 10 + ms.DefaultFont.LineHeight(),black)
+    ms.DefaultFont.DrawTextf(white, 10.0, 10.0, al.ALIGN_LEFT, "FPS: %d", int(ms.FPS.FPS))
+}
+
+func (ms * State) UpdateFrames() {
+    ms.Frames++
+    now := al.Time()
+    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. */
+        ms.FPS.FPS = math.Floor(realfps + 0.5)
+        /* A little trick, to prevent jerkiness, keep half the frames; and half the time  */
+        ms.Frames  = ms.Frames / 2
+        ms.FPS.Time = now - 0.5
+    }
+}
+
+
+func (ms * State) Draw() {
+    ms.DrawFPS()
+    yellow := al.MapRGB(255, 255, 0)
+    al.DrawFilledCircle(30.0, 40.0, 15.0, yellow) 
+    al.FlipDisplay()
+}
+
+func (ms * State) Load() {
+    
+}
+
+func (ms * State) Run() {
+    ms.Load()
+    
+    for !ms.Done {
+        ms.Draw()
+        ms.HandleEvents()
+        ms.UpdateFrames()
+    }
+}
+
+
+func (ms * State) HandleEvents() {
+    for event, ok := ms.Queue.GetNextEvent() ; ok ; event, ok = ms.Queue.GetNextEvent() {
+        ms.HandleEvent(event.Event())
+    }
+}
+
+
+func (ms * State) HandleEvent(event al.Event) {
+    switch uevent := event.(type) {
+        case *al.KeyboardEvent: ms.HandleKeyboardEvent(uevent)
+        default: break
+    }
+
+}
+
+func (ms * State) HandleKeyboardEvent(event * al.KeyboardEvent ) {
+    if (event.Keycode() == al.KEY_ESCAPE) {
+        ms.Done = true
+    }
+}
+
+

+ 513 - 0
engine/tile/tile.go

@@ -0,0 +1,513 @@
+package tile
+
+import import "log"
+// import "os"
+// import "math"
+
+import al "gitlab.com/beoran/al5go/al"
+import al "gitlab.com/beoran/ebsgo/engine/geometry/point"
+
+
+/** A tile set */
+type Set struct { 
+  Tiles     []Tile
+  Sheet     * al.Bitmap
+  W         int
+  H         int
+  TileW     int
+  TileH     int
+  FirstGID  int  /* Offset of tile set in TMX map file. Used to  correct tile offsets. */
+};
+
+
+
+/** A Tiled-style animation frame of a tile. */
+type Frame struct {
+    Index int  /* Tile set index for this frame of animation. */
+    Duration float64 /* Duration of the frame in s. */
+}
+
+/** 
+* A single tile from a tile map.
+* Tiles can be animated. This works like this: a tile has an animation
+* pointer and offset which points to the next tile to be drawn in the tileset.
+*/
+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. */
+  Kind            int
+
+  /* Offset to the tile to skip to when animating. If this is
+  0 the tile is not animated. If nonzero, the tile will skip to
+  the tile in the same tile set set with index index + anim.
+  May be negative to "roll back" an animation to it's begin. */  
+  Anim            int
+  
+  /** For unanimated tiles, active is set to the index of the tile itself.
+   For animated tiles, it is set to the index of tile that currently should
+   be displayed in stead of this tile due to animation.
+  */
+  Active        int 
+  
+  Wait          float64
+  /* 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
+  /* 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
+  /* Automatic shadow activation flag. */
+  Shadow        int
+  /* Automatic shadow mask number. */
+  ShadowMask    int
+  /* Tiled-style animation frames. */
+  Frames        []Frame
+  /* Active frame for TMX-style animations. */
+  ActiveFrame   int
+};
+
+/* NOTE: Tiles could be implemented using sub bitmaps as they seem to be
+* slightly faster if they are preallocated. however the speed gain would
+* be around 2%, so it's not a priority yet. It could simplify some of
+* the code, though.
+*/
+
+
+func NewTileset(sheet * al.Bitmap, tile_w, tile_h, firstgid int) Set {
+    set := &Set{}
+    set.Sheet       = sheet
+    set.TileW       = tile_w
+    set.TileH       = tile_h
+    set.FirstGID    = firstgid
+    set.W           = sheet.Width() / set.TileW
+    set.H           = shet.Height() / set.TileH
+    size           := set.W * set.H
+    set.Tiles       = make([]Tile, size)
+    set.Last        = 0
+    for := 0 ; i < size; i ++ {
+        set.Tiles[i].Init(set, i)
+    }
+}
+
+func (tile * Tile) Init(set * Set, index int) {
+    tile.Tileset    = set
+    tile.Index      = index
+    tile.Wait       = 0.25
+    tile.Recalculate()
+} 
+
+func (tile Tile) SheetY(set Tileset) int {
+    (tile->Active * set->TileW) / (set->W * set->TileH)
+}
+
+func (tile Tile) SheetX(set Tileset) int {
+    (tile->Active * set->TileW) % (set->W)
+}
+
+
+/** Recalculates the tile's position (now) in it's tile set. */
+func (tile * Tile) Recalculate() {
+    if !tile.Tileset {
+        return
+    }
+    
+    x := float64(tile.SheetX(tile.Tileset))
+    y := float64(tile.SheetY(tile.Tileset))
+        
+    tile.now = point.New(x, y)
+}
+
+
+func (set * Set) Get(index int) * Tile {
+    if nil != set && index >= 0 && index <= len(set.Tiles) {
+        return set.Tiles[index]
+    } 
+    return nil
+}
+
+
+/** Tile types */
+const (
+    TILE_NONE   = 0
+    TILE_WALL   = iota
+    TILE_WATER  = iota
+    TILE_LEDGE  = iota
+    TILE_STAIR  = iota
+    TILE_PUSH   = iota
+    TILE_NORTH  = iota
+    TILE_SOUTH  = iota
+    TILE_EAST   = iota
+    TILE_WEST   = iota
+    TILE_ICE    = iota
+ )
+ 
+ 
+
+
+/* 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   ,
+}
+
+
+/** Sets a tile's flags from a property string.
+* This uses an internal lookup table.
+*/
+func (tile * Tile) SetProperty(property string) {
+    val, ok := FlagNames[property];
+    if (ok) {   
+        tile.Flags |= (1 << val)
+    }
+}
+
+/** Initializes a tile's frame of animation. */
+func (frame * Frame) Init(index int, duration float64) {
+    frame.Index     = index
+    frame.Duration  = duration
+}
+
+
+/** Gets the nth frame of Tiled style animations for this tile 
+ * or NULL if no such animation frame. */
+func (tile Tile) Frame(index int)  * Frame {
+    if nil == tile.Frames{ 
+       return nil
+    } else { 
+        return tiles.Frames[index]
+    }
+} 
+
+/** Gets the amount of Tiled style animations for this tile, or 0 if none. */
+int tile_frame_count(Tile * tile) {
+  if nil == tile.Frames { 
+       return nil
+    } else { 
+        return len(tiles.Frames)
+    }
+}
+
+/** 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;
+} 
+
+
+/** Rewinds a tile's animations. */
+void tile_rewindanime(Tile * tile) {
+  if (!tile) return;
+  tile->active        = tile->index;
+  tile->active_frame  = 0;
+  // Finally recalculate tile position.
+  tile_recalculate(tile);
+}
+
+/** 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;
+  }
+  
+  tile->time    += dt; // advance animation time of tile. 
+  // Don't animate if not enough time has passed
+  if(tile->time < frame->duration) return;
+  // advance the animation frame, loop it around if needed. 
+  tile->active_frame++;
+  if (tile->active_frame >= tile_frame_count(tile)) {
+    tile->active_frame = 0;
+  }
+  // Get new tile frame
+  frame = tile_frame(tile, tile->active_frame);
+  // 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);
+  // 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);
+  // 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);
+}
+
+/* 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);
+  }
+} 
+
+
+/** 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);
+  }  
+} 
+
+
+/** Draw a tile to the current active drawing target at the
+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);
+  // 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);
+}
+
+
+/* Used for drawing masked tiles. */
+static Image  * tile_mask_buffer = NULL;   
+
+
+/** 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 
+be flipped and rotated as per the given mask_flags. The mask bitmap
+should be white, but with different alpha levels on the white 
+which will be applied as the mask. Does nothing if tile is NULL.  
+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) {  
+  /* 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 
+   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);
+  } 
+  
+  /* Keep the target bitmap. */
+  target = al_get_target_bitmap();
+  
+  /* 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;
+  /* 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);
+  
+  /* 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);
+  
+  sx = 0.0;
+  sy = 0.0;
+  if (angle != 0.0) {
+    sx = TILE_H / 2.0;
+    sy = TILE_W / 2.0;
+    dx += sx;
+    dy += sy;
+  } 
+  
+  /* 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);
+  /* 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;
+}
+
+/**  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;
+}
+