|
@@ -0,0 +1,655 @@
|
|
|
+package tile
|
|
|
+
|
|
|
+import "fmt"
|
|
|
+
|
|
|
+import "gitlab.com/beoran/al5go/al"
|
|
|
+
|
|
|
+import "gitlab.com/beoran/ebsgo/engine/physics/camera"
|
|
|
+import "gitlab.com/beoran/ebsgo/engine/fifi"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+ BLEND_CORNER = 0
|
|
|
+ BLEND_SIDE = 1
|
|
|
+ BLEND_SHAPE_MAX = 2
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+ BLEND_SHARP = 0
|
|
|
+ BLEND_GRADUAL = 1
|
|
|
+ BLEND_FUZZY = 2
|
|
|
+ BLEND_FUZZY_GRADUAL = 3
|
|
|
+ BLEND_TYPE_MAX = 4
|
|
|
+)
|
|
|
+
|
|
|
+const BLEND_BITMAPS_MAX = 255
|
|
|
+
|
|
|
+
|
|
|
+var BlendMasks [BLEND_TYPE_MAX][BLEND_SHAPE_MAX]*al.Bitmap
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+* 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
|
|
|
+
|
|
|
+ Blends * al.Bitmap
|
|
|
+ Name string
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+* 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)
|
|
|
+
|
|
|
+
|
|
|
+ * 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
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * 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
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ al.HoldBitmapDrawing(false)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * 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
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ tw := float32(pane.Tileset.TileW)
|
|
|
+ th := float32(pane.Tileset.TileH)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * 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)
|
|
|
+
|
|
|
+
|
|
|
+ * 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (pane * Pane) Update(dt float64) {
|
|
|
+ if pane.Tileset != nil {
|
|
|
+ pane.Tileset.Update(dt)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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},
|
|
|
+ { 0, -1},
|
|
|
+ { 1, -1},
|
|
|
+ {-1, 0},
|
|
|
+ { 1, 0},
|
|
|
+ {-1, 1},
|
|
|
+ { 0, 1},
|
|
|
+ { 1, 1},
|
|
|
+}
|
|
|
+
|
|
|
+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 )
|
|
|
+ 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() {
|
|
|
+
|
|
|
+ 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
|
|
|
+}
|
|
|
+
|