|
@@ -0,0 +1,513 @@
|
|
|
+package tile
|
|
|
+
|
|
|
+import import "log"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import al "gitlab.com/beoran/al5go/al"
|
|
|
+import al "gitlab.com/beoran/ebsgo/engine/geometry/point"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type Set struct {
|
|
|
+ Tiles []Tile
|
|
|
+ Sheet * al.Bitmap
|
|
|
+ W int
|
|
|
+ H int
|
|
|
+ TileW int
|
|
|
+ TileH int
|
|
|
+ FirstGID int
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type Frame struct {
|
|
|
+ Index int
|
|
|
+ Duration float64
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+* A single tile from a tile map.
|
|
|
+* Tiles can be animated. This works like this: a tile has an animation
|
|
|
+* pointer and offset which points to the next tile to be drawn in the tileset.
|
|
|
+*/
|
|
|
+type Tile struct {
|
|
|
+ Tileset Set
|
|
|
+ Index int
|
|
|
+ Flags int
|
|
|
+ Kind int
|
|
|
+
|
|
|
+
|
|
|
+ 0 the tile is not animated. If nonzero, the tile will skip to
|
|
|
+ the tile in the same tile set set with index index + anim.
|
|
|
+ May be negative to "roll back" an animation to it's begin. */
|
|
|
+ Anim int
|
|
|
+
|
|
|
+
|
|
|
+ For animated tiles, it is set to the index of tile that currently should
|
|
|
+ be displayed in stead of this tile due to animation.
|
|
|
+ */
|
|
|
+ Active int
|
|
|
+
|
|
|
+ Wait float64
|
|
|
+
|
|
|
+ Time float64
|
|
|
+
|
|
|
+ Position point.P
|
|
|
+
|
|
|
+ Blend int
|
|
|
+
|
|
|
+ BlendMAsk int
|
|
|
+
|
|
|
+ Light int
|
|
|
+ LightMAsk int
|
|
|
+
|
|
|
+ Shadow int
|
|
|
+
|
|
|
+ ShadowMask int
|
|
|
+
|
|
|
+ Frames []Frame
|
|
|
+
|
|
|
+ ActiveFrame int
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+* slightly faster if they are preallocated. however the speed gain would
|
|
|
+* be around 2%, so it's not a priority yet. It could simplify some of
|
|
|
+* the code, though.
|
|
|
+*/
|
|
|
+
|
|
|
+
|
|
|
+func NewTileset(sheet * al.Bitmap, tile_w, tile_h, firstgid int) Set {
|
|
|
+ set := &Set{}
|
|
|
+ set.Sheet = sheet
|
|
|
+ set.TileW = tile_w
|
|
|
+ set.TileH = tile_h
|
|
|
+ set.FirstGID = firstgid
|
|
|
+ set.W = sheet.Width() / set.TileW
|
|
|
+ set.H = shet.Height() / set.TileH
|
|
|
+ size := set.W * set.H
|
|
|
+ set.Tiles = make([]Tile, size)
|
|
|
+ set.Last = 0
|
|
|
+ for := 0 ; i < size; i ++ {
|
|
|
+ set.Tiles[i].Init(set, i)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (tile * Tile) Init(set * Set, index int) {
|
|
|
+ tile.Tileset = set
|
|
|
+ tile.Index = index
|
|
|
+ tile.Wait = 0.25
|
|
|
+ tile.Recalculate()
|
|
|
+}
|
|
|
+
|
|
|
+func (tile Tile) SheetY(set Tileset) int {
|
|
|
+ (tile->Active * set->TileW) / (set->W * set->TileH)
|
|
|
+}
|
|
|
+
|
|
|
+func (tile Tile) SheetX(set Tileset) int {
|
|
|
+ (tile->Active * set->TileW) % (set->W)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (tile * Tile) Recalculate() {
|
|
|
+ if !tile.Tileset {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ x := float64(tile.SheetX(tile.Tileset))
|
|
|
+ y := float64(tile.SheetY(tile.Tileset))
|
|
|
+
|
|
|
+ tile.now = point.New(x, y)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (set * Set) Get(index int) * Tile {
|
|
|
+ if nil != set && index >= 0 && index <= len(set.Tiles) {
|
|
|
+ return set.Tiles[index]
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+ TILE_NONE = 0
|
|
|
+ TILE_WALL = iota
|
|
|
+ TILE_WATER = iota
|
|
|
+ TILE_LEDGE = iota
|
|
|
+ TILE_STAIR = iota
|
|
|
+ TILE_PUSH = iota
|
|
|
+ TILE_NORTH = iota
|
|
|
+ TILE_SOUTH = iota
|
|
|
+ TILE_EAST = iota
|
|
|
+ TILE_WEST = iota
|
|
|
+ TILE_ICE = iota
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var FlagNames := map[string]int {
|
|
|
+ "wall" , TILE_WALL ,
|
|
|
+ "water", TILE_WATER ,
|
|
|
+ "ledge", TILE_LEDGE ,
|
|
|
+ "stair", TILE_STAIR ,
|
|
|
+ "push" , TILE_PUSH ,
|
|
|
+ "north", TILE_NORTH ,
|
|
|
+ "south", TILE_SOUTH ,
|
|
|
+ "east" , TILE_EAST ,
|
|
|
+ "west" , TILE_WEST ,
|
|
|
+ "ice" , TILE_ICE ,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+* This uses an internal lookup table.
|
|
|
+*/
|
|
|
+func (tile * Tile) SetProperty(property string) {
|
|
|
+ val, ok := FlagNames[property];
|
|
|
+ if (ok) {
|
|
|
+ tile.Flags |= (1 << val)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (frame * Frame) Init(index int, duration float64) {
|
|
|
+ frame.Index = index
|
|
|
+ frame.Duration = duration
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * or NULL if no such animation frame. */
|
|
|
+func (tile Tile) Frame(index int) * Frame {
|
|
|
+ if nil == tile.Frames{
|
|
|
+ return nil
|
|
|
+ } else {
|
|
|
+ return tiles.Frames[index]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_frame_count(Tile * tile) {
|
|
|
+ if nil == tile.Frames {
|
|
|
+ return nil
|
|
|
+ } else {
|
|
|
+ return len(tiles.Frames)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+Tile * tile_add_animation_frame(Tile * tile, int index, double duration) {
|
|
|
+ if (tile->frames) {
|
|
|
+ int size;
|
|
|
+ size = dynar_size(tile->frames);
|
|
|
+ if (!dynar_size_(tile->frames, size + 1)) return NULL;
|
|
|
+ tileframe_init(dynar_getdata(tile->frames, size), index, duration);
|
|
|
+ } else {
|
|
|
+ tile->frames = dynar_new(1, sizeof(struct TileFrame_));
|
|
|
+ if (!tile->frames) return NULL;
|
|
|
+ tileframe_init(dynar_getdata(tile->frames, 0), index, duration);
|
|
|
+ }
|
|
|
+ return tile;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void tile_rewindanime(Tile * tile) {
|
|
|
+ if (!tile) return;
|
|
|
+ tile->active = tile->index;
|
|
|
+ tile->active_frame = 0;
|
|
|
+
|
|
|
+ tile_recalculate(tile);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void tile_update_classic(Tile * tile, double dt) {
|
|
|
+ int active = 0;
|
|
|
+ Tile * aidtile = NULL;
|
|
|
+ Tile * nowtile = tileset_get(tile->set, tile->active);
|
|
|
+
|
|
|
+
|
|
|
+ if(!nowtile) return;
|
|
|
+ tile->time += dt;
|
|
|
+
|
|
|
+ if(tile->time < tile->wait) return;
|
|
|
+
|
|
|
+ tile->time = 0.0;
|
|
|
+
|
|
|
+ active = tile->active + nowtile->anim;
|
|
|
+ aidtile = tileset_get(tile->set, active);
|
|
|
+
|
|
|
+ if(!aidtile) return;
|
|
|
+
|
|
|
+ tile->active = active;
|
|
|
+
|
|
|
+ tile_recalculate(tile);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void tile_update_tmx(Tile * tile, double dt) {
|
|
|
+ int active = 0;
|
|
|
+ TileFrame * frame = NULL;
|
|
|
+ Tile * aidtile = NULL;
|
|
|
+
|
|
|
+ frame = tile_frame(tile, tile->active_frame);
|
|
|
+ if (!frame) {
|
|
|
+ tile->active_frame = 0;
|
|
|
+ frame = tile_frame(tile, tile->active_frame);
|
|
|
+ if (!frame) return;
|
|
|
+ }
|
|
|
+
|
|
|
+ tile->time += dt;
|
|
|
+
|
|
|
+ if(tile->time < frame->duration) return;
|
|
|
+
|
|
|
+ tile->active_frame++;
|
|
|
+ if (tile->active_frame >= tile_frame_count(tile)) {
|
|
|
+ tile->active_frame = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame = tile_frame(tile, tile->active_frame);
|
|
|
+
|
|
|
+ tile->time = 0.0;
|
|
|
+ if (!frame) return;
|
|
|
+
|
|
|
+
|
|
|
+ active = frame->index;
|
|
|
+ aidtile = tileset_get(tile->set, active);
|
|
|
+
|
|
|
+ if(!aidtile) return;
|
|
|
+
|
|
|
+ tile->active = active;
|
|
|
+
|
|
|
+ tile_recalculate(tile);
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * precedence, otherwise, use the classic style animation. */
|
|
|
+void tile_update(Tile * tile, double dt) {
|
|
|
+ if (tile->frames) {
|
|
|
+ tile_update_tmx(tile, dt);
|
|
|
+ } else {
|
|
|
+ tile_update_classic(tile, dt);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void tileset_update(Tileset * set, double dt) {
|
|
|
+ int index, size;
|
|
|
+ if (!set) return;
|
|
|
+ if (!set->tiles) return;
|
|
|
+ size = tileset_size(set);
|
|
|
+ for (index = 0; index < size; index++) {
|
|
|
+ Tile * tile = tileset_get(set, index);
|
|
|
+ tile_update(tile, dt);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+given coordinates. Does nothing if tile is NULL. */
|
|
|
+void tile_draw(Tile * tile, int x, int y, int drawflags) {
|
|
|
+ Tileset * set;
|
|
|
+ Image * sheet;
|
|
|
+ Color dcolor = al_map_rgb(0xee, 0xee, 0x00);
|
|
|
+ if (!tile) return;
|
|
|
+ set = tile->set;
|
|
|
+ sheet = set->sheet;
|
|
|
+ float dx = (float) x;
|
|
|
+ float dy = (float) y;
|
|
|
+ float sx = (float) tile->now.x;
|
|
|
+ float sy = (float) tile->now.y;
|
|
|
+ float sw = (float) TILE_W;
|
|
|
+ float sh = (float) TILE_H;
|
|
|
+ al_draw_bitmap_region(sheet, sx, sy, sw, sh, dx, dy, drawflags);
|
|
|
+
|
|
|
+#ifdef TILE_SHOW_SOLID
|
|
|
+ if (tile_isflag(tile, TILE_WALL)) {
|
|
|
+ al_draw_rectangle(dx, dy, dx+TILE_W, dy+TILE_H, dcolor, 2);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static Image * tile_mask_buffer = NULL;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * applying the given mask bitmap, where the mask will
|
|
|
+be flipped and rotated as per the given mask_flags. The mask bitmap
|
|
|
+should be white, but with different alpha levels on the white
|
|
|
+which will be applied as the mask. Does nothing if tile is NULL.
|
|
|
+This requires al_hold_bitmap_drawing to be turned off!
|
|
|
+*/
|
|
|
+void tile_draw_masked_to
|
|
|
+(Image * result, Tile * tile, Image * mask, float angle, int mask_flags) {
|
|
|
+
|
|
|
+ Tileset * set;
|
|
|
+ Image * sheet;
|
|
|
+ ALLEGRO_BITMAP * target;
|
|
|
+ Color dcolor = al_map_rgb(0xee, 0x00, 0xee);
|
|
|
+ float dx, dy, sx, sy, sw, sh;
|
|
|
+ int bmpflags;
|
|
|
+
|
|
|
+ if (!tile) return;
|
|
|
+
|
|
|
+
|
|
|
+ it being static. And leaked at program shutdown, but I don't care :p. */
|
|
|
+ if (!tile_mask_buffer) {
|
|
|
+ bmpflags = al_get_new_bitmap_flags();
|
|
|
+ al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);
|
|
|
+ tile_mask_buffer = al_create_bitmap(TILE_W, TILE_H);
|
|
|
+ al_set_new_bitmap_flags(bmpflags);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ target = al_get_target_bitmap();
|
|
|
+
|
|
|
+
|
|
|
+ al_set_target_bitmap(tile_mask_buffer);
|
|
|
+ set = tile->set;
|
|
|
+ sheet = set->sheet;
|
|
|
+ dx = 0.0;
|
|
|
+ dy = 0.0;
|
|
|
+ sx = (float) tile->now.x;
|
|
|
+ sy = (float) tile->now.y;
|
|
|
+ sw = (float) TILE_W;
|
|
|
+ sh = (float) TILE_H;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
|
|
|
+ al_draw_bitmap_region(sheet, sx, sy, sw, sh, 0, 0, 0);
|
|
|
+
|
|
|
+
|
|
|
+ al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);
|
|
|
+ al_draw_bitmap(mask, 0, 0, mask_flags);
|
|
|
+
|
|
|
+
|
|
|
+ al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
|
|
|
+
|
|
|
+ sx = 0.0;
|
|
|
+ sy = 0.0;
|
|
|
+ if (angle != 0.0) {
|
|
|
+ sx = TILE_H / 2.0;
|
|
|
+ sy = TILE_W / 2.0;
|
|
|
+ dx += sx;
|
|
|
+ dy += sy;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ al_set_target_bitmap(result);
|
|
|
+ al_draw_rotated_bitmap(tile_mask_buffer, sx, sy, dx, dy, angle, 0);
|
|
|
+
|
|
|
+ al_set_target_bitmap(target);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_index(Tile * tile) {
|
|
|
+ if (!tile) return -1;
|
|
|
+ return tile->index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_kind(Tile * tile) {
|
|
|
+ if (!tile) return -1;
|
|
|
+ return tile->kind;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Zero means no blending, positive is a blend priority.
|
|
|
+ */
|
|
|
+int tile_blend(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->blend;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_blend_(Tile * tile, int priority) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->blend = priority;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Returns 0 for the default mask if not set.
|
|
|
+ */
|
|
|
+int tile_blend_mask(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->blend_mask;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_blend_mask_(Tile * tile, int mask) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ if (mask < 0) mask = 0;
|
|
|
+ if (mask > 2) mask = 0;
|
|
|
+ return tile->blend_mask = mask;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_light(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->light;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_light_(Tile * tile, int value) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->light = value;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Returns 0 for the default mask if not set.
|
|
|
+ */
|
|
|
+int tile_light_mask(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->light_mask;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_light_mask_(Tile * tile, int mask) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ if (mask < 0) mask = 0;
|
|
|
+ return tile->light_mask = mask;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_shadow(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->shadow;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_shadow_(Tile * tile, int value) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->shadow = value;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Returns 0 for the default mask if not set.
|
|
|
+ */
|
|
|
+int tile_shadow_mask(Tile * tile) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ return tile->shadow_mask;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int tile_shadow_mask_(Tile * tile, int mask) {
|
|
|
+ if (!tile) return 0;
|
|
|
+ if (mask < 0) mask = 0;
|
|
|
+ return tile->shadow_mask = mask;
|
|
|
+}
|
|
|
+
|