tile.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. package tile
  2. import import "log"
  3. // import "os"
  4. // import "math"
  5. import al "gitlab.com/beoran/al5go/al"
  6. import al "gitlab.com/beoran/ebsgo/engine/geometry/point"
  7. /** A tile set */
  8. type Set struct {
  9. Tiles []Tile
  10. Sheet * al.Bitmap
  11. W int
  12. H int
  13. TileW int
  14. TileH int
  15. FirstGID int /* Offset of tile set in TMX map file. Used to correct tile offsets. */
  16. };
  17. /** A Tiled-style animation frame of a tile. */
  18. type Frame struct {
  19. Index int /* Tile set index for this frame of animation. */
  20. Duration float64 /* Duration of the frame in s. */
  21. }
  22. /**
  23. * A single tile from a tile map.
  24. * Tiles can be animated. This works like this: a tile has an animation
  25. * pointer and offset which points to the next tile to be drawn in the tileset.
  26. */
  27. type Tile struct {
  28. Tileset Set /* Tileset this tile belongs to */
  29. Index int /* Index in the tile set. */
  30. Flags int /* Information about the tile's properties. */
  31. Kind int
  32. /* Offset to the tile to skip to when animating. If this is
  33. 0 the tile is not animated. If nonzero, the tile will skip to
  34. the tile in the same tile set set with index index + anim.
  35. May be negative to "roll back" an animation to it's begin. */
  36. Anim int
  37. /** For unanimated tiles, active is set to the index of the tile itself.
  38. For animated tiles, it is set to the index of tile that currently should
  39. be displayed in stead of this tile due to animation.
  40. */
  41. Active int
  42. Wait float64
  43. /* Time in s to wait before jumping to the next frame of this tile. */
  44. Time float64
  45. /* Time since last animation in s. */
  46. Position point.P
  47. /* Automatic blending activation and priority. */
  48. Blend int
  49. /* Mask number to use for automatic blending, if any. */
  50. BlendMAsk int
  51. /* Automatic lighting activation flag. */
  52. Light int
  53. LightMAsk int
  54. /* Automatic shadow activation flag. */
  55. Shadow int
  56. /* Automatic shadow mask number. */
  57. ShadowMask int
  58. /* Tiled-style animation frames. */
  59. Frames []Frame
  60. /* Active frame for TMX-style animations. */
  61. ActiveFrame int
  62. };
  63. /* NOTE: Tiles could be implemented using sub bitmaps as they seem to be
  64. * slightly faster if they are preallocated. however the speed gain would
  65. * be around 2%, so it's not a priority yet. It could simplify some of
  66. * the code, though.
  67. */
  68. func NewTileset(sheet * al.Bitmap, tile_w, tile_h, firstgid int) Set {
  69. set := &Set{}
  70. set.Sheet = sheet
  71. set.TileW = tile_w
  72. set.TileH = tile_h
  73. set.FirstGID = firstgid
  74. set.W = sheet.Width() / set.TileW
  75. set.H = shet.Height() / set.TileH
  76. size := set.W * set.H
  77. set.Tiles = make([]Tile, size)
  78. set.Last = 0
  79. for := 0 ; i < size; i ++ {
  80. set.Tiles[i].Init(set, i)
  81. }
  82. }
  83. func (tile * Tile) Init(set * Set, index int) {
  84. tile.Tileset = set
  85. tile.Index = index
  86. tile.Wait = 0.25
  87. tile.Recalculate()
  88. }
  89. func (tile Tile) SheetY(set Tileset) int {
  90. (tile->Active * set->TileW) / (set->W * set->TileH)
  91. }
  92. func (tile Tile) SheetX(set Tileset) int {
  93. (tile->Active * set->TileW) % (set->W)
  94. }
  95. /** Recalculates the tile's position (now) in it's tile set. */
  96. func (tile * Tile) Recalculate() {
  97. if !tile.Tileset {
  98. return
  99. }
  100. x := float64(tile.SheetX(tile.Tileset))
  101. y := float64(tile.SheetY(tile.Tileset))
  102. tile.now = point.New(x, y)
  103. }
  104. func (set * Set) Get(index int) * Tile {
  105. if nil != set && index >= 0 && index <= len(set.Tiles) {
  106. return set.Tiles[index]
  107. }
  108. return nil
  109. }
  110. /** Tile types */
  111. const (
  112. TILE_NONE = 0
  113. TILE_WALL = iota
  114. TILE_WATER = iota
  115. TILE_LEDGE = iota
  116. TILE_STAIR = iota
  117. TILE_PUSH = iota
  118. TILE_NORTH = iota
  119. TILE_SOUTH = iota
  120. TILE_EAST = iota
  121. TILE_WEST = iota
  122. TILE_ICE = iota
  123. )
  124. /* Helper lookup table for the tile flag names */
  125. var FlagNames := map[string]int {
  126. "wall" , TILE_WALL ,
  127. "water", TILE_WATER ,
  128. "ledge", TILE_LEDGE ,
  129. "stair", TILE_STAIR ,
  130. "push" , TILE_PUSH ,
  131. "north", TILE_NORTH ,
  132. "south", TILE_SOUTH ,
  133. "east" , TILE_EAST ,
  134. "west" , TILE_WEST ,
  135. "ice" , TILE_ICE ,
  136. }
  137. /** Sets a tile's flags from a property string.
  138. * This uses an internal lookup table.
  139. */
  140. func (tile * Tile) SetProperty(property string) {
  141. val, ok := FlagNames[property];
  142. if (ok) {
  143. tile.Flags |= (1 << val)
  144. }
  145. }
  146. /** Initializes a tile's frame of animation. */
  147. func (frame * Frame) Init(index int, duration float64) {
  148. frame.Index = index
  149. frame.Duration = duration
  150. }
  151. /** Gets the nth frame of Tiled style animations for this tile
  152. * or NULL if no such animation frame. */
  153. func (tile Tile) Frame(index int) * Frame {
  154. if nil == tile.Frames{
  155. return nil
  156. } else {
  157. return tiles.Frames[index]
  158. }
  159. }
  160. /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
  161. int tile_frame_count(Tile * tile) {
  162. if nil == tile.Frames {
  163. return nil
  164. } else {
  165. return len(tiles.Frames)
  166. }
  167. }
  168. /** Adds a Tiled-style animation frame to the tile. */
  169. Tile * tile_add_animation_frame(Tile * tile, int index, double duration) {
  170. if (tile->frames) {
  171. int size;
  172. size = dynar_size(tile->frames);
  173. if (!dynar_size_(tile->frames, size + 1)) return NULL;
  174. tileframe_init(dynar_getdata(tile->frames, size), index, duration);
  175. } else {
  176. tile->frames = dynar_new(1, sizeof(struct TileFrame_));
  177. if (!tile->frames) return NULL;
  178. tileframe_init(dynar_getdata(tile->frames, 0), index, duration);
  179. }
  180. return tile;
  181. }
  182. /** Rewinds a tile's animations. */
  183. void tile_rewindanime(Tile * tile) {
  184. if (!tile) return;
  185. tile->active = tile->index;
  186. tile->active_frame = 0;
  187. // Finally recalculate tile position.
  188. tile_recalculate(tile);
  189. }
  190. /** Updates a tile to animate it using classic style animation.*/
  191. void tile_update_classic(Tile * tile, double dt) {
  192. int active = 0;
  193. Tile * aidtile = NULL;
  194. Tile * nowtile = tileset_get(tile->set, tile->active);
  195. // nowtile is the tile that is currently active, that is shown.
  196. // in stead of ourself, but it also may be ourself.
  197. if(!nowtile) return;
  198. tile->time += dt; // advance animation time of tile.
  199. // Don't animate if not enough time has passed
  200. if(tile->time < tile->wait) return;
  201. // if we get here, reset animation time.
  202. tile->time = 0.0;
  203. // take the animation parameter and add it to the active
  204. active = tile->active + nowtile->anim;
  205. aidtile = tileset_get(tile->set, active);
  206. // Check if there is such a tile.
  207. if(!aidtile) return;
  208. // If there is no such tile, don't change the active tile of this tile.
  209. tile->active = active;
  210. // Finally recalculate tile position.
  211. tile_recalculate(tile);
  212. }
  213. /** Updates a tile to anmate it using TMX style animation. */
  214. void tile_update_tmx(Tile * tile, double dt) {
  215. int active = 0;
  216. TileFrame * frame = NULL;
  217. Tile * aidtile = NULL;
  218. frame = tile_frame(tile, tile->active_frame);
  219. if (!frame) { /* Animation overshoit itself somehow??? */
  220. tile->active_frame = 0;
  221. frame = tile_frame(tile, tile->active_frame);
  222. if (!frame) return;
  223. }
  224. tile->time += dt; // advance animation time of tile.
  225. // Don't animate if not enough time has passed
  226. if(tile->time < frame->duration) return;
  227. // advance the animation frame, loop it around if needed.
  228. tile->active_frame++;
  229. if (tile->active_frame >= tile_frame_count(tile)) {
  230. tile->active_frame = 0;
  231. }
  232. // Get new tile frame
  233. frame = tile_frame(tile, tile->active_frame);
  234. // If we get here, reset animation time.
  235. tile->time = 0.0;
  236. if (!frame) return;
  237. // Get the active tile t use from the animation frame
  238. active = frame->index;
  239. aidtile = tileset_get(tile->set, active);
  240. // Check if there is such a tile.
  241. if(!aidtile) return;
  242. // If there is no such tile, don't change the active tile of this tile.
  243. tile->active = active;
  244. // Finally recalculate tile position.
  245. tile_recalculate(tile);
  246. // tile->now = aidtile->now;
  247. // 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);
  248. }
  249. /* Animates the tile. If it has a TMX style animation, that takes
  250. * precedence, otherwise, use the classic style animation. */
  251. void tile_update(Tile * tile, double dt) {
  252. if (tile->frames) {
  253. tile_update_tmx(tile, dt);
  254. } else {
  255. tile_update_classic(tile, dt);
  256. }
  257. }
  258. /** Updates all tiles in a tile set so they all get animated. */
  259. void tileset_update(Tileset * set, double dt) {
  260. int index, size;
  261. if (!set) return;
  262. if (!set->tiles) return;
  263. size = tileset_size(set);
  264. for (index = 0; index < size; index++) {
  265. Tile * tile = tileset_get(set, index);
  266. tile_update(tile, dt);
  267. }
  268. }
  269. /** Draw a tile to the current active drawing target at the
  270. given coordinates. Does nothing if tile is NULL. */
  271. void tile_draw(Tile * tile, int x, int y, int drawflags) {
  272. Tileset * set;
  273. Image * sheet;
  274. Color dcolor = al_map_rgb(0xee, 0xee, 0x00);
  275. if (!tile) return;
  276. set = tile->set;
  277. sheet = set->sheet;
  278. float dx = (float) x;
  279. float dy = (float) y;
  280. float sx = (float) tile->now.x;
  281. float sy = (float) tile->now.y;
  282. float sw = (float) TILE_W;
  283. float sh = (float) TILE_H;
  284. al_draw_bitmap_region(sheet, sx, sy, sw, sh, dx, dy, drawflags);
  285. // debugging solid tiles
  286. #ifdef TILE_SHOW_SOLID
  287. if (tile_isflag(tile, TILE_WALL)) {
  288. al_draw_rectangle(dx, dy, dx+TILE_W, dy+TILE_H, dcolor, 2);
  289. }
  290. #endif // TILE_SHOW_SOLID
  291. // al_draw_bitmap(sheet, dx, dy, 0);
  292. }
  293. /* Used for drawing masked tiles. */
  294. static Image * tile_mask_buffer = NULL;
  295. /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H
  296. * applying the given mask bitmap, where the mask will
  297. be flipped and rotated as per the given mask_flags. The mask bitmap
  298. should be white, but with different alpha levels on the white
  299. which will be applied as the mask. Does nothing if tile is NULL.
  300. This requires al_hold_bitmap_drawing to be turned off!
  301. */
  302. void tile_draw_masked_to
  303. (Image * result, Tile * tile, Image * mask, float angle, int mask_flags) {
  304. /* This function need a mask buffer. */
  305. Tileset * set;
  306. Image * sheet;
  307. ALLEGRO_BITMAP * target;
  308. Color dcolor = al_map_rgb(0xee, 0x00, 0xee);
  309. float dx, dy, sx, sy, sw, sh;
  310. int bmpflags;
  311. if (!tile) return;
  312. /* Create a 32x32 tile bitmap that will be reused thanks to
  313. it being static. And leaked at program shutdown, but I don't care :p. */
  314. if (!tile_mask_buffer) {
  315. bmpflags = al_get_new_bitmap_flags();
  316. al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);
  317. tile_mask_buffer = al_create_bitmap(TILE_W, TILE_H);
  318. al_set_new_bitmap_flags(bmpflags);
  319. }
  320. /* Keep the target bitmap. */
  321. target = al_get_target_bitmap();
  322. /* Copy the tile into the buffer. */
  323. al_set_target_bitmap(tile_mask_buffer);
  324. set = tile->set;
  325. sheet = set->sheet;
  326. dx = 0.0;
  327. dy = 0.0;
  328. sx = (float) tile->now.x;
  329. sy = (float) tile->now.y;
  330. sw = (float) TILE_W;
  331. sh = (float) TILE_H;
  332. /* Set blender to copy mode. */
  333. // al_clear_to_color(al_map_rgba_f(0,0,0,0));
  334. al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
  335. al_draw_bitmap_region(sheet, sx, sy, sw, sh, 0, 0, 0);
  336. /* Draw the mask over the tile, taking the alpha of the mask */
  337. al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);
  338. al_draw_bitmap(mask, 0, 0, mask_flags);
  339. /* Restore normal Allegro blending. */
  340. al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
  341. sx = 0.0;
  342. sy = 0.0;
  343. if (angle != 0.0) {
  344. sx = TILE_H / 2.0;
  345. sy = TILE_W / 2.0;
  346. dx += sx;
  347. dy += sy;
  348. }
  349. /* Draw the tile mask buffer to the result bitmap */
  350. al_set_target_bitmap(result);
  351. al_draw_rotated_bitmap(tile_mask_buffer, sx, sy, dx, dy, angle, 0);
  352. /* And restore the target bitmap. */
  353. al_set_target_bitmap(target);
  354. }
  355. /** Tile's index. Returns -1 if tile is NULL; */
  356. int tile_index(Tile * tile) {
  357. if (!tile) return -1;
  358. return tile->index;
  359. }
  360. /** Information about the tile's properties. Return -1 if tile is NULL. */
  361. int tile_kind(Tile * tile) {
  362. if (!tile) return -1;
  363. return tile->kind;
  364. }
  365. /** Information about tile's blending properties and priority.
  366. * Zero means no blending, positive is a blend priority.
  367. */
  368. int tile_blend(Tile * tile) {
  369. if (!tile) return 0;
  370. return tile->blend;
  371. }
  372. /** Set the tile's blending and priority */
  373. int tile_blend_(Tile * tile, int priority) {
  374. if (!tile) return 0;
  375. return tile->blend = priority;
  376. }
  377. /** Information about tile's blending mask
  378. * Returns 0 for the default mask if not set.
  379. */
  380. int tile_blend_mask(Tile * tile) {
  381. if (!tile) return 0;
  382. return tile->blend_mask;
  383. }
  384. /** Set the tile's blending mask */
  385. int tile_blend_mask_(Tile * tile, int mask) {
  386. if (!tile) return 0;
  387. if (mask < 0) mask = 0;
  388. if (mask > 2) mask = 0;
  389. return tile->blend_mask = mask;
  390. }
  391. /** Get the tile's light flag. Zero means no lighting. */
  392. int tile_light(Tile * tile) {
  393. if (!tile) return 0;
  394. return tile->light;
  395. }
  396. /** Set the tile's light flag */
  397. int tile_light_(Tile * tile, int value) {
  398. if (!tile) return 0;
  399. return tile->light = value;
  400. }
  401. /** Information about tile's lighting mask
  402. * Returns 0 for the default mask if not set.
  403. */
  404. int tile_light_mask(Tile * tile) {
  405. if (!tile) return 0;
  406. return tile->light_mask;
  407. }
  408. /** Set the tile's light mask */
  409. int tile_light_mask_(Tile * tile, int mask) {
  410. if (!tile) return 0;
  411. if (mask < 0) mask = 0;
  412. return tile->light_mask = mask;
  413. }
  414. /** Get the tile's shadow flag. Zero means no autoshadow. */
  415. int tile_shadow(Tile * tile) {
  416. if (!tile) return 0;
  417. return tile->shadow;
  418. }
  419. /** Set the tile's light flag */
  420. int tile_shadow_(Tile * tile, int value) {
  421. if (!tile) return 0;
  422. return tile->shadow = value;
  423. }
  424. /** Information about tile's shadow mask
  425. * Returns 0 for the default mask if not set.
  426. */
  427. int tile_shadow_mask(Tile * tile) {
  428. if (!tile) return 0;
  429. return tile->shadow_mask;
  430. }
  431. /** Set the tile's shadow mask */
  432. int tile_shadow_mask_(Tile * tile, int mask) {
  433. if (!tile) return 0;
  434. if (mask < 0) mask = 0;
  435. return tile->shadow_mask = mask;
  436. }