/
db.lua
282 lines (254 loc) · 7.98 KB
/
db.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
-- Aseprite Attachment System
-- Copyright (c) 2022-2023 Igara Studio S.A.
--
-- This file is released under the terms of the MIT license.
-- Read LICENSE.txt for more information.
----------------------------------------------------------------------
-- Extension Properties:
--
-- Sprite = {
-- version = 4
-- }
--
-- Tileset = { -- A tileset represents a category for one layer
-- id = categoryID, -- Tileset/category ID, referenced by layers that can use this category/tileset
-- }
--
-- Layer = {
-- id=layerID, -- ID for this layer (only for tilemap layers!)
-- categories={ categoryID1, categoryID2, etc. },
-- folders={
-- { name="Folder Name",
-- items={ { tile=tileIndex1, position=Point(column, row) }, ... },
-- viewport=Size(columns, rows) }
-- },
-- }
--
-- Tile = {
-- ref=Point(x, y), -- this reference point is present only in the categoryID1 tileset
-- anchors={
-- { layerId=layerID,
-- position=Point(x1, y1)},
-- }
-- }
----------------------------------------------------------------------
local db = {
-- Plugin-key to access extension properties ("the DB") in
-- layers/tiles/etc. E.g. layer.properties(PK)
PK = "aseprite/Attachment-System",
-- Version of the database (DB)
kLatestDBVersion = 4,
kBaseSetName = "Base Set",
}
local PK = db.PK
local function contains(t, item)
for _,v in pairs(t) do
if v == item then
return true
end
end
return false
end
local function createBaseSetFolder(layer)
if #layer.tileset == 1 then
layer.sprite:newTile(layer.tileset, 1)
end
local items = {}
for ti=1,#layer.tileset-1 do
table.insert(items, { tile=ti, position=Point(ti-1, 0) })
end
return { name=db.kBaseSetName, items=items }
end
-- Update layer folders from version=1 to version=2 Before this
-- folders have an array of tile indexes, and now is a tile of objects
-- (each object with a "tile" field, then a "position" is optional and
-- created only when we drop).
local function updateFoldersFromIndexesToObjects(folders)
for i=1,#folders do
local folder = folders[i]
for j=1,#folder.items do
folder.items[j] = { tile=folder.items[j], position=Point(j-1, 0) }
end
folders[i] = folder
end
return folders
end
local function setupLayers(spr, currentVersion, layers)
for _,layer in ipairs(layers) do
if layer.isTilemap then
-- Add ID to the layer (this was added in DB version=2)
if not layer.properties(PK).id then
layer.properties(PK).id = db.calculateNewLayerID(spr)
end
local categories = layer.properties(PK).categories
local folders = layer.properties(PK).folders
local tilesetID = layer.tileset.properties(PK).id
assert(tilesetID ~= nil)
if not categories then
categories = { }
end
if not contains(categories, tilesetID) then
table.insert(categories, tilesetID)
layer.properties(PK).categories = categories
end
if not folders or #folders == 0 then
layer.properties(PK).folders = { createBaseSetFolder(layer) }
elseif currentVersion < 3 then
-- In version=3 folders items were converted from tile indexes
-- to objects with {tile,position}
layer.properties(PK).folders =
updateFoldersFromIndexesToObjects(layer.properties(PK).folders)
end
end
if layer.isGroup then
setupLayers(spr, currentVersion, layer.layers)
end
end
end
local function calculateMaxLayerIDBetweenLayers(layers)
local maxId = 0
for i=1,#layers do
local layer = layers[i]
if layer and layer.properties(PK).id then
maxId = math.max(maxId, layer.properties(PK).id)
end
if layer.isGroup then
maxId = math.max(maxId, calculateMaxLayerIDBetweenLayers(layer.layers))
end
end
return maxId
end
----------------------------------------------------------------------
-- Public API
----------------------------------------------------------------------
function db.calculateNewLayerID(spr)
return 1+calculateMaxLayerIDBetweenLayers(spr.layers)
end
function db.calculateNewCategoryID(spr)
local maxId = 0
for i=1,#spr.tilesets do
local tileset = spr.tilesets[i]
if tileset and tileset.properties(PK).id then
maxId = math.max(maxId, tileset.properties(PK).id)
end
end
return maxId+1
end
function db.findTilesetByCategoryID(spr, categoryID)
for i=1,#spr.tilesets do
local ts = spr.tilesets[i]
if ts and ts.properties(PK).id == categoryID then
return ts
end
end
return nil
end
function db.isBaseSetFolder(folder)
return (folder.name == db.kBaseSetName)
end
function db.getBaseSetFolder(layer, folders)
assert(folders)
for _,folder in ipairs(folders) do
if db.isBaseSetFolder(folder) then
return folder
end
end
local folder = createBaseSetFolder(layer)
table.insert(folders, folder)
return folder
end
-- Gets the base tileset (the tileset assigned to the first category
-- of the layer, or just the active tileset if the layer doesn't
-- contain categories yet). This tileset is the one used to store the
-- anchor/reference points per tile.
function db.getBaseTileset(layer)
local ts = nil
local layerProperties = layer.properties(PK)
if layerProperties.categories and #layerProperties.categories then
ts = db.findTilesetByCategoryID(layer.sprite,
layerProperties.categories[1])
if not ts then
ts = layer.tileset
end
else
ts = layer.tileset
end
return ts
end
function db.findAnchorOnLayer(parentLayer, childLayer, parentTile)
if parentLayer.isTilemap and childLayer.isTilemap then
local baseTs = db.getBaseTileset(parentLayer)
local anchors = baseTs:tile(parentTile).properties(PK).anchors
if anchors and #anchors >= 1 then
for i=1, #anchors, 1 do
if anchors[i].layerId == childLayer.properties(PK).id then
return anchors[i]
end
end
end
end
return nil
end
function db.findParentLayer(layers, childLayer)
for _,layer in ipairs(layers) do
if layer.isGroup then
local result = db.findParentLayer(layer.layers, childLayer)
if result then return result end
elseif db.findAnchorOnLayer(layer, childLayer, 1) then
return layer
end
end
return nil
end
-- These properties should be set in setupLayers()/setupSprite(), but
-- we can set them here just in case. Anyway if the setup functions
-- don't fully setup the properties, we'll generate undo/redo
-- information just showing the layer in the Attachment System window
function db.getLayerProperties(layer)
local properties = layer.properties(PK)
if not properties.id then
properties.id = db.calculateNewLayerID(layer.sprite)
end
if not properties.categories then
properties.categories = {}
end
local id = layer.tileset.properties(PK).id
if not id then
id = db.calculateNewCategoryID(layer.sprite)
layer.tileset.properties(PK).id = id
end
if not contains(properties.categories, id) then
-- Don't use table.insert(properties.categories, id) directly, as
-- the id will not be added to the properties.categories table.
local categories = properties.categories
table.insert(categories, id)
properties.categories = categories
end
if not properties.folders or #properties.folders == 0 then
properties.folders = { createBaseSetFolder(layer) }
end
return properties
end
function db.setupSprite(spr)
-- Setup the sprite DB
local currentVersion = spr.properties(PK).version
if currentVersion == nil then
currentVersion = 0
end
-- Add ID to each tileset
for i=1,#spr.tilesets do
local tileset = spr.tilesets[i]
if tileset and not tileset.properties(PK).id then
tileset.properties(PK).id = db.calculateNewCategoryID(spr)
end
end
-- Setup each tilemap layer
setupLayers(spr, currentVersion, spr.layers)
-- Latest version in the sprite
spr.properties(PK).version = db.kLatestDBVersion
-- Introduced in version=4
if spr.tileManagementPlugin ~= PK then
spr.tileManagementPlugin = PK
end
end
return db