tile.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package tile
  2. // import "log"
  3. // import "os"
  4. // import "math"
  5. import "gitlab.com/beoran/al5go/al"
  6. import "gitlab.com/beoran/ebsgo/engine/geometry/point"
  7. const TILE_W = 32
  8. const TILE_H = 32
  9. var showSolid = false;
  10. /** A tile set */
  11. type Set struct {
  12. Tiles []Tile
  13. Sheet * al.Bitmap
  14. W int
  15. H int
  16. TileW int
  17. TileH int
  18. FirstGID int /* Offset of tile set in TMX map file. Used to correct tile offsets. */
  19. };
  20. /** A Tiled-style animation frame of a tile. */
  21. type Frame struct {
  22. Index int /* Tile set index for this frame of animation. */
  23. Duration float64 /* Duration of the frame in s. */
  24. }
  25. /**
  26. * A single tile from a tile map.
  27. * Tiles can be animated. This works like this: a tile has an animation
  28. * pointer and offset which points to the next tile to be drawn in the tileset.
  29. */
  30. type Tile struct {
  31. Tileset * Set /* Tileset this tile belongs to */
  32. Index int /* Index in the tile set. */
  33. Flags int /* Information about the tile's properties. */
  34. Kind int
  35. /* Offset to the tile to skip to when animating. If this is
  36. 0 the tile is not animated. If nonzero, the tile will skip to
  37. the tile in the same tile set set with index index + anim.
  38. May be negative to "roll back" an animation to it's begin. */
  39. Anim int
  40. /** For unanimated tiles, active is set to the index of the tile itself.
  41. For animated tiles, it is set to the index of tile that currently should
  42. be displayed in stead of this tile due to animation.
  43. */
  44. Active int
  45. Wait float64
  46. /* Time in s to wait before jumping to the next frame of this tile. */
  47. Time float64
  48. /* Time since last animation in s. */
  49. Position point.P
  50. /* Automatic blending activation and priority. */
  51. Blend int
  52. /* Mask number to use for automatic blending, if any. */
  53. BlendMask int
  54. /* Automatic lighting activation flag. */
  55. Light int
  56. LightMAsk int
  57. /* Automatic shadow activation flag. */
  58. Shadow int
  59. /* Automatic shadow mask number. */
  60. ShadowMask int
  61. /* Tiled-style animation frames. */
  62. Frames []Frame
  63. /* Active frame for TMX-style animations. */
  64. ActiveFrame int
  65. };
  66. /* NOTE: Tiles could be implemented using sub bitmaps as they seem to be
  67. * slightly faster if they are preallocated. however the speed gain would
  68. * be around 2%, so it's not a priority yet. It could simplify some of
  69. * the code, though.
  70. */
  71. func NewSet(sheet * al.Bitmap, tile_w, tile_h, firstgid int) * Set {
  72. set := &Set{}
  73. set.Sheet = sheet
  74. set.TileW = tile_w
  75. set.TileH = tile_h
  76. set.FirstGID = firstgid
  77. set.W = sheet.Width() / set.TileW
  78. set.H = sheet.Height()/ set.TileH
  79. size := set.W * set.H
  80. set.Tiles = make([]Tile, size)
  81. for i := 0 ; i < size; i ++ {
  82. set.Tiles[i].Init(set, i)
  83. }
  84. return set
  85. }
  86. func (tile * Tile) Init(set * Set, index int) {
  87. tile.Tileset = set
  88. tile.Index = index
  89. tile.Wait = 0.25
  90. tile.Recalculate()
  91. }
  92. func (tile Tile) SheetY(set * Set) int {
  93. return (tile.Active * set.TileW) / (set.W * set.TileH)
  94. }
  95. func (tile Tile) SheetX(set * Set) int {
  96. return (tile.Active * set.TileW) % (set.W)
  97. }
  98. /** Recalculates the tile's position (now) in it's tile set. */
  99. func (tile * Tile) Recalculate() {
  100. if nil == tile.Tileset {
  101. return
  102. }
  103. x := float64(tile.SheetX(tile.Tileset))
  104. y := float64(tile.SheetY(tile.Tileset))
  105. tile.Position = point.New(x, y)
  106. }
  107. func (set * Set) Tile(index int) * Tile {
  108. if nil != set && index >= 0 && index <= len(set.Tiles) {
  109. return &set.Tiles[index]
  110. }
  111. return nil
  112. }
  113. /** Tile types */
  114. const (
  115. TILE_NONE = 0
  116. TILE_WALL = iota
  117. TILE_WATER = iota
  118. TILE_LEDGE = iota
  119. TILE_STAIR = iota
  120. TILE_PUSH = iota
  121. TILE_NORTH = iota
  122. TILE_SOUTH = iota
  123. TILE_EAST = iota
  124. TILE_WEST = iota
  125. TILE_UP = iota
  126. TILE_DOWN = iota
  127. TILE_ICE = iota
  128. )
  129. /* Helper lookup table for the tile flag names */
  130. var FlagNames map[string]uint = map[string]uint {
  131. "wall" : TILE_WALL ,
  132. "water": TILE_WATER ,
  133. "ledge": TILE_LEDGE ,
  134. "stair": TILE_STAIR ,
  135. "push" : TILE_PUSH ,
  136. "north": TILE_NORTH ,
  137. "south": TILE_SOUTH ,
  138. "east" : TILE_EAST ,
  139. "west" : TILE_WEST ,
  140. "up" : TILE_UP ,
  141. "down" : TILE_DOWN ,
  142. "ice" : TILE_ICE ,
  143. }
  144. /** Sets a tile's flags from a property string.
  145. * This uses an internal lookup table.
  146. */
  147. func (tile * Tile) SetProperty(property string) {
  148. val, ok := FlagNames[property];
  149. if (ok) {
  150. tile.Flags |= (1 << val)
  151. }
  152. }
  153. func (tile * Tile) HasFlag(flag uint) bool {
  154. return (tile.Flags & (1 << flag)) == (1 << flag)
  155. }
  156. func (tile * Tile) IsWall() bool {
  157. return tile.HasFlag(TILE_WALL)
  158. }
  159. /** Initializes a tile's frame of animation. */
  160. func (frame * Frame) Init(index int, duration float64) {
  161. frame.Index = index
  162. frame.Duration = duration
  163. }
  164. /** Gets the nth frame of Tiled style animations for this tile
  165. * or NULL if no such animation frame. */
  166. func (tile Tile) Frame(index int) * Frame {
  167. if nil == tile.Frames{
  168. return nil
  169. } else {
  170. return &tile.Frames[index]
  171. }
  172. }
  173. /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
  174. func (tile Tile) FrameCount() int {
  175. if nil == tile.Frames {
  176. return 0
  177. } else {
  178. return len(tile.Frames)
  179. }
  180. }
  181. /** Adds a Tiled-style animation frame to the tile. */
  182. func (tile * Tile) AddAnimationFrame(index int, duration float64) (frame * Frame) {
  183. if (nil == tile.Frames) {
  184. tile.Frames = make([]Frame, 1)
  185. frame = &tile.Frames[0]
  186. } else {
  187. frame = &Frame{}
  188. tile.Frames = append(tile.Frames, *frame)
  189. }
  190. frame.Init(index, duration)
  191. return frame
  192. }
  193. /** Rewinds a tile's animations. */
  194. func (tile * Tile) RewindAnimations() {
  195. tile.Active = tile.Index
  196. tile.ActiveFrame = 0
  197. // Finally recalculate tile position.
  198. tile.Recalculate()
  199. }
  200. /** Updates a tile to animate it using TMX style animation. */
  201. func (tile * Tile) UpdateAnimation(dt float64) {
  202. active := 0;
  203. frame := tile.Frame(tile.ActiveFrame)
  204. if nil == frame { /* Animation overshot itself */
  205. tile.ActiveFrame = 0
  206. frame = tile.Frame(tile.ActiveFrame)
  207. if nil == frame {
  208. return
  209. }
  210. }
  211. tile.Time += dt // advance animation time of tile.
  212. // Don't animate if not enough time has passed
  213. if tile.Time < frame.Duration {
  214. return
  215. }
  216. // advance the animation frame, loop it around if needed.
  217. tile.ActiveFrame++
  218. if tile.ActiveFrame >= tile.FrameCount() {
  219. tile.ActiveFrame = 0
  220. }
  221. // Get new tile frame
  222. frame = tile.Frame(tile.ActiveFrame);
  223. // If we get here, reset animation time.
  224. tile.Time = 0.0
  225. if nil == frame {
  226. return
  227. }
  228. // Get the active tile to use from the animation frame
  229. active = frame.Index
  230. aidtile := tile.Tileset.Tile(active);
  231. // Check if there is such a tile.
  232. if nil == aidtile {
  233. return
  234. }
  235. // If there is no such tile, don't change the active tile of this tile.
  236. tile.Active = active
  237. // Finally recalculate tile position.
  238. tile.Recalculate();
  239. }
  240. /* Animates the tile. Animates the tile if it has animation frames. */
  241. func (tile * Tile) Update(dt float64) {
  242. if nil != tile.Frames {
  243. tile.UpdateAnimation(dt)
  244. }
  245. }
  246. /** Updates all tiles in a tile set so they all get animated. */
  247. func (set * Set) Update(dt float64) {
  248. if nil == set.Tiles {
  249. return
  250. }
  251. for i := 0 ; i < len(set.Tiles); i ++ {
  252. tile := &set.Tiles[i]
  253. tile.Update(dt)
  254. }
  255. }
  256. /** Draw a tile to the current active drawing target at the
  257. given coordinates. Does nothing if tile is NULL. */
  258. func (tile Tile) Draw(x, y float32, drawflags int) {
  259. set := tile.Tileset
  260. sheet := set.Sheet
  261. dx := float32(x)
  262. dy := float32(y)
  263. sx := float32(tile.Position.X)
  264. sy := float32(tile.Position.Y)
  265. sw := float32(set.TileW)
  266. sh := float32(set.TileH)
  267. sheet.DrawRegion(sx, sy, sw, sh, dx, dy, drawflags);
  268. // debugging solid tiles
  269. if showSolid {
  270. if (tile.Flags & (1<<TILE_WALL)) == (1<<TILE_WALL) {
  271. dcolor := al.MapRGB(0xee, 0xee, 0x00)
  272. al.DrawRectangle(dx, dy, dx+sw, dy+sh, dcolor, 2);
  273. }
  274. }
  275. }
  276. /* Used for drawing masked tiles. */
  277. var tileMaskBuffer * al.Bitmap
  278. /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H
  279. * applying the given mask bitmap, where the mask will
  280. be flipped and rotated as per the given mask_flags. The mask bitmap
  281. should be white, but with different alpha levels on the white
  282. which will be applied as the mask. Does nothing if tile is NULL.
  283. This requires al_hold_bitmap_drawing to be turned off!
  284. */
  285. func (tile * Tile) DrawMaskedTo(result * al.Bitmap, mask * al.Bitmap, angle float32, mask_flags int) {
  286. /* This function need a mask buffer. */
  287. /* Create a 32x32 tile bitmap that will be reused thanks to
  288. it being static. And leaked at program shutdown, but I don't care :p. */
  289. if nil == tileMaskBuffer {
  290. bmpflags := al.NewBitmapFlags()
  291. al.SetNewBitmapFlags(al.CONVERT_BITMAP)
  292. tileMaskBuffer = al.CreateBitmap(TILE_W, TILE_H)
  293. al.SetNewBitmapFlags(bmpflags)
  294. }
  295. /* Keep the target bitmap. */
  296. target := al.TargetBitmap()
  297. /* Copy the tile into the buffer. */
  298. al.SetTargetBitmap(tileMaskBuffer)
  299. set := tile.Tileset
  300. sheet := set.Sheet
  301. dx := float32(0.0)
  302. dy := float32(0.0)
  303. sx := float32(tile.Position.X)
  304. sy := float32(tile.Position.Y)
  305. sw := float32(set.TileW )
  306. sh := float32(set.TileH )
  307. /* Set blender to copy mode. */
  308. al.SetBlender(al.ADD, al.ONE, al.ZERO)
  309. sheet.DrawRegion(sx, sy, sw, sh, 0, 0, 0);
  310. /* Draw the mask over the tile, taking the alpha of the mask */
  311. al.SetBlender(al.ADD, al.ZERO, al.ALPHA)
  312. mask.Draw(0, 0, mask_flags)
  313. /* Restore normal Allegro blending. */
  314. al.SetBlender(al.ADD, al.ONE, al.INVERSE_ALPHA)
  315. sx = 0.0
  316. sy = 0.0
  317. if (angle != 0.0) {
  318. sx = float32(set.TileW) / 2.0
  319. sy = float32(set.TileH) / 2.0
  320. dx += sx
  321. dy += sy
  322. }
  323. /* Draw the tile mask buffer to the result bitmap */
  324. al.SetTargetBitmap(result)
  325. tileMaskBuffer.DrawRotated(sx, sy, dx, dy, angle, 0)
  326. /* And restore the target bitmap. */
  327. al.SetTargetBitmap(target)
  328. }
  329. func (set Set) Tile(index int) * Tile {
  330. if set.Tiles == nil {
  331. return nil
  332. }
  333. if (index < 0) || (index > len(set.Tiles)) {
  334. return nil
  335. }
  336. return &set.Tiles[index]
  337. }