tile.go 11 KB

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