SHOW:
|
|
- or go back to the newest paste.
1 | - | -- Version: Unspeakable Crawling Skull |
1 | + | -- Version: Unspeakable Crawling Skully |
2 | ||
3 | Texter = {} | |
4 | Texter._LastInput = { | |
5 | Str = "", | |
6 | Args = "1, 3", | |
7 | Font = "7px", | |
8 | Mode = 0 | |
9 | } | |
10 | Texter._Fonts = {} | |
11 | ||
12 | ------------------ User config zone ------------------- | |
13 | Texter.FONT_FOLDERS_TO_LOAD = {"TexterFonts", "Scripts", "%."} -- All file will be "dofile" under those foler name, the root dir must be written regex form %. | |
14 | - | Texter.DISPLAY_MESSAGES = true -- Should show the startup message [true: show, false: don't show] |
14 | + | Texter.DISPLAY_MESSAGES = false -- Should show the startup message [true: show, false: don't show] |
15 | Texter.FONT_SEARCH_DEPTH = 3 -- How deep should texter search the font folder [0: only the root folder] | |
16 | ---------------- User config zone ends ---------------- | |
17 | ||
18 | function Texter.Init(register) | |
19 | Texter._Fontmatrix = {} | |
20 | Texter._Fonts = {} | |
21 | -- Please don't let Texter too far away from font file :) | |
22 | local fontFiles = Texter.LoadAllFontsInFolder(".", Texter.FONT_FOLDERS_TO_LOAD, Texter.FONT_SEARCH_DEPTH) | |
23 | local msg = "" | |
24 | if(Texter._Fontmatrix ~= nil) then | |
25 | for fontName in pairs(Texter._Fontmatrix) do | |
26 | table.insert(Texter._Fonts, fontName) | |
27 | end | |
28 | msg = "Texter: "..#fontFiles.." font file(s) loaded, "..#Texter._Fonts.." font(s) available in total." | |
29 | else | |
30 | msg = "Texter: No font found." | |
31 | end | |
32 | if( Texter.DISPLAY_MESSAGES ) then | |
33 | tpt.log(msg) | |
34 | end | |
35 | if( register == nil or register == true ) then | |
36 | tpt.register_keypress(Texter._HotkeyHandler) | |
37 | end | |
38 | end | |
39 | ||
40 | -- Internal methods | |
41 | function Texter._IsFolder(path) | |
42 | local _isFolder = false | |
43 | if(path ~= nil) then | |
44 | - | -- API bug, not fixed right now (ver 85.0) |
44 | + | -- API bug, fixed(ver 86.0) |
45 | -- if( fs.isDirectory(path) ) then | |
46 | -- _isFolder = true | |
47 | -- end | |
48 | if( fs.isDirectory(path) == fs.isDirectory(".") ) then | |
49 | _isFolder = true | |
50 | end | |
51 | end | |
52 | return _isFolder | |
53 | end | |
54 | function Texter._GetFontHeight(fontName) | |
55 | local height = 0 | |
56 | if( Texter._Fontmatrix ~= nil and Texter._Fontmatrix[fontName] ~=nil and Texter._Fontmatrix[fontName].height ~= nil ) then | |
57 | height = Texter._Fontmatrix[fontName].height | |
58 | else | |
59 | height = 7 -- 7 is a lucky number | |
60 | end | |
61 | return height | |
62 | end | |
63 | function Texter._Input(eventHandler, fonts, paramsText, strText, fontText, mode) | |
64 | local controlHeight = 17 | |
65 | if( paramsText == nil ) then paramsText = "" end | |
66 | if( strText == nil ) then strText = "" end | |
67 | if( fontText == nil ) then fontText = "" end | |
68 | if( mode == nil ) then mode = 0 end | |
69 | if( fonts == nil ) then fonts = {} end | |
70 | ||
71 | local isContains = function(inTable, value) | |
72 | local isContain = false | |
73 | for i=1, #inTable do | |
74 | if( value == inTable[i] ) then | |
75 | isContain = true | |
76 | break | |
77 | end | |
78 | end | |
79 | return isContain | |
80 | end | |
81 | ||
82 | local TexterWin = Window:new(-1, -1, 500, 100) | |
83 | local paramsBox = Textbox:new(10, controlHeight + 15, 150, controlHeight, paramsText, "ptype, hspc, vspc") | |
84 | local paramsLabel = Label:new(11, 9, 130, controlHeight, "Element type and\nhorizontal/vertical spacing.") | |
85 | local modeBox = Button:new(170, controlHeight + 15, 150, controlHeight, "No mode available") | |
86 | local modeLabel = Label:new(171, 9, 150, controlHeight, "Font mode you want to use.") | |
87 | local stringBox = Textbox:new(10, 57, 480, controlHeight, strText, "Input text to create under mouse. Use \\n for new line, \\\\n for \\n. Sorry, you can't get \\\\n.") | |
88 | local fontBtn = Button:new(330, controlHeight + 15, 150, controlHeight, "No font available") | |
89 | local fontLabel = Label:new(331, 9, 150, controlHeight, "Select the font you want to use.") | |
90 | local cancelBtn = Button:new(0, 83, 251, controlHeight, "Cancel") | |
91 | cancelBtn:action(function(sender)interface.closeWindow(TexterWin)end) | |
92 | local okayBtn = Button:new(250, 83, 250, controlHeight, "Okay") | |
93 | okayBtn:action( | |
94 | function(sender) | |
95 | if( eventHandler ~= nil and type(eventHandler) == "function" ) then | |
96 | -- Use event because I can't make things like tpt.input | |
97 | eventHandler(paramsBox:text(), stringBox:text(), fontBtn:text(), tonumber(modeBox:text())) | |
98 | end | |
99 | interface.closeWindow(TexterWin) | |
100 | end | |
101 | ) | |
102 | if( mode >= 0 ) then | |
103 | modeBox:text(tostring(mode)) | |
104 | end | |
105 | modeBox:action( | |
106 | function(sender) | |
107 | local input = tpt.input("Font mode", "The draw mode:\n0 Ignore all additional information\n+1 Keep the ptype in font\n+2 Keep the dcolor in font\nAdd your choices up then type in.") | |
108 | local mode = tonumber(input) | |
109 | if( mode~= nil and mode >= 0 )then | |
110 | sender:text(input) | |
111 | else | |
112 | sender:text("0") | |
113 | end | |
114 | end | |
115 | ) | |
116 | if(#fonts > 0) then | |
117 | if( isContains(fonts, fontText) ) then | |
118 | fontBtn:text(fontText) | |
119 | else | |
120 | fontBtn:text(fonts[1]) | |
121 | end | |
122 | fontBtn:action( | |
123 | function(sender) | |
124 | local winX, winY = TexterWin:position() | |
125 | local btnX, btnY = fontBtn:position() | |
126 | local btnW, btnH = fontBtn:size() | |
127 | local listWin = Window:new(winX+btnX, winY+btnY-controlHeight*(#fonts-1)/2, btnW, (btnH-1)*#fonts) | |
128 | for i=1, #fonts do | |
129 | local optionBtn = Button:new(0, (btnH-1)*(i-1), btnW, btnH, fonts[i]) | |
130 | optionBtn:action( | |
131 | function(sender) | |
132 | interface.closeWindow(listWin) | |
133 | fontBtn:text(optionBtn:text()) | |
134 | end | |
135 | ) | |
136 | listWin:addComponent(optionBtn) | |
137 | end | |
138 | interface.showWindow(listWin) | |
139 | end | |
140 | ) | |
141 | end | |
142 | ||
143 | TexterWin:addComponent(cancelBtn) | |
144 | TexterWin:addComponent(okayBtn) | |
145 | TexterWin:addComponent(stringBox) | |
146 | TexterWin:addComponent(paramsBox) | |
147 | TexterWin:addComponent(paramsLabel) | |
148 | TexterWin:addComponent(modeBox) | |
149 | TexterWin:addComponent(modeLabel) | |
150 | TexterWin:addComponent(fontBtn) | |
151 | TexterWin:addComponent(fontLabel) | |
152 | ||
153 | interface.showWindow(TexterWin) | |
154 | end | |
155 | function Texter._FindAllFile(rootPath, foldersToLoad, depth) | |
156 | if( rootPath == nil ) then | |
157 | rootPath = "." | |
158 | end | |
159 | if( depth == nil ) then | |
160 | depth = 1 | |
161 | end | |
162 | local files = {} | |
163 | local isTargetFolder = false | |
164 | for i, folderName in ipairs(foldersToLoad) do | |
165 | if( string.match(rootPath, "\\?"..folderName.."$") ~= nil ) then | |
166 | isTargetFolder = true | |
167 | break | |
168 | end | |
169 | end | |
170 | if( isTargetFolder ) then -- If not the folder we want, ignore it | |
171 | files = fs.list(rootPath) | |
172 | end | |
173 | ||
174 | -- Trim match array | |
175 | local index = 1 -- Lua fool | |
176 | for i=1, #files do | |
177 | if( files[index] == "Texter.lua" | |
178 | or string.match(files[index], "%.texterfont$") == nil | |
179 | or Texter._IsFolder(rootPath.."\\"..files[index]) ) then | |
180 | table.remove(files, index) | |
181 | else | |
182 | files[index] = rootPath.."\\"..files[index] -- full path | |
183 | index = index + 1 | |
184 | end | |
185 | end | |
186 | ||
187 | -- Check subs | |
188 | if( depth > 0 ) then | |
189 | local subs = fs.list(rootPath) | |
190 | local subFiles = nil | |
191 | for i=1, #subs do | |
192 | if( Texter._IsFolder(rootPath.."\\"..subs[i]) ) then | |
193 | subFiles = Texter._FindAllFile(rootPath.."\\"..subs[i], foldersToLoad, depth - 1) | |
194 | Texter._AppendArray(files, subFiles) | |
195 | end | |
196 | end | |
197 | end | |
198 | return files | |
199 | end | |
200 | function Texter._AppendArray(oriArray, arrayToAppend) -- Append an array to the original array | |
201 | if( oriArray~= nil and arrayToAppend ~= nil and #arrayToAppend>0 ) then | |
202 | for i=1, #arrayToAppend do | |
203 | table.insert(oriArray, arrayToAppend[i]) | |
204 | end | |
205 | end | |
206 | return oriArray | |
207 | end | |
208 | ||
209 | -- Helper methods and handlers | |
210 | function Texter._HotkeyHandler(key, keyNum, modifier, event) -- Hotkey handler | |
211 | if( event==1 and keyNum==116 and modifier==64 ) then -- Ctrl + t | |
212 | -- Additional settings | |
213 | local ptype = elements[tpt.selectedl] | |
214 | if( ptype == nil ) then | |
215 | ptype = 1 --"DUST" | |
216 | end | |
217 | -- Prompt | |
218 | Texter._Input( | |
219 | Texter._InputHandler, | |
220 | Texter._Fonts, | |
221 | elements.property(ptype, "Name")..", "..Texter._LastInput.Args, | |
222 | Texter._LastInput.Str, | |
223 | Texter._LastInput.Font, | |
224 | Texter._LastInput.Mode | |
225 | ) | |
226 | end | |
227 | end | |
228 | function Texter._InputHandler(params, str, fontName, mode) -- Input handler | |
229 | local args = {} | |
230 | if( string.len(str) > 0 ) then | |
231 | Texter._LastInput.Str = str | |
232 | str = string.gsub(str, "([^\\]?)\\n", "%1\n") -- Small trick | |
233 | str = string.gsub(str, "([^\\]?)\\\\n", "%1\\n") | |
234 | end | |
235 | ||
236 | if( string.len(params) > 0 ) then Texter._LastInput.Args = string.gsub(params, "%s*%w*%s*,%s*(.*)", "%1") end | |
237 | local i=1 | |
238 | for arg in string.gmatch(params, "%s*(%w*)%s*,?") do | |
239 | args[i] = arg | |
240 | i = i+1 | |
241 | end | |
242 | Texter._LastInput.Font = fontName | |
243 | Texter._LastInput.Mode = mode | |
244 | ||
245 | if( pcall(tpt.element, args[1]) == false ) then | |
246 | args[1] = "DUST" | |
247 | end | |
248 | Texter.Tstr(str, tpt.mousex, tpt.mousey, args[1], mode, args[2], args[3], fontName) | |
249 | end | |
250 | ||
251 | -- APIs | |
252 | function Texter.LoadAllFontsInFolder(rootPath, foldersToLoad, depth) -- Load all fonts in target folder(s) | |
253 | local fonts = Texter._FindAllFile(rootPath, foldersToLoad, depth) | |
254 | if( fonts ~= nil ) then | |
255 | for i=1, #fonts do | |
256 | dofile(fonts[i]) | |
257 | end | |
258 | end | |
259 | return fonts | |
260 | end | |
261 | function Texter.Tchar(char, x, y, ptype, mode, fontName) -- Draw a single character | |
262 | local mtx = {} | |
263 | local letter = {} | |
264 | local PTYPE_MASK = 0xFF | |
265 | local DCOLOR_MASH = 0xFFFFFFFF | |
266 | local DCOLOR_OFFSET = 8 | |
267 | local margin_L = 0 -- margin left | |
268 | local margin_R = 0 -- margin right | |
269 | local margin_T = 0 -- margin top | |
270 | -- if given font not available, use the first available one | |
271 | if( fontName == nil or Texter._Fontmatrix[fontName] == nil ) then | |
272 | for font in pairs(Texter._Fontmatrix) do | |
273 | fontName = font | |
274 | break | |
275 | end | |
276 | end | |
277 | -- if still not available, break | |
278 | if( fontName == nil ) then return 0 end | |
279 | ||
280 | -- get character data | |
281 | letter = Texter._Fontmatrix[fontName][char] | |
282 | if( letter == nil ) then | |
283 | letter = Texter._Fontmatrix[fontName]["nil"] | |
284 | end | |
285 | if( letter == nil ) then return 0 end -- ["nil"] not defined | |
286 | mtx = letter.Mtx | |
287 | if( mtx == nil ) then mtx = {} end | |
288 | if( letter.Margin ~= nil ) then | |
289 | if(letter.Margin.Left ~= nil)then margin_L = letter.Margin.Left end | |
290 | if(letter.Margin.Right ~= nil)then margin_R = letter.Margin.Right end | |
291 | if(letter.Margin.Top ~= nil)then margin_T = letter.Margin.Top end | |
292 | end | |
293 | ||
294 | local width = 0 | |
295 | for i=1, #mtx do --mtx height | |
296 | if(#mtx[i] > width)then width = #mtx[i] end | |
297 | for j=1, width do | |
298 | local particle = mtx[i][j] | |
299 | if( particle~=0 ) then | |
300 | local success = false | |
301 | local p = {} | |
302 | p.ptype = bit.band(particle , PTYPE_MASK ) | |
303 | p.dcolor = bit.band(particle/2^DCOLOR_OFFSET, DCOLOR_MASH) -- bit.rshift can only handle 5 bits :( | |
304 | if( ptype == 0 or ptype == "0" ) then | |
305 | pcall(tpt.delete, x+j-1+margin_L, y+i-1+margin_T) | |
306 | else | |
307 | -- mode 0 use the given type | |
308 | -- +1 use the font ptype only | |
309 | -- +2 use the font dcolor only | |
310 | if( bit.band(mode, 1) ~= 1 ) then | |
311 | p.ptype = ptype | |
312 | end | |
313 | -- tpt.log("particle is "..particle..", ptype is "..ptype..", to draw is "..p.ptype)-- debug | |
314 | pcall(tpt.create, x+j-1+margin_L, y+i-1+margin_T, p.ptype) | |
315 | if( bit.band(mode, 2) == 2 ) then -- Paint it even when failed to create. Because there might be existed particle | |
316 | -- tpt.log("Try to draw dcolor: "..p.dcolor.." ( 0x"..string.format("%X", p.dcolor).." )") --debug | |
317 | - | pcall(tpt.set_property, p.dcolor, x+j-1+margin_L, y+i-1+margin_T) |
317 | + | pcall(tpt.set_property, "dcolor", p.dcolor, x+j-1+margin_L, y+i-1+margin_T, 1, 1) |
318 | end | |
319 | end | |
320 | end | |
321 | end | |
322 | end | |
323 | width = width + margin_L + margin_R | |
324 | return width | |
325 | end | |
326 | function Texter.Tstr(str, x, y, ptype, mode, hspacing, vspacing, fontName) -- Draw a string | |
327 | local ox = 0 | |
328 | local oy = 0 | |
329 | local oy = 0 | |
330 | local fontH = Texter._GetFontHeight(fontName) | |
331 | if( mode == nil ) then | |
332 | mode = 0 | |
333 | end | |
334 | if( hspacing == nil ) then | |
335 | hspacing = 1 | |
336 | end | |
337 | if( vspacing == nil ) then | |
338 | vspacing = 3 | |
339 | end | |
340 | for i=1,string.len(str) do | |
341 | if( string.sub(str, i, i) == "\n" ) then | |
342 | oy = vspacing + oy + fontH | |
343 | ox = 0 | |
344 | else | |
345 | ox = hspacing + ox + Texter.Tchar(string.sub(str, i, i), x+ox, y+oy, ptype, mode, fontName) | |
346 | end | |
347 | end | |
348 | return string.len(str) | |
349 | end | |
350 | function T(str, ptype, mode, hspc, vspc, fontName) -- Shortcut for better user experience | |
351 | if( ptype == nil ) then | |
352 | ptype = elements[tpt.selectedl] -- elements.property(tpt.selectedl, "Name") | |
353 | end | |
354 | if( ptype == nil ) then | |
355 | ptype = "DUST" | |
356 | end | |
357 | if( str == nil ) then str = "" end | |
358 | Texter.Tstr(str, tpt.mousex, tpt.mousey, ptype, mode, hspc, vspc, fontName) | |
359 | return string.len(str) | |
360 | end | |
361 | ||
362 | Texter.Init() |