1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module atelier.render.tileset; 7 8 import std.conv; 9 10 import bindbc.sdl, bindbc.sdl.image; 11 12 import atelier.core; 13 import atelier.render.window; 14 import atelier.render.sprite; 15 import atelier.render.drawable; 16 17 /// Series of aligned tiles. 18 final class Tileset { 19 @property { 20 /// loaded ? 21 bool isLoaded() const { 22 return drawable.isLoaded; 23 } 24 /// Number of tiles 25 int maxtiles() const { 26 return _maxtiles; 27 } 28 /// Ditto 29 int maxtiles(int newMaxTiles) { 30 _maxtiles = newMaxTiles; 31 if (_maxtiles <= 0 || _maxtiles > columns * lines) 32 _maxtiles = columns * lines; 33 return _maxtiles; 34 } 35 } 36 37 /// The maximum number of tile, cannot be more than columns * lines, cannot be less than 0. 38 private int _maxtiles; 39 40 /// Drawable region of the first tile (the source size). 41 Vec4i clip; 42 43 /// Spacing between each tiles. 44 Vec2i margin; 45 46 /// Number of tiles on the horizontal axis. 47 int columns = 1, /// Number of tiles on the vertical axis. 48 lines = 1; 49 50 /// Source material. 51 Drawable drawable; 52 53 /// Destination size. 54 Vec2f size = Vec2f.zero, scale = Vec2f.one; 55 56 /// Angle in which the sprite will be rendered. 57 float angle = 0f; 58 59 /// Mirroring property. 60 Flip flip = Flip.none; 61 62 /// Anchor. 63 Vec2f anchor = Vec2f.half; 64 65 /// Color added to the tile. 66 Color color = Color.white; 67 68 /// Opacity of the tile. 69 float alpha = 1f; 70 71 /// Blending algorithm. 72 Blend blend = Blend.alpha; 73 74 /// Ctor 75 this() { 76 } 77 78 /// Copy ctor 79 this(Tileset tileset) { 80 _maxtiles = tileset._maxtiles; 81 clip = tileset.clip; 82 margin = tileset.margin; 83 columns = tileset.columns; 84 lines = tileset.lines; 85 drawable = tileset.drawable; 86 size = tileset.size; 87 scale = tileset.scale; 88 angle = tileset.angle; 89 flip = tileset.flip; 90 anchor = tileset.anchor; 91 color = tileset.color; 92 alpha = tileset.alpha; 93 blend = tileset.blend; 94 } 95 96 /// Ctor 97 this(Drawable newDrawable, Vec4i newClip, int newColumns, int newLines, int newMaxTiles = -1) { 98 drawable = newDrawable; 99 clip = newClip; 100 columns = newColumns; 101 lines = newLines; 102 size = to!Vec2f(clip.zw); 103 maxtiles(newMaxTiles); 104 } 105 106 /// Create a new Sprite from the tile id. 107 Sprite getSprite(int id) { 108 if (id >= _maxtiles) 109 id = _maxtiles - 1; 110 if (id < 0) 111 id = 0; 112 Vec2i coord = Vec2i(id % columns, id / columns); 113 Vec4i spriteClip = Vec4i(clip.x + coord.x * clip.z, clip.y + coord.y * clip.w, 114 clip.z, clip.w); 115 Sprite sprite = new Sprite(drawable, spriteClip); 116 sprite.flip = flip; 117 sprite.blend = blend; 118 sprite.color = color; 119 sprite.alpha = alpha; 120 sprite.anchor = anchor; 121 sprite.angle = angle; 122 sprite.size = size; 123 return sprite; 124 } 125 126 /// Return each tile as a Sprite. 127 Sprite[] asSprites() { 128 Sprite[] sprites; 129 foreach (id; 0 .. _maxtiles) 130 sprites ~= getSprite(id); 131 return sprites; 132 } 133 134 /// Set the sprite's size to fit inside the specified size. 135 void fit(Vec2f size_) { 136 size = to!Vec2f(clip.zw).fit(size_); 137 } 138 139 /// Render a tile 140 void drawRotated(int id, const Vec2f position) { 141 if (id >= _maxtiles) 142 id = _maxtiles - 1; 143 if (id < 0) 144 id = 0; 145 146 Vec2i coord = Vec2i(id % columns, id / columns); 147 if (coord.y > lines) 148 throw new Exception("Tileset id out of bounds"); 149 150 Vec2f finalSize = scale * size * transformScale(); 151 Vec2f dist = (anchor - Vec2f.half).rotated(angle) * size * scale; 152 153 Vec4i currentClip = Vec4i(clip.x + coord.x * (clip.z + margin.x), 154 clip.y + coord.y * (clip.w + margin.y), clip.z, clip.w); 155 drawable.color = color; 156 drawable.blend = blend; 157 drawable.alpha = alpha; 158 drawable.draw(transformRenderSpace(position - dist), finalSize, currentClip, angle, flip); 159 } 160 161 /// Ditto 162 void draw(int id, const Vec2f position) { 163 if (id >= _maxtiles) 164 id = _maxtiles - 1; 165 if (id < 0) 166 id = 0; 167 168 Vec2f finalSize = scale * size * transformScale(); 169 Vec2i coord = Vec2i(id % columns, id / columns); 170 if (coord.y > lines) 171 throw new Exception("Tileset id out of bounds"); 172 Vec4i currentClip = Vec4i(clip.x + coord.x * (clip.z + margin.x), 173 clip.y + coord.y * (clip.w + margin.y), clip.z, clip.w); 174 drawable.color = color; 175 drawable.blend = blend; 176 drawable.alpha = alpha; 177 drawable.draw(transformRenderSpace(position), finalSize, currentClip, angle, flip, anchor); 178 } 179 }