tile.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. package tile
  2. import "gitlab.com/beoran/ebsgo/monolog"
  3. // import "os"
  4. // import "math"
  5. import "gitlab.com/beoran/al5go/al"
  6. import "gitlab.com/beoran/ebsgo/engine/geometry"
  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 // Width, in tiles
  15. H int // Height, in tiles
  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 geometry.Vector
  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. if sheet == nil {
  73. return nil
  74. }
  75. set := &Set{}
  76. set.Sheet = sheet
  77. set.TileW = tile_w
  78. set.TileH = tile_h
  79. set.FirstGID = firstgid
  80. set.W = sheet.Width() / set.TileW
  81. set.H = sheet.Height() / set.TileH
  82. size := set.W * set.H
  83. set.Tiles = make([]Tile, size)
  84. for i := 0 ; i < size; i ++ {
  85. set.Tiles[i].Init(set, i)
  86. }
  87. return set
  88. }
  89. func (tile * Tile) Init(set * Set, index int) {
  90. tile.Tileset = set
  91. tile.Index = index
  92. tile.Active = index
  93. tile.Wait = 0.25
  94. tile.Recalculate()
  95. }
  96. func (tile Tile) SheetY(set * Set) int {
  97. return (tile.Active / set.W) * set.TileH
  98. }
  99. func (tile Tile) SheetX(set * Set) int {
  100. return (tile.Active % set.W) * set.TileW
  101. }
  102. /** Recalculates the tile's position (now) in it's tile set. */
  103. func (tile * Tile) Recalculate() {
  104. if nil == tile.Tileset {
  105. return
  106. }
  107. x := float32(tile.SheetX(tile.Tileset))
  108. y := float32(tile.SheetY(tile.Tileset))
  109. tile.Position = geometry.NewVector(x, y)
  110. }
  111. func (set Set) Tile(index int) * Tile {
  112. if index >= 0 && index <= len(set.Tiles) {
  113. return &set.Tiles[index]
  114. }
  115. return nil
  116. }
  117. /** Tile types */
  118. const (
  119. TILE_NONE = 0
  120. TILE_WALL = iota
  121. TILE_WATER = iota
  122. TILE_LEDGE = iota
  123. TILE_STAIR = iota
  124. TILE_PUSH = iota
  125. TILE_NORTH = iota
  126. TILE_SOUTH = iota
  127. TILE_EAST = iota
  128. TILE_WEST = iota
  129. TILE_UP = iota
  130. TILE_DOWN = iota
  131. TILE_ICE = iota
  132. )
  133. /* Helper lookup table for the tile flag names */
  134. var FlagNames map[string]uint = map[string]uint {
  135. "wall" : TILE_WALL ,
  136. "water": TILE_WATER ,
  137. "ledge": TILE_LEDGE ,
  138. "stair": TILE_STAIR ,
  139. "push" : TILE_PUSH ,
  140. "north": TILE_NORTH ,
  141. "south": TILE_SOUTH ,
  142. "east" : TILE_EAST ,
  143. "west" : TILE_WEST ,
  144. "up" : TILE_UP ,
  145. "down" : TILE_DOWN ,
  146. "ice" : TILE_ICE ,
  147. }
  148. /** Sets a tile's flags from a property string.
  149. * This uses an internal lookup table.
  150. */
  151. func (tile * Tile) SetProperty(property string) {
  152. val, ok := FlagNames[property];
  153. if (ok) {
  154. tile.Flags |= (1 << val)
  155. }
  156. }
  157. func (tile * Tile) HasFlag(flag uint) bool {
  158. return (tile.Flags & (1 << flag)) == (1 << flag)
  159. }
  160. func (tile * Tile) IsWall() bool {
  161. return tile.HasFlag(TILE_WALL)
  162. }
  163. /** Initializes a tile's frame of animation. */
  164. func (frame * Frame) Init(index int, duration float64) {
  165. frame.Index = index
  166. frame.Duration = duration
  167. }
  168. /** Gets the nth frame of Tiled style animations for this tile
  169. * or NULL if no such animation frame. */
  170. func (tile Tile) Frame(index int) * Frame {
  171. if nil == tile.Frames{
  172. return nil
  173. } else {
  174. return &tile.Frames[index]
  175. }
  176. }
  177. /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
  178. func (tile Tile) FrameCount() int {
  179. if nil == tile.Frames {
  180. return 0
  181. } else {
  182. return len(tile.Frames)
  183. }
  184. }
  185. /** Adds a Tiled-style animation frame to the tile. */
  186. func (tile * Tile) AddAnimationFrame(index int, duration float64) (frame * Frame) {
  187. if (nil == tile.Frames) {
  188. tile.Frames = make([]Frame, 1)
  189. frame = &tile.Frames[0]
  190. } else {
  191. frame = &Frame{}
  192. tile.Frames = append(tile.Frames, *frame)
  193. }
  194. frame.Init(index, duration)
  195. return frame
  196. }
  197. /** Rewinds a tile's animations. */
  198. func (tile * Tile) RewindAnimations() {
  199. tile.Active = tile.Index
  200. tile.ActiveFrame = 0
  201. // Finally recalculate tile position.
  202. tile.Recalculate()
  203. }
  204. /** Updates a tile to animate it using TMX style animation. */
  205. func (tile * Tile) UpdateAnimation(dt float64) {
  206. active := 0;
  207. frame := tile.Frame(tile.ActiveFrame)
  208. if nil == frame { /* Animation overshot itself */
  209. tile.ActiveFrame = 0
  210. frame = tile.Frame(tile.ActiveFrame)
  211. if nil == frame {
  212. return
  213. }
  214. }
  215. monolog.Log("TILE", "Animation for tile: %d %d %f %f\n",
  216. tile.ActiveFrame, frame.Index, frame.Duration, tile.Time)
  217. tile.Time += dt // advance animation time of tile.
  218. // Don't animate if not enough time has passed
  219. if tile.Time < frame.Duration {
  220. return
  221. }
  222. // advance the animation frame, loop it around if needed.
  223. tile.ActiveFrame++
  224. if tile.ActiveFrame >= tile.FrameCount() {
  225. tile.ActiveFrame = 0
  226. }
  227. // Get new tile frame
  228. frame = tile.Frame(tile.ActiveFrame);
  229. // If we get here, reset animation time.
  230. tile.Time = 0.0
  231. if nil == frame {
  232. return
  233. }
  234. monolog.Log("TILE", "Animation for tile: %d %d %f %f %b\n",
  235. tile.ActiveFrame, frame.Index, frame.Duration, tile.Time)
  236. // Get the active tile to use from the animation frame
  237. active = frame.Index
  238. aidtile := tile.Tileset.Tile(active);
  239. // Check if there is such a tile.
  240. if nil == aidtile {
  241. return
  242. }
  243. // If there is such a tile, change the active tile index of this tile.
  244. tile.Active = active
  245. // Finally recalculate tile position.
  246. tile.Recalculate()
  247. monolog.Log("TILE", "Updated animation for tile: %d\n", tile.Active)
  248. }
  249. /* Animates the tile. Animates the tile if it has animation frames. */
  250. func (tile * Tile) Update(dt float64) {
  251. if nil != tile.Frames {
  252. tile.UpdateAnimation(dt)
  253. }
  254. }
  255. /** Updates all tiles in a tile set so they all get animated. */
  256. func (set * Set) Update(dt float64) {
  257. if nil == set.Tiles {
  258. return
  259. }
  260. for i := 0 ; i < len(set.Tiles); i ++ {
  261. tile := &set.Tiles[i]
  262. tile.Update(dt)
  263. }
  264. }
  265. /** Recalculates all tiles in a tile set so they all get the rght position. */
  266. func (set * Set) Recalculate() {
  267. if nil == set.Tiles {
  268. return
  269. }
  270. for i := 0 ; i < len(set.Tiles); i ++ {
  271. tile := &set.Tiles[i]
  272. tile.Recalculate()
  273. }
  274. }
  275. /** Draw a tile to the current active drawing target at the
  276. given coordinates. Does nothing if tile is NULL. */
  277. func (tile Tile) Draw(x, y float32, drawflags int) {
  278. set := tile.Tileset
  279. sheet := set.Sheet
  280. dx := float32(x)
  281. dy := float32(y)
  282. sx := float32(tile.Position.X)
  283. sy := float32(tile.Position.Y)
  284. sw := float32(set.TileW)
  285. sh := float32(set.TileH)
  286. sheet.DrawRegion(sx, sy, sw, sh, dx, dy, drawflags);
  287. // debugging solid tiles
  288. if showSolid {
  289. if (tile.Flags & (1<<TILE_WALL)) == (1<<TILE_WALL) {
  290. dcolor := al.MapRGB(0xee, 0xee, 0x00)
  291. al.DrawRectangle(dx, dy, dx+sw, dy+sh, dcolor, 2);
  292. }
  293. }
  294. }
  295. /* Used for drawing masked tiles. */
  296. var tileMaskBuffer * al.Bitmap
  297. /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H
  298. * applying the given mask bitmap, where the mask will
  299. be flipped and rotated as per the given mask_flags. The mask bitmap
  300. should be white, but with different alpha levels on the white
  301. which will be applied as the mask. Does nothing if tile is NULL.
  302. This requires al_hold_bitmap_drawing to be turned off!
  303. */
  304. func (tile * Tile) DrawMaskedTo(result * al.Bitmap, mask * al.Bitmap, angle float32, mask_flags int) {
  305. /* This function need a mask buffer. */
  306. /* Create a 32x32 tile bitmap that will be reused thanks to
  307. it being static. And leaked at program shutdown, but I don't care :p. */
  308. if nil == tileMaskBuffer {
  309. bmpflags := al.NewBitmapFlags()
  310. al.SetNewBitmapFlags(al.CONVERT_BITMAP)
  311. tileMaskBuffer = al.CreateBitmap(TILE_W, TILE_H)
  312. al.SetNewBitmapFlags(bmpflags)
  313. }
  314. /* Keep the target bitmap. */
  315. target := al.TargetBitmap()
  316. /* Copy the tile into the buffer. */
  317. al.SetTargetBitmap(tileMaskBuffer)
  318. set := tile.Tileset
  319. sheet := set.Sheet
  320. dx := float32(0.0)
  321. dy := float32(0.0)
  322. sx := float32(tile.Position.X)
  323. sy := float32(tile.Position.Y)
  324. sw := float32(set.TileW )
  325. sh := float32(set.TileH )
  326. /* Set blender to copy mode. */
  327. al.SetBlender(al.ADD, al.ONE, al.ZERO)
  328. sheet.DrawRegion(sx, sy, sw, sh, 0, 0, 0);
  329. /* Draw the mask over the tile, taking the alpha of the mask */
  330. al.SetBlender(al.ADD, al.ZERO, al.ALPHA)
  331. mask.Draw(0, 0, mask_flags)
  332. /* Restore normal Allegro blending. */
  333. al.SetBlender(al.ADD, al.ONE, al.INVERSE_ALPHA)
  334. sx = 0.0
  335. sy = 0.0
  336. if (angle != 0.0) {
  337. sx = float32(set.TileW) / 2.0
  338. sy = float32(set.TileH) / 2.0
  339. dx += sx
  340. dy += sy
  341. }
  342. /* Draw the tile mask buffer to the result bitmap */
  343. al.SetTargetBitmap(result)
  344. tileMaskBuffer.DrawRotated(sx, sy, dx, dy, angle, 0)
  345. /* And restore the target bitmap. */
  346. al.SetTargetBitmap(target)
  347. }