123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- package tile
- import import "log"
- // import "os"
- // import "math"
- import al "gitlab.com/beoran/al5go/al"
- import al "gitlab.com/beoran/ebsgo/engine/geometry/point"
- /** A tile set */
- type Set struct {
- Tiles []Tile
- Sheet * al.Bitmap
- W int
- H int
- TileW int
- TileH int
- FirstGID int /* Offset of tile set in TMX map file. Used to correct tile offsets. */
- };
- /** A Tiled-style animation frame of a tile. */
- type Frame struct {
- Index int /* Tile set index for this frame of animation. */
- Duration float64 /* Duration of the frame in s. */
- }
- /**
- * 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 /* Tileset this tile belongs to */
- Index int /* Index in the tile set. */
- Flags int /* Information about the tile's properties. */
- Kind int
- /* Offset to the tile to skip to when animating. If this is
- 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 unanimated tiles, active is set to the index of the tile itself.
- 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 in s to wait before jumping to the next frame of this tile. */
- Time float64
- /* Time since last animation in s. */
- Position point.P
- /* Automatic blending activation and priority. */
- Blend int
- /* Mask number to use for automatic blending, if any. */
- BlendMAsk int
- /* Automatic lighting activation flag. */
- Light int
- LightMAsk int
- /* Automatic shadow activation flag. */
- Shadow int
- /* Automatic shadow mask number. */
- ShadowMask int
- /* Tiled-style animation frames. */
- Frames []Frame
- /* Active frame for TMX-style animations. */
- ActiveFrame int
- };
- /* NOTE: Tiles could be implemented using sub bitmaps as they seem to be
- * 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)
- }
- /** Recalculates the tile's position (now) in it's tile set. */
- 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
- }
- /** Tile types */
- 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
- )
-
-
- /* Helper lookup table for the tile flag names */
- 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 ,
- }
- /** Sets a tile's flags from a property string.
- * This uses an internal lookup table.
- */
- func (tile * Tile) SetProperty(property string) {
- val, ok := FlagNames[property];
- if (ok) {
- tile.Flags |= (1 << val)
- }
- }
- /** Initializes a tile's frame of animation. */
- func (frame * Frame) Init(index int, duration float64) {
- frame.Index = index
- frame.Duration = duration
- }
- /** Gets the nth frame of Tiled style animations for this tile
- * 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]
- }
- }
- /** Gets the amount of Tiled style animations for this tile, or 0 if none. */
- int tile_frame_count(Tile * tile) {
- if nil == tile.Frames {
- return nil
- } else {
- return len(tiles.Frames)
- }
- }
- /** Adds a Tiled-style animation frame to the tile. */
- 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;
- }
- /** Rewinds a tile's animations. */
- void tile_rewindanime(Tile * tile) {
- if (!tile) return;
- tile->active = tile->index;
- tile->active_frame = 0;
- // Finally recalculate tile position.
- tile_recalculate(tile);
- }
- /** Updates a tile to animate it using classic style animation.*/
- void tile_update_classic(Tile * tile, double dt) {
- int active = 0;
- Tile * aidtile = NULL;
- Tile * nowtile = tileset_get(tile->set, tile->active);
- // nowtile is the tile that is currently active, that is shown.
- // in stead of ourself, but it also may be ourself.
- if(!nowtile) return;
- tile->time += dt; // advance animation time of tile.
- // Don't animate if not enough time has passed
- if(tile->time < tile->wait) return;
- // if we get here, reset animation time.
- tile->time = 0.0;
- // take the animation parameter and add it to the active
- active = tile->active + nowtile->anim;
- aidtile = tileset_get(tile->set, active);
- // Check if there is such a tile.
- if(!aidtile) return;
- // If there is no such tile, don't change the active tile of this tile.
- tile->active = active;
- // Finally recalculate tile position.
- tile_recalculate(tile);
- }
- /** Updates a tile to anmate it using TMX style animation. */
- 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) { /* Animation overshoit itself somehow??? */
- tile->active_frame = 0;
- frame = tile_frame(tile, tile->active_frame);
- if (!frame) return;
- }
-
- tile->time += dt; // advance animation time of tile.
- // Don't animate if not enough time has passed
- if(tile->time < frame->duration) return;
- // advance the animation frame, loop it around if needed.
- tile->active_frame++;
- if (tile->active_frame >= tile_frame_count(tile)) {
- tile->active_frame = 0;
- }
- // Get new tile frame
- frame = tile_frame(tile, tile->active_frame);
- // If we get here, reset animation time.
- tile->time = 0.0;
- if (!frame) return;
-
- // Get the active tile t use from the animation frame
- active = frame->index;
- aidtile = tileset_get(tile->set, active);
- // Check if there is such a tile.
- if(!aidtile) return;
- // If there is no such tile, don't change the active tile of this tile.
- tile->active = active;
- // Finally recalculate tile position.
- tile_recalculate(tile);
- // tile->now = aidtile->now;
- // 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);
- }
- /* Animates the tile. If it has a TMX style animation, that takes
- * 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);
- }
- }
- /** Updates all tiles in a tile set so they all get animated. */
- 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);
- }
- }
- /** Draw a tile to the current active drawing target at the
- 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);
- // debugging solid tiles
- #ifdef TILE_SHOW_SOLID
- if (tile_isflag(tile, TILE_WALL)) {
- al_draw_rectangle(dx, dy, dx+TILE_W, dy+TILE_H, dcolor, 2);
- }
- #endif // TILE_SHOW_SOLID
-
- // al_draw_bitmap(sheet, dx, dy, 0);
- }
- /* Used for drawing masked tiles. */
- static Image * tile_mask_buffer = NULL;
- /** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H
- * 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) {
- /* This function need a mask buffer. */
- 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;
-
- /* Create a 32x32 tile bitmap that will be reused thanks to
- 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);
- }
-
- /* Keep the target bitmap. */
- target = al_get_target_bitmap();
-
- /* Copy the tile into the buffer. */
- 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;
- /* Set blender to copy mode. */
- // al_clear_to_color(al_map_rgba_f(0,0,0,0));
- al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
- al_draw_bitmap_region(sheet, sx, sy, sw, sh, 0, 0, 0);
-
- /* Draw the mask over the tile, taking the alpha of the mask */
- al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);
- al_draw_bitmap(mask, 0, 0, mask_flags);
- /* Restore normal Allegro blending. */
- 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;
- }
-
- /* Draw the tile mask buffer to the result bitmap */
- al_set_target_bitmap(result);
- al_draw_rotated_bitmap(tile_mask_buffer, sx, sy, dx, dy, angle, 0);
- /* And restore the target bitmap. */
- al_set_target_bitmap(target);
- }
- /** Tile's index. Returns -1 if tile is NULL; */
- int tile_index(Tile * tile) {
- if (!tile) return -1;
- return tile->index;
- }
- /** Information about the tile's properties. Return -1 if tile is NULL. */
- int tile_kind(Tile * tile) {
- if (!tile) return -1;
- return tile->kind;
- }
- /** Information about tile's blending properties and priority.
- * Zero means no blending, positive is a blend priority.
- */
- int tile_blend(Tile * tile) {
- if (!tile) return 0;
- return tile->blend;
- }
- /** Set the tile's blending and priority */
- int tile_blend_(Tile * tile, int priority) {
- if (!tile) return 0;
- return tile->blend = priority;
- }
- /** Information about tile's blending mask
- * Returns 0 for the default mask if not set.
- */
- int tile_blend_mask(Tile * tile) {
- if (!tile) return 0;
- return tile->blend_mask;
- }
- /** Set the tile's blending 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;
- }
- /** Get the tile's light flag. Zero means no lighting. */
- int tile_light(Tile * tile) {
- if (!tile) return 0;
- return tile->light;
- }
- /** Set the tile's light flag */
- int tile_light_(Tile * tile, int value) {
- if (!tile) return 0;
- return tile->light = value;
- }
- /** Information about tile's lighting mask
- * Returns 0 for the default mask if not set.
- */
- int tile_light_mask(Tile * tile) {
- if (!tile) return 0;
- return tile->light_mask;
- }
- /** Set the tile's light mask */
- int tile_light_mask_(Tile * tile, int mask) {
- if (!tile) return 0;
- if (mask < 0) mask = 0;
- return tile->light_mask = mask;
- }
- /** Get the tile's shadow flag. Zero means no autoshadow. */
- int tile_shadow(Tile * tile) {
- if (!tile) return 0;
- return tile->shadow;
- }
- /** Set the tile's light flag */
- int tile_shadow_(Tile * tile, int value) {
- if (!tile) return 0;
- return tile->shadow = value;
- }
- /** Information about tile's shadow mask
- * Returns 0 for the default mask if not set.
- */
- int tile_shadow_mask(Tile * tile) {
- if (!tile) return 0;
- return tile->shadow_mask;
- }
- /** Set the tile's shadow mask */
- int tile_shadow_mask_(Tile * tile, int mask) {
- if (!tile) return 0;
- if (mask < 0) mask = 0;
- return tile->shadow_mask = mask;
- }
|