├── .gitattributes ├── LICENSE.md ├── README.md ├── conf.lua ├── main.lua ├── particles ├── 1.png ├── 2.png ├── circle.png ├── cloud.png ├── ps_earth.png ├── ps_mystic.png ├── shockwave.png ├── smoke.png ├── spark.png ├── twirl.png └── wisp.png ├── ui_scripts ├── ui.lua ├── ui_button.lua ├── ui_checkbox.lua ├── ui_collection.lua ├── ui_element.lua ├── ui_groupbox.lua ├── ui_image.lua ├── ui_imagecollection.lua ├── ui_label.lua ├── ui_listbox.lua ├── ui_pageswitch.lua ├── ui_particleemitter.lua ├── ui_progressbar.lua ├── ui_radiobutton.lua ├── ui_shape.lua ├── ui_spin.lua ├── ui_tabbedlist.lua ├── ui_textfield.lua ├── ui_timer.lua └── ui_uielement.lua └── utils.lua /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mkdxdx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APE 2 | Another Particle Editor for LÖVE2D game engine 3 | 4 | This is a particle system editor for LÖVE2D, created for quick prototyping particle systems. 5 | It features paged interface, runtime texture loading, code-to-clipboard generation and also 6 | includes interface classes for use in other projects aswell. 7 | 8 | USAGE 9 | 10 | In order to texture loading to work, code must not be in a .love file and must be unpacked into separate directory 11 | with folder "particles" in it. 12 | Every value and property of a particle system is changed with spin fields, which are controlled with mousewheel. 13 | Pressing left shift, left ctrl or left alt will modify step, with which value is increased. 14 | Clicking on Texture manager button will show texture manager tab, where user can set texture, offset and add quads. 15 | To generate and copy emitter code to clipboard, click on Code button under Miscellaneous tab in the bottom. 16 | 17 | 18 | All particle textures are distributed under CC BY license. 19 | -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.window.width = 1280 -- t.screen.width in 0.8.0 and earlier 3 | t.window.height = 768 -- t.screen.height in 0.8.0 and earlier 4 | t.window.fullscreen = false 5 | t.window.vsync = true 6 | t.modules.joystick = false 7 | t.title = "ape" 8 | t.identity = "ape" 9 | t.console = false 10 | end -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | APE (another particle editor) for LÖVE2D by cval 3 | 4 | - a particle system editor tool for quick particle system prototyping, multi-paged interface and code-to-clipboard option 5 | - features crude interface system, numerous framework-over-framework attempts; 6 | - meets all the creator's needs, who also hopes that it will meet yours (= 7 | 8 | 25.03.2016 added 'F11' to open save directory by SiENcE 9 | 24.03.2016 added update of GUI when loading particles by SiENcE 10 | 23.03.2016 added load/save functionality by SiENcE 11 | 13.10.2015 version is uploaded on community forum 12 | 13 | IMPORTANT NOTE that this version was created with 0.9.2 engine version, so please update yours if you have version below 14 | otherwise some features will cause editor to crash =( 15 | ]] 16 | 17 | local l_gfx = love.graphics 18 | ui_scrdir = "ui_scripts/" 19 | require(ui_scrdir.."ui") 20 | require("utils") 21 | 22 | -- open savefolder 23 | local function openSavefolder() 24 | love.system.openURL("file://" .. love.filesystem.getSaveDirectory()) 25 | end 26 | 27 | -- take screenshot and save it to the save folder with the current date. 28 | -- If the Screenshots folder does not exist, it will attempt to create it. 29 | local function saveparticle( em, spsrange, spcrange, szspins, rbclgr, cbuseq, gbtexman, lbbmode, gbimode ) 30 | ----------------------------------------------------------------------- 31 | local particle = {} 32 | particle['buffersize'] = em:getBufferSize() 33 | particle['direction'] = em:getDirection() 34 | 35 | local as,asx,asy = em:getAreaSpread() 36 | particle['areaspread'] = { as=as, asx=asx, asy=asy } 37 | 38 | particle['emissionrate'] = em:getEmissionRate() 39 | particle['emitterlifetime'] = em:getEmitterLifetime() 40 | local pttlmin,pttlmax = em:getParticleLifetime() 41 | particle['particlelifetime'] = { pttlmin=pttlmin,pttlmax=pttlmax } 42 | local elaxmin,elaymin,elaxmax,elaymax = em:getLinearAcceleration() 43 | particle['linearacceleration'] = { elaxmin=elaxmin,elaymin=elaymin,elaxmax=elaxmax,elaymax=elaymax } 44 | local praccmin,praccmax = em:getRadialAcceleration() 45 | particle['radialacceleration'] = { praccmin=praccmin,praccmax=praccmax } 46 | local protmin,protmax = em:getRotation() 47 | particle['rotation'] = { protmin=protmin,protmax=protmax } 48 | local ptgamin,ptgamax = em:getTangentialAcceleration() 49 | particle['tangentialacceleration'] = { ptgamin=ptgamin,ptgamax=ptgamax } 50 | local pspdmin,pspdmax = em:getSpeed() 51 | particle['speed'] = { pspdmin=pspdmin,pspdmax=pspdmax } 52 | local pspinmin,pspinmax = em:getSpin() 53 | particle['spin'] = { pspinmin=pspinmin,pspinmax=pspinmax } 54 | particle['spinvariation'] = em:getSpinVariation() 55 | local pldmin,pldmax = em:getLinearDamping() 56 | particle['lineardamping'] = { pldmin=pldmin,pldmax=pldmax } 57 | particle['spread'] = em:getSpread() 58 | particle['relativerotation'] = em:hasRelativeRotation() 59 | local ox,oy = em:getOffset() 60 | particle['offset'] = { ox=ox,oy=oy } 61 | 62 | particle['sizesrange'] = spsrange.value 63 | particle['sizevariation'] = em:getSizeVariation() 64 | particle['sizes'] = {} 65 | for i=1,spsrange.value do 66 | particle['sizes'][i]=szspins[i].value 67 | end 68 | 69 | particle['colorsrange'] = spcrange.value 70 | particle['colors'] = {} 71 | for i=1,spcrange.value do 72 | if not particle['colors'][i] then particle['colors'][i] = {} end 73 | for j=1,4 do 74 | particle['colors'][i][j]=rbclgr[i].color[j] 75 | end 76 | end 77 | 78 | particle['quadsuse'] = gbtexman:getItem("GB_TM_Offset"):getItem("B_TM_Offset_CenterQuad").active 79 | 80 | particle['quads'] = {} 81 | if cbuseq.checked == true and #lbqlist.items>0 then 82 | local texw,texh = em:getTexture():getWidth(),em:getTexture():getHeight() 83 | for i=1,#lbqlist.items do 84 | if not particle['quads'][i] then particle['quads'][i] = {} end 85 | particle['quads'][i]={ lbqlist.items[i],texw, texh } 86 | end 87 | end 88 | 89 | local lbtex = gbtexman:getItem("LB_TextureList") 90 | particle['image'] = lbtex:getSelected() 91 | particle['blendmode'] = lbbmode:getSelected() 92 | particle['insertmode'] = em:getInsertMode() 93 | 94 | local dump = ndump( {particle=particle} ) 95 | 96 | local filename = "par_" .. string.format( "%s.txt", os.date("%m-%d_%H-%M-%S", os.time()) ) 97 | 98 | if not love.filesystem.exists('saves/') then love.filesystem.createDirectory('/saves/') end 99 | love.filesystem.write('saves/' .. filename, "local " .. dump .. "\nreturn particle\n" ) 100 | end 101 | 102 | local function loadparticle( filename, uim, em, page ) 103 | if not love.filesystem.exists('saves/' .. filename) then return end 104 | local content = love.filesystem.read('saves/' .. filename ) 105 | local particle = loadstring(content)() 106 | 107 | em:setBufferSize( particle['buffersize'] ) 108 | page:getItem("SP_BufferSize").value = particle['buffersize'] 109 | 110 | em:setDirection( particle['direction'] ) 111 | page:getItem("SP_Direction").value = particle['direction'] 112 | 113 | em:setAreaSpread( particle["areaspread"]["as"], particle["areaspread"]["asx"], particle["areaspread"]["asy"]) 114 | local gb_distr = page:getItem("GB_AreaDistribution") 115 | local rbdn = gb_distr:getItem("RB_Distr_None") 116 | local rbdnorm = gb_distr:getItem("RB_Distr_Normal") 117 | local rbdu = gb_distr:getItem("RB_Distr_Uniform") 118 | print(rbdn.checked,rbdnorm.checked,rbdu.checked) 119 | gb_distr:getItem("SP_Distr_X"):hide() 120 | gb_distr:getItem("SP_Distr_Y"):hide() 121 | if particle["areaspread"]["as"] == 'none' then 122 | rbdn.checked = true 123 | rbdnorm.checked = false 124 | rbdu.checked = false 125 | elseif particle["areaspread"]["as"] == 'normal' then 126 | rbdn.checked = false 127 | rbdnorm.checked = true 128 | rbdu.checked = false 129 | gb_distr:getItem("SP_Distr_X"):show() 130 | gb_distr:getItem("SP_Distr_Y"):show() 131 | elseif particle["areaspread"]["as"] == 'uniform' then 132 | rbdn.checked = false 133 | rbdnorm.checked = false 134 | rbdu.checked = true 135 | gb_distr:getItem("SP_Distr_X"):show() 136 | gb_distr:getItem("SP_Distr_Y"):show() 137 | else 138 | rbdn.checked = true 139 | rbdnorm.checked = false 140 | rbdu.checked = false 141 | end 142 | gb_distr:getItem("SP_Distr_X").value = tonumber(particle["areaspread"]["asx"]) 143 | gb_distr:getItem("SP_Distr_Y").value = tonumber(particle["areaspread"]["asy"]) 144 | 145 | em:setEmissionRate( particle['emissionrate'] ) 146 | page:getItem("SP_ERate").value = particle['emissionrate'] 147 | 148 | em:setEmitterLifetime( particle['emitterlifetime'] ) 149 | page:getItem("SP_EmLifetime").value = particle['emitterlifetime'] 150 | 151 | em:setParticleLifetime( particle['particlelifetime'].pttlmin, particle['particlelifetime'].pttlmax ) 152 | local gbplt = page:getItem("GB_PLifetime") 153 | gbplt:getItem("SP_PLifetime_Min").value = particle['particlelifetime'].pttlmin 154 | gbplt:getItem("SP_PLifetime_Max").value = particle['particlelifetime'].pttlmax 155 | 156 | em:setParticleLifetime( particle['particlelifetime'].pttlmin, particle['particlelifetime'].pttlmax ) 157 | local gb_lacc = page:getItem("GB_LinearAcceleration") 158 | gb_lacc:getItem("SP_LA_XMin").value = particle["linearacceleration"]["elaxmin"] 159 | gb_lacc:getItem("SP_LA_YMin").value = particle["linearacceleration"]["elaymin"] 160 | gb_lacc:getItem("SP_LA_XMax").value = particle["linearacceleration"]["elaxmax"] 161 | gb_lacc:getItem("SP_LA_YMax").value = particle["linearacceleration"]["elaymax"] 162 | 163 | em:setRadialAcceleration( particle["radialacceleration"]["praccmin"], particle["radialacceleration"]["praccmax"] ) 164 | local gbracc = page:getItem("GB_RadialAcceleration") 165 | gbracc:getItem("SP_RadialAcc_Min").value = particle["radialacceleration"]["praccmin"] 166 | gbracc:getItem("SP_RadialAcc_Max").value = particle["radialacceleration"]["praccmax"] 167 | 168 | em:setRotation( particle["rotation"]["protmin"], particle["rotation"]["protmax"] ) 169 | local gbrot = page:getItem("GB_Rotation") 170 | gbrot:getItem("SP_Rotation_Min").value = particle["rotation"]["protmin"] 171 | gbrot:getItem("SP_Rotation_Max").value = particle["rotation"]["protmax"] 172 | 173 | em:setTangentialAcceleration( particle["tangentialacceleration"]["ptgamin"], particle["tangentialacceleration"]["ptgamax"] ) 174 | local gbtga = page:getItem("GB_TangentialAcc") 175 | gbtga:getItem("SP_TgAcc_Min").value = particle["tangentialacceleration"]["ptgamin"] 176 | gbtga:getItem("SP_TgAcc_Max").value = particle["tangentialacceleration"]["ptgamax"] 177 | 178 | em:setSpeed( particle["speed"]["pspdmin"], particle["speed"]["pspdmax"] ) 179 | local gbspd = page:getItem("GB_Speed") 180 | gbspd:getItem("SP_Speed_Min").value = particle["speed"]["pspdmin"] 181 | gbspd:getItem("SP_Speed_Max").value = particle["speed"]["pspdmax"] 182 | 183 | em:setSpin( particle["spin"]["pspinmin"], particle["spin"]["pspinmax"] ) 184 | local gbspin = page:getItem("GB_Spin") 185 | gbspin:getItem("SP_Spin_Min").value = particle["spin"]["pspinmin"] 186 | gbspin:getItem("SP_Spin_Max").value = particle["spin"]["pspinmax"] 187 | 188 | em:setSpinVariation( particle["spinvariation"] ) 189 | page:getItem("SP_Spin_Variation").value = particle["spinvariation"] 190 | 191 | em:setLinearDamping( particle["lineardamping"]["pldmin"], particle["lineardamping"]["pldmax"] ) 192 | local gbldamp = page:getItem("GB_LinearDamping") 193 | gbldamp:getItem("SP_LD_Min").value = particle["lineardamping"]["pldmin"] 194 | gbldamp:getItem("SP_LD_Max").value = particle["lineardamping"]["pldmax"] 195 | 196 | em:setSpread( particle["spread"] ) 197 | page:getItem("SP_Spread").value = particle["spread"] 198 | 199 | em:setRelativeRotation( enable ) 200 | page:getItem("CB_RelativeRotation").checked = particle["relativerotation"] 201 | 202 | -- Offset 203 | em:setOffset( particle["offset"]["ox"], particle["offset"]["oy"] ) 204 | local gbtexman = page:getItem("GB_Tex_Manager") 205 | local gbtmoff = gbtexman:getItem("GB_TM_Offset") 206 | gbtmoff:getItem("SP_TM_OffsetX").value = particle["offset"]["ox"] 207 | gbtmoff:getItem("SP_TM_OffsetY").value = particle["offset"]["oy"] 208 | 209 | -- Sizes 210 | local gbsizes = page:getItem("GB_Size_Selector") 211 | gbsizes:getItem("SP_Size_Range").value = particle['sizesrange'] 212 | em:setSizeVariation( particle["sizevariation"] ) 213 | gbsizes:getItem("SP_Size_Var").value = particle["sizevariation"] 214 | local sizes = {} 215 | for i=1,8 do 216 | local spsz = gbsizes:getItem("SP_Size_" .. tostring(i)) 217 | if i <= particle['sizesrange'] then 218 | spsz.value = particle["sizes"][tostring(i)] 219 | spsz.active = true 220 | sizes[i] = particle["sizes"][tostring(i)] 221 | else 222 | spsz.value = 1.0 223 | spsz.active = false 224 | end 225 | end 226 | em:setSizes( unpack(sizes) ) 227 | 228 | -- Colors 229 | local gbcolors = page:getItem("GB_Color_Selector") 230 | gbcolors:getItem("SP_Color_Range").value = particle["colorsrange"] 231 | local clapp = {} 232 | for i=1,8 do 233 | local rbcl = gbcolors:getItem("RB_Color" .. tostring(i)) 234 | if i == 1 then rbcl.checked = true end 235 | if i <= particle["colorsrange"] then 236 | local cl = {} 237 | for j=1,4 do 238 | --print(j, particle["colors"][tostring(i)][tostring(j)]) 239 | rbcl.group[i].color[j] = particle["colors"][tostring(i)][tostring(j)] 240 | cl[j] = rbcl.group[i].color[j] 241 | end 242 | clapp[i] = cl 243 | 244 | rbcl.active = true 245 | else 246 | for j=1,4 do 247 | rbcl.group[i].color[j] = 255 248 | end 249 | rbcl.active = false 250 | end 251 | end 252 | em:setColors(unpack(clapp)) 253 | 254 | -- Texture 255 | local gbtexman = page:getItem("GB_Tex_Manager") 256 | local lbtex = gbtexman:getItem("LB_TextureList") 257 | for i,v in ipairs(lbtex.items) do 258 | if v == particle["image"] then 259 | lbtex.index = i 260 | break 261 | end 262 | end 263 | local tex = uim:getItem("IC_Textures"):getItem(lbtex:getSelected()) 264 | em:setTexture( tex ) 265 | page:getItem("GB_Tex_Manager"):getItem("IM_TM_Texture"):setImage(uim:getItem("IC_Textures"):getItem(lbtex:getSelected())) 266 | page:getItem("GB_Tex_Manager"):getItem("L_TextureCaption").caption = "Texture: "..lbtex:getSelected()..":"..tex:getWidth().."x"..tex:getHeight() 267 | 268 | -- Quads 269 | gbtexman:getItem("CB_TM_UseQuads").checked = particle['quadsuse'] 270 | if particle['quadsuse'] then 271 | gbtexman:getItem("GB_QuadControl"):show() 272 | else 273 | gbtexman:getItem("GB_QuadControl"):hide() 274 | end 275 | local gbqctrl = gbtexman:getItem("GB_QuadControl") 276 | local lbqlist = gbqctrl:getItem("LB_TM_QuadList") 277 | lbqlist:clear() 278 | local c_quads = page:getItem("C_Quads") 279 | c_quads:purge() 280 | for i,quadstring in pairsByKeys(particle["quads"]) do 281 | local stringvalues = split(quadstring["1"], ',') 282 | local values = {} 283 | for j,zahl in pairs(stringvalues) do 284 | values[j] = zahl 285 | end 286 | local qx = values[1] 287 | local qy = values[2] 288 | local qw = values[3] 289 | local qh = values[4] 290 | local texwidth = quadstring["2"] 291 | local textheight = quadstring["3"] 292 | lbqlist:addItem(qx..","..qy..","..qw..","..qh) 293 | gbqctrl:getItem("LB_TM_QuadList"):last() 294 | page:getItem("C_Quads"):addItem( love.graphics.newQuad(qx,qy,qw,qh,texwidth,textheight) ) 295 | print('Quadcount:', #c_quads.items) 296 | end 297 | if particle['quadsuse'] then 298 | gbqctrl:getItem("SP_QViewport_W").value = particle["quads"]["1"]["2"] 299 | gbqctrl:getItem("SP_QViewport_H").value = particle["quads"]["1"]["3"] 300 | else 301 | gbqctrl:getItem("SP_QViewport_W").value = tex:getWidth() 302 | gbqctrl:getItem("SP_QViewport_H").value = tex:getHeight() 303 | end 304 | em:setQuads(unpack(c_quads.items)) 305 | 306 | -- update Texture Frame 307 | local imgcross = gbtexman:getItem("CR_Cross") 308 | local sox,soy,im = gbtmoff:getItem("SP_TM_OffsetX"),gbtmoff:getItem("SP_TM_OffsetY"),gbtexman:getItem("IM_TM_Texture") 309 | imgcross:setPosition(im.x+sox.value,im.y+soy.value) 310 | 311 | local quadrect = gbtexman:getItem("R_QuadRect") 312 | if particle['quadsuse'] then 313 | local qx,qy,qw,qh = c_quads:getItem(1):getViewport() 314 | quadrect:setPosition(im.x+qx, im.y+qy) 315 | quadrect:setSize(qw,qh) 316 | else 317 | quadrect:setPosition(im.x, im.y) 318 | quadrect:setSize(tex:getWidth(),tex:getHeight()) 319 | end 320 | 321 | -- insertMode 322 | em:setInsertMode( particle['insertmode'] ) 323 | local gbimode = page:getItem("GB_InsertMode") 324 | local rbimtop = gbimode:getItem("RB_IM_Top") 325 | local rbimbot = gbimode:getItem("RB_IM_Bottom") 326 | local rbimrnd = gbimode:getItem("RB_IM_Random") 327 | rbimtop.checked = false 328 | rbimbot.checked = false 329 | rbimrnd.checked = false 330 | if particle['insertmode'] == 'top' then 331 | rbimtop.checked = true 332 | elseif particle['insertmode'] == 'bottom' then 333 | rbimbot.checked = true 334 | elseif particle['insertmode'] == 'random' then 335 | rbimrnd.checked = true 336 | end 337 | 338 | -- blendmode 339 | local gbmisc = page:getItem("GB_Misc") 340 | local lbbmode = gbmisc:getItem("LB_BlendMode") 341 | -- blendmode select 342 | page:getItem("ParticleEmitter").mode = particle["blendmode"] 343 | for i,v in ipairs(lbbmode.items) do 344 | if v == particle["blendmode"] then 345 | lbbmode.index = i 346 | break 347 | end 348 | end 349 | 350 | page:getItem("ParticleEmitter").ps:start() 351 | end 352 | 353 | function love.load() 354 | love.window.setTitle("APE for LÖVE2D by cval & SiENcE") 355 | l_gfx.setFont(l_gfx.newFont(12)) 356 | uim = UIManager:new() 357 | UIElement.colorFill = {48,48,48,255} 358 | UIElement.colorHardFill = {80,80,80,255} 359 | UIElement.colorDisabledFill = {16,16,16,255} 360 | UIElement.colorHighlight = {96,96,96,255} 361 | local deftexID = love.image.newImageData(1,1) 362 | deftexID:setPixel(0,0,255,255,255) 363 | local ic = uim:addItem(ImageCollection:new("IC_Textures")) 364 | ic:addItem(deftexID,"default") 365 | 366 | local icf = uim:addItem(Collection:new("IC_Files")) 367 | 368 | local pgs = uim:addItem(PageSwitch:new("Scene")) 369 | local pgsc = uim:addItem(PageSwitchController:new("PSController")) 370 | pgsc:setPageSwitch(pgs) 371 | pgsc:setPosition(0,love.graphics.getHeight()-32) 372 | local pgadd = pgsc:getItem("ButtonAdd") 373 | function pgadd:click(b) 374 | if b == 1 then 375 | local pg = pgs:addPage() 376 | pgsc.caption = pgsc.pageswitch.index.."/"..#pgsc.pageswitch.pages 377 | fillPage(pg) 378 | end 379 | end 380 | pgadd:click(1) 381 | end 382 | 383 | function love.update(dt) 384 | uim:update(dt) 385 | end 386 | 387 | function love.draw() 388 | uim:draw() 389 | end 390 | 391 | function love.mousemoved(x,y) 392 | uim:mousemoved(x,y) 393 | end 394 | 395 | function love.mousepressed(x,y,b) 396 | uim:mousepressed(x,y,b) 397 | end 398 | 399 | function love.wheelmoved(x,y) 400 | uim:wheelmoved(x,y) 401 | end 402 | 403 | function love.mousereleased(x,y,b) 404 | uim:mousereleased(x,y,b) 405 | end 406 | 407 | function love.keypressed(key,isrepeat) 408 | uim:keypressed(key,isrepeat) 409 | 410 | -- if key == 'f5' then 411 | -- saveparticle() 412 | -- end 413 | -- if key == 'f9' then 414 | -- loadparticle() 415 | -- end 416 | if key == 'f11' then 417 | openSavefolder() 418 | end 419 | end 420 | 421 | function love.keyreleased(key) 422 | uim:keyreleased(key) 423 | end 424 | 425 | function fillPage(page) 426 | page:setSize(love.graphics:getWidth(),love.graphics:getHeight()) 427 | local vertline = page:addItem(Line:new("L_Vertical")) 428 | vertline.h = love.graphics.getHeight()-4 429 | vertline.x = 240 430 | vertline.y = 2 431 | 432 | 433 | local tex = uim:getItem("IC_Textures"):getItem("default") 434 | local emitter = page:addItem(ParticleEmitter:new("ParticleEmitter",tex)) 435 | emitter.ps:moveTo(love.graphics.getWidth()/2+224,love.graphics.getHeight()/2) 436 | emitter.ps:setQuads() 437 | 438 | local guidetimer = page:addItem(Timer:new("T_GuideShow")) 439 | guidetimer.interval = 3 440 | function guidetimer:trigger() 441 | page:getItem("Arc_ESpread"):hide() 442 | page:getItem("R_EArea"):hide() 443 | end 444 | 445 | local emspr = page:addItem(Arc:new("Arc_ESpread")) 446 | emspr.colorFill = {255,255,0,255} 447 | emspr.mode = "line" 448 | emspr.radius = 128 449 | emspr:setAngle(-0.01,0.01) 450 | emspr:setPosition(love.graphics.getWidth()/2+224,love.graphics.getHeight()/2) 451 | emspr:hide() 452 | 453 | local emarea = page:addItem(Rectangle:new("R_EArea")) 454 | emarea.colorFill = {255,0,0,255} 455 | emarea.mode = "line" 456 | emarea.w = 0 457 | emarea.h = 0 458 | emarea.centerOnPos = true 459 | emarea:setPosition(love.graphics.getWidth()/2+224,love.graphics.getHeight()/2) 460 | emarea:hide() 461 | 462 | local c_quads = page:addItem(Collection:new("C_Quads")) 463 | function c_quads:onadd() 464 | local pe = page:getItem("ParticleEmitter") 465 | pe.ps:setQuads(unpack(c_quads.items)) 466 | end 467 | function c_quads:ondelete() 468 | local pe = page:getItem("ParticleEmitter") 469 | pe.ps:setQuads(unpack(c_quads.items)) 470 | end 471 | 472 | c_quads:addItem(love.graphics.newQuad(0,0,1,1,1,1)) 473 | 474 | 475 | local bsspin = page:addItem(SpinEdit:new("SP_BufferSize")) 476 | bsspin.caption = "Buffer:" 477 | bsspin.leftCaption = true 478 | bsspin:setPosition(56,8) 479 | bsspin.max = nil 480 | bsspin.maxdec = 0 481 | bsspin.min = 1 482 | bsspin.value = 10 483 | bsspin.allowMult = true 484 | bsspin.mult_precise = 1 485 | function bsspin:changeValue() emitter.ps:setBufferSize(bsspin.value) end 486 | 487 | local spdir = page:addItem(Spin:new("SP_Direction")) 488 | spdir.caption = "Direction:" 489 | spdir.leftCaption = true 490 | spdir:setPosition(184,8) 491 | spdir.max = nil 492 | spdir.min = nil 493 | spdir.maxdec = 2 494 | spdir.value = 0 495 | spdir.step = 0.1 496 | spdir.allowMult = true 497 | function spdir:changeValue() 498 | emitter.ps:setDirection(spdir.value) 499 | local d,s = page:getItem("ParticleEmitter").ps:getDirection(),emitter.ps:getSpread() 500 | page:getItem("Arc_ESpread"):setAngle(-math.max((s/2),-0.15)+d,math.max((s/2),0.015)+d) 501 | page:getItem("Arc_ESpread"):show() 502 | page:getItem("R_EArea"):show() 503 | page:getItem("T_GuideShow"):start() 504 | end 505 | 506 | local gb_distr = page:addItem(GroupBox:new("GB_AreaDistribution")) 507 | gb_distr.caption = "Area distribution" 508 | gb_distr:setPosition(8,48) 509 | gb_distr.w = 224 510 | gb_distr.h = 56 511 | 512 | local rbdn = gb_distr:addItem(RadioButton:new("RB_Distr_None")) 513 | rbdn.caption = "none" 514 | rbdn.checked = true 515 | rbdn.buttonStyle = true 516 | rbdn:setSize(64,16) 517 | rbdn:setPosition(gb_distr.x+8,gb_distr.y+8) 518 | function rbdn:click(b) 519 | if b == 1 then 520 | gb_distr:getItem("SP_Distr_X"):hide() 521 | gb_distr:getItem("SP_Distr_Y"):hide() 522 | page:getItem("ParticleEmitter").ps:setAreaSpread(gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption,gb_distr:getItem("SP_Distr_X").value,gb_distr:getItem("SP_Distr_Y").value) 523 | local factor = 1 524 | if gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "normal" then factor = 2 525 | elseif gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "none" then factor = 0 end 526 | page:getItem("R_EArea"):setSize(gb_distr:getItem("SP_Distr_X").value*2*factor,gb_distr:getItem("SP_Distr_Y").value*2*factor) 527 | page:getItem("Arc_ESpread"):show() 528 | page:getItem("R_EArea"):show() 529 | page:getItem("T_GuideShow"):start() 530 | end 531 | end 532 | 533 | local rbdnorm = gb_distr:addItem(RadioButton:new("RB_Distr_Normal")) 534 | rbdnorm.caption = "normal" 535 | rbdnorm.buttonStyle = true 536 | rbdnorm:setSize(64,16) 537 | rbdnorm:setPosition(gb_distr.x+78,gb_distr.y+8) 538 | function rbdnorm:click(b) 539 | if b == 1 then 540 | gb_distr:getItem("SP_Distr_X"):show() 541 | gb_distr:getItem("SP_Distr_Y"):show() 542 | if gb_distr:getItem("SP_Distr_X").value <= 0 then gb_distr:getItem("SP_Distr_X").value = 1 end 543 | if gb_distr:getItem("SP_Distr_Y").value <= 0 then gb_distr:getItem("SP_Distr_Y").value = 1 end 544 | page:getItem("ParticleEmitter").ps:setAreaSpread(gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption,gb_distr:getItem("SP_Distr_X").value,gb_distr:getItem("SP_Distr_Y").value) 545 | local factor = 1 546 | if gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "normal" then factor = 2 547 | elseif gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "none" then factor = 0 end 548 | page:getItem("R_EArea"):setSize(gb_distr:getItem("SP_Distr_X").value*2*factor,gb_distr:getItem("SP_Distr_Y").value*2*factor) 549 | page:getItem("Arc_ESpread"):show() 550 | page:getItem("R_EArea"):show() 551 | page:getItem("T_GuideShow"):start() 552 | end 553 | end 554 | 555 | local rbdu = gb_distr:addItem(RadioButton:new("RB_Distr_Uniform")) 556 | rbdu.caption = "uniform" 557 | rbdu.buttonStyle = true 558 | rbdu:setSize(64,16) 559 | rbdu:setPosition(gb_distr.x+148,gb_distr.y+8) 560 | function rbdu:click(b) 561 | if b == 1 then 562 | gb_distr:getItem("SP_Distr_X"):show() 563 | gb_distr:getItem("SP_Distr_Y"):show() 564 | if gb_distr:getItem("SP_Distr_X").value <= 0 then gb_distr:getItem("SP_Distr_X").value = 1 end 565 | if gb_distr:getItem("SP_Distr_Y").value <= 0 then gb_distr:getItem("SP_Distr_Y").value = 1 end 566 | page:getItem("ParticleEmitter").ps:setAreaSpread(gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption,gb_distr:getItem("SP_Distr_X").value,gb_distr:getItem("SP_Distr_Y").value) 567 | local factor = 1 568 | if gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "normal" then factor = 2 569 | elseif gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "none" then factor = 0 end 570 | page:getItem("R_EArea"):setSize(gb_distr:getItem("SP_Distr_X").value*2*factor,gb_distr:getItem("SP_Distr_Y").value*2*factor) 571 | page:getItem("Arc_ESpread"):show() 572 | page:getItem("R_EArea"):show() 573 | page:getItem("T_GuideShow"):start() 574 | end 575 | end 576 | 577 | local rbdg = {rbdn,rbdnorm,rbdu} 578 | for i=1,3 do rbdg[i]:setGroup(rbdg) end 579 | 580 | local lar = gb_distr:addItem(Label:new("L_Area")) 581 | lar.caption = "Area:" 582 | lar:setPosition(gb_distr.x+8,gb_distr.y+32) 583 | 584 | local spdistrx = gb_distr:addItem(Spin:new("SP_Distr_X")) 585 | spdistrx:setPosition(gb_distr.x+56,gb_distr.y+32) 586 | spdistrx.min = 1 587 | spdistrx.value = 1 588 | spdistrx.maxdec = 0 589 | spdistrx.max = nil 590 | spdistrx.leftCaption = true 591 | spdistrx.caption = "X" 592 | spdistrx.allowMult = true 593 | spdistrx.active = false 594 | spdistrx:hide() 595 | function spdistrx:changeValue() 596 | page:getItem("ParticleEmitter").ps:setAreaSpread(gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption,gb_distr:getItem("SP_Distr_X").value,gb_distr:getItem("SP_Distr_Y").value) 597 | local factor = 1 598 | if gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "normal" then factor = 2 599 | elseif gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "none" then factor = 0 end 600 | page:getItem("R_EArea"):setSize(gb_distr:getItem("SP_Distr_X").value*2*factor,gb_distr:getItem("SP_Distr_Y").value*2*factor) 601 | page:getItem("Arc_ESpread"):show() 602 | page:getItem("R_EArea"):show() 603 | page:getItem("T_GuideShow"):start() 604 | end 605 | 606 | local spdistry = gb_distr:addItem(Spin:new("SP_Distr_Y")) 607 | spdistry:setPosition(gb_distr.x+148,gb_distr.y+32) 608 | spdistry.min = 1 609 | spdistry.value = 1 610 | spdistry.maxdec = 0 611 | spdistry.max = nil 612 | spdistry.leftCaption = true 613 | spdistry.caption = "Y" 614 | spdistry.allowMult = true 615 | spdistry:hide() 616 | spdistry.visible = false 617 | function spdistry:changeValue() 618 | page:getItem("ParticleEmitter").ps:setAreaSpread(gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption,gb_distr:getItem("SP_Distr_X").value,gb_distr:getItem("SP_Distr_Y").value) 619 | local factor = 1 620 | if gb_distr:getItem("RB_Distr_None").group[rbdn:getGroupIndex()].caption == "normal" then factor = 2 end 621 | page:getItem("R_EArea"):setSize(gb_distr:getItem("SP_Distr_X").value*2*factor,gb_distr:getItem("SP_Distr_Y").value*2*factor) 622 | page:getItem("Arc_ESpread"):show() 623 | page:getItem("R_EArea"):show() 624 | page:getItem("T_GuideShow"):start() 625 | end 626 | 627 | local btstart = page:addItem(Button:new("B_EmitterStart")) 628 | btstart:setPosition(168,112) 629 | btstart.caption = "START" 630 | btstart:setSize(64,36) 631 | function btstart:click(b) if b == 1 then page:getItem("ParticleEmitter").ps:start() end end 632 | 633 | local sperate = page:addItem(Spin:new("SP_ERate")) 634 | sperate:setPosition(116,112) 635 | sperate.maxdec = 1 636 | sperate.leftCaption = true 637 | sperate.caption = "Emission rate:" 638 | sperate.min = 0 639 | sperate.value = 1 640 | sperate.max = nil 641 | sperate.allowMult = true 642 | sperate.caption_xpad = -13 643 | function sperate:changeValue() page:getItem("ParticleEmitter").ps:setEmissionRate(sperate.value) end 644 | 645 | local spemlt = page:addItem(Spin:new("SP_EmLifetime")) 646 | spemlt:setPosition(116,132) 647 | spemlt.maxdec = 1 648 | spemlt.leftCaption = true 649 | spemlt.caption = "Emitter lifetime:" 650 | spemlt.min = -1 651 | spemlt.value = -1 652 | spemlt.max = nil 653 | spemlt.step = 0.1 654 | spemlt.allowMult = true 655 | function spemlt:changeValue() if spemlt.value<-1 then spemlt.value = -1 end page:getItem("ParticleEmitter").ps:setEmitterLifetime(spemlt.value) page:getItem("ParticleEmitter").ps:start() end 656 | 657 | 658 | 659 | local gb_lacc = page:addItem(GroupBox:new("GB_LinearAcceleration")) 660 | gb_lacc.caption = "Linear acceleration" 661 | gb_lacc:setPosition(8,172) 662 | gb_lacc.w = 224 663 | gb_lacc.h = 42 664 | 665 | local splaxmin = gb_lacc:addItem(Spin:new("SP_LA_XMin")) 666 | splaxmin:setPosition(gb_lacc.x+40,gb_lacc.y+4) 667 | splaxmin.min = nil 668 | splaxmin.max = nil 669 | splaxmin.allowMult = true 670 | splaxmin.leftCaption = true 671 | splaxmin.caption = "XMin" 672 | function splaxmin:changeValue() 673 | local em = page:getItem("ParticleEmitter").ps 674 | local laxmin = gb_lacc:getItem("SP_LA_XMin").value 675 | local laymin = gb_lacc:getItem("SP_LA_YMin").value 676 | local laxmax = gb_lacc:getItem("SP_LA_XMax").value 677 | local laymax = gb_lacc:getItem("SP_LA_YMax").value 678 | em:setLinearAcceleration(laxmin,laymin,laxmax,laymax) 679 | end 680 | 681 | local splaymin = gb_lacc:addItem(Spin:new("SP_LA_YMin")) 682 | splaymin:setPosition(gb_lacc.x+40,gb_lacc.y+22) 683 | splaymin.min = nil 684 | splaymin.max = nil 685 | splaymin.allowMult = true 686 | splaymin.leftCaption = true 687 | splaymin.caption = "YMin" 688 | function splaymin:changeValue() 689 | local em = page:getItem("ParticleEmitter").ps 690 | local laxmin = gb_lacc:getItem("SP_LA_XMin").value 691 | local laymin = gb_lacc:getItem("SP_LA_YMin").value 692 | local laxmax = gb_lacc:getItem("SP_LA_XMax").value 693 | local laymax = gb_lacc:getItem("SP_LA_YMax").value 694 | em:setLinearAcceleration(laxmin,laymin,laxmax,laymax) 695 | end 696 | 697 | local splaxmax = gb_lacc:addItem(Spin:new("SP_LA_XMax")) 698 | splaxmax:setPosition(gb_lacc.x+154,gb_lacc.y+4) 699 | splaxmax.min = nil 700 | splaxmax.max = nil 701 | splaxmax.allowMult = true 702 | splaxmax.leftCaption = true 703 | splaxmax.caption = "XMax" 704 | function splaxmax:changeValue() 705 | local em = page:getItem("ParticleEmitter").ps 706 | local laxmin = gb_lacc:getItem("SP_LA_XMin").value 707 | local laymin = gb_lacc:getItem("SP_LA_YMin").value 708 | local laxmax = gb_lacc:getItem("SP_LA_XMax").value 709 | local laymax = gb_lacc:getItem("SP_LA_YMax").value 710 | em:setLinearAcceleration(laxmin,laymin,laxmax,laymax) 711 | end 712 | 713 | local splaymax = gb_lacc:addItem(Spin:new("SP_LA_YMax")) 714 | splaymax:setPosition(gb_lacc.x+154,gb_lacc.y+22) 715 | splaymax.min = nil 716 | splaymax.max = nil 717 | splaymax.allowMult = true 718 | splaymax.leftCaption = true 719 | splaymax.caption = "YMax" 720 | function splaymax:changeValue() 721 | local em = page:getItem("ParticleEmitter").ps 722 | local laxmin = gb_lacc:getItem("SP_LA_XMin").value 723 | local laymin = gb_lacc:getItem("SP_LA_YMin").value 724 | local laxmax = gb_lacc:getItem("SP_LA_XMax").value 725 | local laymax = gb_lacc:getItem("SP_LA_YMax").value 726 | em:setLinearAcceleration(laxmin,laymin,laxmax,laymax) 727 | end 728 | 729 | local emtfill = page:addItem(ProgressBar:new("PB_EmitterFill")) 730 | emtfill:setPosition(8,236) 731 | emtfill.showCaption = true 732 | emtfill.caption = "Emitter fill" 733 | emtfill:setSize(160,16) 734 | emtfill.value = 32 735 | function emtfill:update(dt) emtfill.max = emitter.ps:getBufferSize() emtfill.value = emitter.ps:getCount() end 736 | 737 | local lfps = page:addItem(RefreshingLabel:new("RL_FPS")) 738 | local ltim = love.timer 739 | function lfps:update(dt) lfps.caption = "FPS:"..ltim.getFPS() end 740 | lfps:setPosition(180,236) 741 | lfps.wrap = false 742 | 743 | local gbplt = page:addItem(GroupBox:new("GB_PLifetime")) 744 | gbplt:setPosition(8,272) 745 | gbplt.caption = "Part. lifetime" 746 | gbplt:setSize(84,42) 747 | 748 | local sppltmin = gbplt:addItem(Spin:new("SP_PLifetime_Min")) 749 | sppltmin:setPosition(40,276) 750 | sppltmin.allowMult = true 751 | sppltmin.caption = "min" 752 | sppltmin.leftCaption = true 753 | function sppltmin:changeValue() 754 | page:getItem("ParticleEmitter").ps:setParticleLifetime(gbplt:getItem("SP_PLifetime_Min").value,gbplt:getItem("SP_PLifetime_Max").value) 755 | end 756 | 757 | local sppltmax = gbplt:addItem(Spin:new("SP_PLifetime_Max")) 758 | sppltmax:setPosition(40,294) 759 | sppltmax.allowMult = true 760 | sppltmax.leftCaption = true 761 | sppltmax.caption = "max" 762 | function sppltmax:changeValue() 763 | page:getItem("ParticleEmitter").ps:setParticleLifetime(gbplt:getItem("SP_PLifetime_Min").value,gbplt:getItem("SP_PLifetime_Max").value) 764 | end 765 | 766 | local gbracc = page:addItem(GroupBox:new("GB_RadialAcceleration")) 767 | gbracc:setPosition(146,272) 768 | gbracc.caption = "Radial accel." 769 | gbracc:setSize(84,42) 770 | 771 | local spraccmin = gbracc:addItem(Spin:new("SP_RadialAcc_Min")) 772 | spraccmin:setPosition(178,276) 773 | spraccmin.allowMult = true 774 | spraccmin.caption = "min" 775 | spraccmin.leftCaption = true 776 | function spraccmin:changeValue() 777 | page:getItem("ParticleEmitter").ps:setRadialAcceleration(gbracc:getItem("SP_RadialAcc_Min").value,gbracc:getItem("SP_RadialAcc_Max").value) 778 | end 779 | 780 | local spraccmax = gbracc:addItem(Spin:new("SP_RadialAcc_Max")) 781 | spraccmax:setPosition(178,294) 782 | spraccmax.allowMult = true 783 | spraccmax.leftCaption = true 784 | spraccmax.caption = "max" 785 | function spraccmax:changeValue() 786 | page:getItem("ParticleEmitter").ps:setRadialAcceleration(gbracc:getItem("SP_RadialAcc_Min").value,gbracc:getItem("SP_RadialAcc_Max").value) 787 | end 788 | 789 | 790 | local gbrot = page:addItem(GroupBox:new("GB_Rotation")) 791 | gbrot.caption = "Part. rotation" 792 | gbrot:setSize(84,42) 793 | 794 | local sprotmin = gbrot:addItem(Spin:new("SP_Rotation_Min")) 795 | sprotmin:setPosition(32,4) 796 | sprotmin.allowMult = true 797 | sprotmin.caption = "min" 798 | sprotmin.step = 0.1 799 | sprotmin.maxdec = 2 800 | sprotmin.leftCaption = true 801 | function sprotmin:changeValue() 802 | page:getItem("ParticleEmitter").ps:setRotation(gbrot:getItem("SP_Rotation_Min").value,gbrot:getItem("SP_Rotation_Max").value) 803 | end 804 | 805 | local sprotmax = gbrot:addItem(Spin:new("SP_Rotation_Max")) 806 | sprotmax:setPosition(32,22) 807 | sprotmax.allowMult = true 808 | sprotmax.step = 0.1 809 | sprotmax.maxdec = 2 810 | sprotmax.leftCaption = true 811 | sprotmax.caption = "max" 812 | function sprotmax:changeValue() 813 | page:getItem("ParticleEmitter").ps:setRotation(gbrot:getItem("SP_Rotation_Min").value,gbrot:getItem("SP_Rotation_Max").value) 814 | end 815 | gbrot:setPosition(8,334) 816 | 817 | 818 | local gbspin = page:addItem(GroupBox:new("GB_Spin")) 819 | gbspin.caption = "Part. spin" 820 | gbspin:setSize(84,42) 821 | 822 | local spspmin = gbspin:addItem(Spin:new("SP_Spin_Min")) 823 | spspmin:setPosition(32,4) 824 | spspmin.allowMult = true 825 | spspmin.caption = "min" 826 | spspmin.leftCaption = true 827 | function spspmin:changeValue() 828 | page:getItem("ParticleEmitter").ps:setSpin(gbspin:getItem("SP_Spin_Min").value,gbspin:getItem("SP_Spin_Max").value) 829 | end 830 | 831 | local spspmax = gbspin:addItem(Spin:new("SP_Spin_Max")) 832 | spspmax:setPosition(32,22) 833 | spspmax.allowMult = true 834 | spspmax.leftCaption = true 835 | spspmax.caption = "max" 836 | function spspmax:changeValue() 837 | page:getItem("ParticleEmitter").ps:setSpin(gbspin:getItem("SP_Spin_Min").value,gbspin:getItem("SP_Spin_Max").value) 838 | end 839 | gbspin:setPosition(146,334) 840 | 841 | 842 | local gbspd = page:addItem(GroupBox:new("GB_Speed")) 843 | gbspd.caption = "Part. speed" 844 | gbspd:setSize(84,42) 845 | 846 | local spspdmin = gbspd:addItem(Spin:new("SP_Speed_Min")) 847 | spspdmin:setPosition(32,4) 848 | spspdmin.allowMult = true 849 | spspdmin.caption = "min" 850 | spspdmin.leftCaption = true 851 | function spspdmin:changeValue() 852 | page:getItem("ParticleEmitter").ps:setSpeed(gbspd:getItem("SP_Speed_Min").value,gbspd:getItem("SP_Speed_Max").value) 853 | end 854 | 855 | local spspdmax = gbspd:addItem(Spin:new("SP_Speed_Max")) 856 | spspdmax:setPosition(32,22) 857 | spspdmax.allowMult = true 858 | spspdmax.leftCaption = true 859 | spspdmax.caption = "max" 860 | function spspdmax:changeValue() 861 | page:getItem("ParticleEmitter").ps:setSpeed(gbspd:getItem("SP_Speed_Min").value,gbspd:getItem("SP_Speed_Max").value) 862 | end 863 | gbspd:setPosition(8,396) 864 | 865 | 866 | local gbtga = page:addItem(GroupBox:new("GB_TangentialAcc")) 867 | gbtga.caption = "Tg accel." 868 | gbtga:setSize(84,42) 869 | 870 | local sptgamin = gbtga:addItem(Spin:new("SP_TgAcc_Min")) 871 | sptgamin:setPosition(32,4) 872 | sptgamin.allowMult = true 873 | sptgamin.caption = "min" 874 | sptgamin.leftCaption = true 875 | function sptgamin:changeValue() 876 | page:getItem("ParticleEmitter").ps:setTangentialAcceleration(gbtga:getItem("SP_TgAcc_Min").value,gbtga:getItem("SP_TgAcc_Max").value) 877 | end 878 | 879 | local sptgamax = gbtga:addItem(Spin:new("SP_TgAcc_Max")) 880 | sptgamax:setPosition(32,22) 881 | sptgamax.allowMult = true 882 | sptgamax.leftCaption = true 883 | sptgamax.caption = "max" 884 | function sptgamax:changeValue() 885 | page:getItem("ParticleEmitter").ps:setTangentialAcceleration(gbtga:getItem("SP_TgAcc_Min").value,gbtga:getItem("SP_TgAcc_Max").value) 886 | end 887 | gbtga:setPosition(146,396) 888 | 889 | local linediv = page:addItem(Line:new("L_Divider")) 890 | linediv:setPosition(120,272) 891 | linediv:setSize(1,168) 892 | 893 | 894 | local spspvar = page:addItem(Spin:new("SP_Spin_Variation")) 895 | spspvar:setPosition(56,444) 896 | spspvar.caption = "Spin v." 897 | spspvar.step = 0.1 898 | spspvar.maxdec = 2 899 | spspvar.min = 0 900 | spspvar.max = 1 901 | spspvar.allowMult = true 902 | spspvar.leftCaption = true 903 | function spspvar:changeValue() page:getItem("ParticleEmitter").ps:setSpinVariation(page:getItem("SP_Spin_Variation").value) end 904 | 905 | local spspread = page:addItem(Spin:new("SP_Spread")) 906 | spspread:setPosition(180,444) 907 | spspread.caption = "Spread" 908 | spspread.step = 0.1 909 | spspread.maxdec = 2 910 | spspread.min = 0 911 | spspread.max = 6.28 912 | spspread.allowMult = true 913 | spspread.leftCaption = true 914 | function spspread:changeValue() 915 | page:getItem("ParticleEmitter").ps:setSpread(page:getItem("SP_Spread").value) 916 | local d,s = page:getItem("ParticleEmitter").ps:getDirection(),emitter.ps:getSpread() 917 | page:getItem("Arc_ESpread"):setAngle(-math.max((s/2),-0.15)+d,math.max((s/2),0.015)+d) 918 | page:getItem("Arc_ESpread"):show() 919 | page:getItem("R_EArea"):show() 920 | page:getItem("T_GuideShow"):start() 921 | end 922 | 923 | local cbrelrot = page:addItem(CheckBox:new("CB_RelativeRotation")) 924 | cbrelrot.caption = "Rel. rotation" 925 | cbrelrot:setPosition(8,468) 926 | function cbrelrot:click(b) if b == 1 then page:getItem("ParticleEmitter").ps:setRelativeRotation(cbrelrot.checked) end end 927 | 928 | local cbfmouse = page:addItem(CheckBox:new("CB_FollowMouse")) 929 | cbfmouse.caption = "Follow mouse" 930 | cbfmouse:setPosition(128,468) 931 | function cbfmouse:click(b) if b == 1 then page:getItem("ParticleEmitter").followMouse = cbfmouse.checked if cbfmouse.checked == false then 932 | page:getItem("ParticleEmitter").ps:moveTo(love.graphics.getWidth()/2+224,love.graphics.getHeight()/2) end 933 | end end 934 | 935 | local gbimode = page:addItem(GroupBox:new("GB_InsertMode")) 936 | gbimode:setSize(224,24) 937 | gbimode.caption = "Insert mode" 938 | 939 | local rbimtop = gbimode:addItem(RadioButton:new("RB_IM_Top")) 940 | rbimtop:setPosition(4,4) 941 | rbimtop.caption = "top" 942 | rbimtop.buttonStyle = true 943 | rbimtop:setSize(64,16) 944 | rbimtop.checked = true 945 | function rbimtop:click(b) if b == 1 then page:getItem("ParticleEmitter").ps:setInsertMode(rbimtop.group[rbimtop:getGroupIndex()].caption) end end 946 | 947 | local rbimbot = gbimode:addItem(RadioButton:new("RB_IM_Bottom")) 948 | rbimbot:setPosition(80,4) 949 | rbimbot.caption = "bottom" 950 | rbimbot.buttonStyle = true 951 | rbimbot:setSize(64,16) 952 | function rbimbot:click(b) if b == 1 then page:getItem("ParticleEmitter").ps:setInsertMode(rbimbot.group[rbimbot:getGroupIndex()].caption) end end 953 | 954 | local rbimrnd = gbimode:addItem(RadioButton:new("RB_IM_Random")) 955 | rbimrnd:setPosition(156,4) 956 | rbimrnd.caption = "random" 957 | rbimrnd.buttonStyle = true 958 | rbimrnd:setSize(64,16) 959 | function rbimrnd:click(b) if b == 1 then page:getItem("ParticleEmitter").ps:setInsertMode(rbimrnd.group[rbimrnd:getGroupIndex()].caption) end end 960 | local imgroup = {rbimtop,rbimbot,rbimrnd} 961 | for i=1,3 do imgroup[i]:setGroup(imgroup) end 962 | gbimode:setPosition(8,504) 963 | 964 | 965 | local gbsizes = page:addItem(GroupBox:new("GB_Size_Selector")) 966 | gbsizes:setSize(224,48) 967 | gbsizes.caption = "Sizes" 968 | 969 | local spsrange = gbsizes:addItem(Spin:new("SP_Size_Range")) 970 | spsrange.caption = "Range:" 971 | spsrange.leftCaption = true 972 | spsrange:setPosition(56,4) 973 | spsrange:setSize(24,16) 974 | spsrange.max = 8 975 | spsrange.maxdec = 0 976 | spsrange.min = 1 977 | spsrange.value = 1 978 | 979 | 980 | local spszvar = gbsizes:addItem(Spin:new("SP_Size_Var")) 981 | spszvar.caption = "Var." 982 | spszvar.max = 1 983 | spszvar.maxdec = 2 984 | spszvar.min = 0 985 | spszvar.leftCaption = true 986 | spszvar.allowMult = true 987 | spszvar.step = 0.1 988 | spszvar:setPosition(188,4) 989 | spszvar:setSize(32,16) 990 | function spszvar:changeValue() page:getItem("ParticleEmitter").ps:setSizeVariation(self.value) end 991 | 992 | local spsz8 = gbsizes:addItem(Spin:new("SP_Size_8")) 993 | local spsz7 = gbsizes:addItem(Spin:new("SP_Size_7")) 994 | local spsz6 = gbsizes:addItem(Spin:new("SP_Size_6")) 995 | local spsz5 = gbsizes:addItem(Spin:new("SP_Size_5")) 996 | local spsz4 = gbsizes:addItem(Spin:new("SP_Size_4")) 997 | local spsz3 = gbsizes:addItem(Spin:new("SP_Size_3")) 998 | local spsz2 = gbsizes:addItem(Spin:new("SP_Size_2")) 999 | local spsz1 = gbsizes:addItem(Spin:new("SP_Size_1")) 1000 | local szspins = {spsz1,spsz2,spsz3,spsz4,spsz5,spsz6,spsz7,spsz8} 1001 | 1002 | spsz1:setSize(24,16) 1003 | spsz1.caption = "" 1004 | spsz1:setPosition(8,28) 1005 | spsz1.step = 0.1 1006 | spsz1.value = 1 1007 | spsz1.allowMult = true 1008 | spsz1.max = nil 1009 | spsz1.min = nil 1010 | function spsz1:changeValue() 1011 | local szarr = {} 1012 | for i=1,spsrange.value do 1013 | szarr[i] = szspins[i].value 1014 | end 1015 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1016 | end 1017 | 1018 | spsz2:setSize(24,16) 1019 | spsz2.caption = "" 1020 | spsz2:setPosition(spsz1.x+spsz1.w+2,spsz1.y) 1021 | spsz2.step = 0.1 1022 | spsz2.value = 1 1023 | spsz2.allowMult = true 1024 | spsz2.max = nil 1025 | spsz2.min = nil 1026 | spsz2.active = false 1027 | function spsz2:changeValue() 1028 | local szarr = {} 1029 | for i=1,spsrange.value do 1030 | szarr[i] = szspins[i].value 1031 | end 1032 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1033 | end 1034 | 1035 | spsz3:setSize(24,16) 1036 | spsz3.caption = "" 1037 | spsz3:setPosition(spsz2.x+spsz2.w+2,spsz2.y) 1038 | spsz3.step = 0.1 1039 | spsz3.value = 1 1040 | spsz3.allowMult = true 1041 | spsz3.max = nil 1042 | spsz3.min = nil 1043 | spsz3.active = false 1044 | function spsz3:changeValue() 1045 | local szarr = {} 1046 | for i=1,spsrange.value do 1047 | szarr[i] = szspins[i].value 1048 | end 1049 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1050 | end 1051 | 1052 | spsz4:setSize(24,16) 1053 | spsz4.caption = "" 1054 | spsz4:setPosition(spsz3.x+spsz3.w+2,spsz3.y) 1055 | spsz4.step = 0.1 1056 | spsz4.value = 1 1057 | spsz4.allowMult = true 1058 | spsz4.max = nil 1059 | spsz4.min = nil 1060 | spsz4.active = false 1061 | function spsz4:changeValue() 1062 | local szarr = {} 1063 | for i=1,spsrange.value do 1064 | szarr[i] = szspins[i].value 1065 | end 1066 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1067 | end 1068 | 1069 | spsz5:setSize(24,16) 1070 | spsz5.caption = "" 1071 | spsz5:setPosition(spsz4.x+spsz4.w+2,spsz3.y) 1072 | spsz5.step = 0.1 1073 | spsz5.value = 1 1074 | spsz5.allowMult = true 1075 | spsz5.max = nil 1076 | spsz5.min = nil 1077 | spsz5.active = false 1078 | function spsz5:changeValue() 1079 | local szarr = {} 1080 | for i=1,spsrange.value do 1081 | szarr[i] = szspins[i].value 1082 | end 1083 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1084 | end 1085 | 1086 | spsz6:setSize(24,16) 1087 | spsz6.caption = "" 1088 | spsz6:setPosition(spsz5.x+spsz5.w+2,spsz5.y) 1089 | spsz6.step = 0.1 1090 | spsz6.allowMult = true 1091 | spsz6.max = nil 1092 | spsz6.value = 1 1093 | spsz6.min = nil 1094 | spsz6.active = false 1095 | function spsz6:changeValue() 1096 | local szarr = {} 1097 | for i=1,spsrange.value do 1098 | szarr[i] = szspins[i].value 1099 | end 1100 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1101 | end 1102 | 1103 | spsz7:setSize(24,16) 1104 | spsz7.caption = "" 1105 | spsz7:setPosition(spsz6.x+spsz6.w+2,spsz6.y) 1106 | spsz7.step = 0.1 1107 | spsz7.allowMult = true 1108 | spsz7.max = nil 1109 | spsz7.value = 1 1110 | spsz7.min = nil 1111 | spsz7.active = false 1112 | function spsz7:changeValue() 1113 | local szarr = {} 1114 | for i=1,spsrange.value do 1115 | szarr[i] = szspins[i].value 1116 | end 1117 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1118 | end 1119 | 1120 | spsz8:setSize(24,16) 1121 | spsz8.caption = "" 1122 | spsz8:setPosition(spsz7.x+spsz7.w+2,spsz7.y) 1123 | spsz8.step = 0.1 1124 | spsz8.value = 1 1125 | spsz8.allowMult = true 1126 | spsz8.max = nil 1127 | spsz8.min = nil 1128 | spsz8.active = false 1129 | function spsz8:changeValue() 1130 | local szarr = {} 1131 | for i=1,spsrange.value do 1132 | szarr[i] = szspins[i].value 1133 | end 1134 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1135 | end 1136 | 1137 | function spsrange:changeValue() 1138 | local szarr = {} 1139 | for i=1,8 do 1140 | szspins[i].active = (i<=self.value) 1141 | if i<=self.value then 1142 | szarr[i] = szspins[i].value 1143 | end 1144 | end 1145 | page:getItem("ParticleEmitter").ps:setSizes(unpack(szarr)) 1146 | end 1147 | gbsizes:setPosition(8,548) 1148 | 1149 | -- color picker commencing! 1150 | 1151 | local gbcolors = page:addItem(GroupBox:new("GB_Color_Selector")) 1152 | gbcolors:setSize(224,48) 1153 | gbcolors.caption = "Colors" 1154 | 1155 | local spcrange = gbcolors:addItem(Spin:new("SP_Color_Range")) 1156 | spcrange.caption = "Range:" 1157 | spcrange.leftCaption = true 1158 | spcrange.maxdec = 0 1159 | spcrange:setPosition(56,4) 1160 | spcrange:setSize(28,16) 1161 | spcrange.max = 8 1162 | spcrange.min = 1 1163 | spcrange.value = 1 1164 | 1165 | local spcvala = gbcolors:addItem(Spin:new("SP_Color_ValA")) 1166 | spcvala.caption = "" 1167 | spcvala.maxdec = 0 1168 | spcvala.leftCaption = true 1169 | spcvala:setPosition(188,4) 1170 | spcvala:setSize(32,16) 1171 | spcvala.max = 255 1172 | spcvala.min = 0 1173 | spcvala.value = 255 1174 | spcvala.allowMult = true 1175 | spcvala.mult_precision = 1 1176 | 1177 | local spcvalb = gbcolors:addItem(Spin:new("SP_Color_ValB")) 1178 | spcvalb.caption = "" 1179 | spcvalb.leftCaption = true 1180 | spcvalb.maxdec = 0 1181 | spcvalb:setPosition(154,4) 1182 | spcvalb:setSize(32,16) 1183 | spcvalb.max = 255 1184 | spcvalb.min = 0 1185 | spcvalb.value = 255 1186 | spcvalb.allowMult = true 1187 | spcvalb.mult_precision = 1 1188 | 1189 | local spcvalg = gbcolors:addItem(Spin:new("SP_Color_ValG")) 1190 | spcvalg.caption = "" 1191 | spcvalg.leftCaption = true 1192 | spcvalg.maxdec = 0 1193 | spcvalg:setPosition(120,4) 1194 | spcvalg:setSize(32,16) 1195 | spcvalg.max = 255 1196 | spcvalg.min = 0 1197 | spcvalg.value = 255 1198 | spcvalg.allowMult = true 1199 | spcvalg.mult_precision = 1 1200 | 1201 | local spcvalr = gbcolors:addItem(Spin:new("SP_Color_ValR")) 1202 | spcvalr.caption = "" 1203 | spcvalr.leftCaption = true 1204 | spcvalr.maxdec = 0 1205 | spcvalr:setPosition(86,4) 1206 | spcvalr:setSize(32,16) 1207 | spcvalr.max = 255 1208 | spcvalr.min = 0 1209 | spcvalr.allowMult = true 1210 | spcvalr.mult_precision = 1 1211 | spcvalr.value = 255 1212 | 1213 | 1214 | local rbcl1 = gbcolors:addItem(RadioColorPicker:new("RB_Color1")) 1215 | rbcl1.buttonStyle = true 1216 | rbcl1:setSize(24,16) 1217 | rbcl1.caption = "1" 1218 | rbcl1:setPosition(8,28) 1219 | rbcl1.checked = true 1220 | function rbcl1:click(b) 1221 | if b == 1 then 1222 | local cs = page:getItem("GB_Color_Selector") 1223 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1224 | for i=1,4 do valarr[i].value = rbcl1.group[rbcl1:getGroupIndex()].color[i] end 1225 | end 1226 | end 1227 | 1228 | local rbcl2 = gbcolors:addItem(RadioColorPicker:new("RB_Color2")) 1229 | rbcl2.buttonStyle = true 1230 | rbcl2:setSize(24,16) 1231 | rbcl2.caption = "1" 1232 | rbcl2:setPosition(33,28) 1233 | function rbcl2:click(b) 1234 | if b == 1 then 1235 | local cs = page:getItem("GB_Color_Selector") 1236 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1237 | for i=1,4 do valarr[i].value = rbcl2.group[rbcl1:getGroupIndex()].color[i] end 1238 | end 1239 | end 1240 | 1241 | local rbcl3 = gbcolors:addItem(RadioColorPicker:new("RB_Color3")) 1242 | rbcl3.buttonStyle = true 1243 | rbcl3:setSize(24,16) 1244 | rbcl3.caption = "1" 1245 | rbcl3:setPosition(58,28) 1246 | function rbcl3:click(b) 1247 | if b == 1 then 1248 | local cs = page:getItem("GB_Color_Selector") 1249 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1250 | for i=1,4 do valarr[i].value = rbcl3.group[rbcl1:getGroupIndex()].color[i] end 1251 | end 1252 | end 1253 | 1254 | local rbcl4 = gbcolors:addItem(RadioColorPicker:new("RB_Color4")) 1255 | rbcl4.buttonStyle = true 1256 | rbcl4:setSize(24,16) 1257 | rbcl4.caption = "1" 1258 | rbcl4:setPosition(83,28) 1259 | function rbcl4:click(b) 1260 | if b == 1 then 1261 | local cs = page:getItem("GB_Color_Selector") 1262 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1263 | for i=1,4 do valarr[i].value = rbcl4.group[rbcl1:getGroupIndex()].color[i] end 1264 | end 1265 | end 1266 | 1267 | local rbcl5 = gbcolors:addItem(RadioColorPicker:new("RB_Color5")) 1268 | rbcl5.buttonStyle = true 1269 | rbcl5:setSize(24,16) 1270 | rbcl5.caption = "1" 1271 | rbcl5:setPosition(108,28) 1272 | function rbcl5:click(b) 1273 | if b == 1 then 1274 | local cs = page:getItem("GB_Color_Selector") 1275 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1276 | for i=1,4 do valarr[i].value = rbcl5.group[rbcl1:getGroupIndex()].color[i] end 1277 | end 1278 | end 1279 | 1280 | local rbcl6 = gbcolors:addItem(RadioColorPicker:new("RB_Color6")) 1281 | rbcl6.buttonStyle = true 1282 | rbcl6:setSize(24,16) 1283 | rbcl6.caption = "1" 1284 | rbcl6:setPosition(133,28) 1285 | function rbcl6:click(b) 1286 | if b == 1 then 1287 | local cs = page:getItem("GB_Color_Selector") 1288 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1289 | for i=1,4 do valarr[i].value = rbcl6.group[rbcl1:getGroupIndex()].color[i] end 1290 | end 1291 | end 1292 | 1293 | local rbcl7 = gbcolors:addItem(RadioColorPicker:new("RB_Color7")) 1294 | rbcl7.buttonStyle = true 1295 | rbcl7:setSize(24,16) 1296 | rbcl7.caption = "1" 1297 | rbcl7:setPosition(158,28) 1298 | function rbcl7:click(b) 1299 | if b == 1 then 1300 | local cs = page:getItem("GB_Color_Selector") 1301 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1302 | for i=1,4 do valarr[i].value = rbcl7.group[rbcl1:getGroupIndex()].color[i] end 1303 | end 1304 | end 1305 | 1306 | local rbcl8 = gbcolors:addItem(RadioColorPicker:new("RB_Color8")) 1307 | rbcl8.buttonStyle = true 1308 | rbcl8:setSize(24,16) 1309 | rbcl8.caption = "1" 1310 | rbcl8:setPosition(183,28) 1311 | function rbcl8:click(b) 1312 | if b == 1 then 1313 | local cs = page:getItem("GB_Color_Selector") 1314 | local valarr = {cs:getItem("SP_Color_ValR"),cs:getItem("SP_Color_ValG"),cs:getItem("SP_Color_ValB"),cs:getItem("SP_Color_ValA"),} 1315 | for i=1,4 do valarr[i].value = rbcl8.group[rbcl1:getGroupIndex()].color[i] end 1316 | end 1317 | end 1318 | 1319 | local rbclgr = {rbcl1,rbcl2,rbcl3,rbcl4,rbcl5,rbcl6,rbcl7,rbcl8} 1320 | for i=1,8 do 1321 | rbclgr[i]:setGroup(rbclgr) 1322 | if i>1 then rbclgr[i].active = false end 1323 | end 1324 | 1325 | function spcrange:changeValue() 1326 | local clapp = {} 1327 | for i=1,8 do 1328 | local cl = {} 1329 | if i<=spcrange.value then 1330 | rbclgr[i].active = true 1331 | for j=1,4 do 1332 | cl[j] = rbclgr[i].color[j] 1333 | end 1334 | clapp[i] = cl 1335 | else 1336 | rbclgr[i].active = false 1337 | end 1338 | end 1339 | page:getItem("ParticleEmitter").ps:setColors(unpack(clapp)) 1340 | end 1341 | 1342 | function spcvalr:changeValue() 1343 | local rb = page:getItem("GB_Color_Selector"):getItem("RB_Color1") 1344 | rb.group[rb:getGroupIndex()].color[1] = spcvalr.value 1345 | local clapp = {} 1346 | local spcrange = gbcolors:getItem("SP_Color_Range") 1347 | for i=1,8 do 1348 | local cl = {} 1349 | if i<=spcrange.value then 1350 | for j=1,4 do 1351 | cl[j] = rbclgr[i].color[j] 1352 | end 1353 | clapp[i] = cl 1354 | end 1355 | end 1356 | page:getItem("ParticleEmitter").ps:setColors(unpack(clapp)) 1357 | end 1358 | 1359 | function spcvalg:changeValue() 1360 | local rb = page:getItem("GB_Color_Selector"):getItem("RB_Color1") 1361 | rb.group[rb:getGroupIndex()].color[2] = spcvalg.value 1362 | local clapp = {} 1363 | local spcrange = gbcolors:getItem("SP_Color_Range") 1364 | for i=1,8 do 1365 | local cl = {} 1366 | if i<=spcrange.value then 1367 | for j=1,4 do 1368 | cl[j] = rbclgr[i].color[j] 1369 | end 1370 | clapp[i] = cl 1371 | end 1372 | end 1373 | page:getItem("ParticleEmitter").ps:setColors(unpack(clapp)) 1374 | end 1375 | 1376 | function spcvalb:changeValue() 1377 | local rb = page:getItem("GB_Color_Selector"):getItem("RB_Color1") 1378 | rb.group[rb:getGroupIndex()].color[3] = spcvalb.value 1379 | local clapp = {} 1380 | local spcrange = gbcolors:getItem("SP_Color_Range") 1381 | for i=1,8 do 1382 | local cl = {} 1383 | if i<=spcrange.value then 1384 | for j=1,4 do 1385 | cl[j] = rbclgr[i].color[j] 1386 | end 1387 | clapp[i] = cl 1388 | end 1389 | end 1390 | page:getItem("ParticleEmitter").ps:setColors(unpack(clapp)) 1391 | end 1392 | 1393 | function spcvala:changeValue() 1394 | local rb = page:getItem("GB_Color_Selector"):getItem("RB_Color1") 1395 | rb.group[rb:getGroupIndex()].color[4] = spcvala.value 1396 | local clapp = {} 1397 | local spcrange = gbcolors:getItem("SP_Color_Range") 1398 | for i=1,8 do 1399 | local cl = {} 1400 | if i<=spcrange.value then 1401 | for j=1,4 do 1402 | cl[j] = rbclgr[i].color[j] 1403 | end 1404 | clapp[i] = cl 1405 | end 1406 | end 1407 | page:getItem("ParticleEmitter").ps:setColors(unpack(clapp)) 1408 | end 1409 | gbcolors:setPosition(8,616) 1410 | 1411 | local gbldamp = page:addItem(GroupBox:new("GB_LinearDamping")) 1412 | gbldamp.caption = "Lin. damping" 1413 | gbldamp:setSize(84,42) 1414 | 1415 | local spldmin = gbldamp:addItem(Spin:new("SP_LD_Min")) 1416 | spldmin.caption = "min" 1417 | spldmin.allowMult = true 1418 | spldmin.leftCaption = true 1419 | spldmin:setPosition(32,4) 1420 | function spldmin:changeValue() page:getItem("ParticleEmitter").ps:setLinearDamping(gbldamp:getItem("SP_LD_Min").value,gbldamp:getItem("SP_LD_Max").value) end 1421 | 1422 | local spldmax = gbldamp:addItem(Spin:new("SP_LD_Max")) 1423 | spldmax.caption = "max" 1424 | spldmax.allowMult = true 1425 | spldmax.leftCaption = true 1426 | spldmax:setPosition(32,22) 1427 | function spldmax:changeValue() page:getItem("ParticleEmitter").ps:setLinearDamping(gbldamp:getItem("SP_LD_Min").value,gbldamp:getItem("SP_LD_Max").value) end 1428 | 1429 | gbldamp:setPosition(8,684) 1430 | 1431 | local btexman = page:addItem(Button:new("B_TexMan_Show")) 1432 | btexman.caption = "Texture manager" 1433 | btexman:setSize(112,54) 1434 | btexman:setPosition(120,670) 1435 | function btexman:click(b) if b==1 then local tm = page:getItem("GB_Tex_Manager") if tm.visible == true then tm:hide() else tm:show() end end end 1436 | 1437 | local gbtexman = page:addItem(GroupBox:new("GB_Tex_Manager")) 1438 | gbtexman.caption = "Texture manager" 1439 | gbtexman:setSize(224,256) 1440 | gbtexman.showBorder = false 1441 | gbtexman.cornerLT = true 1442 | gbtexman.visible = false 1443 | 1444 | local lbtex = gbtexman:addItem(ListBox:new("LB_TextureList")) 1445 | lbtex:setSize(160,276) 1446 | lbtex:setPosition(4,32) 1447 | function lbtex:click(b) if b == 1 then 1448 | local tex = uim:getItem("IC_Textures"):getItem(lbtex:getSelected()) 1449 | page:getItem("ParticleEmitter").ps:setTexture(tex) 1450 | page:getItem("GB_Tex_Manager"):getItem("IM_TM_Texture"):setImage(uim:getItem("IC_Textures"):getItem(lbtex:getSelected())) 1451 | page:getItem("GB_Tex_Manager"):getItem("L_TextureCaption").caption = "Texture: "..lbtex:getSelected()..":"..tex:getWidth().."x"..tex:getHeight() 1452 | local qc = page:getItem("C_Quads") 1453 | qc:purge() 1454 | qc:addItem(love.graphics.newQuad(0,0,tex:getWidth(),tex:getHeight(),tex:getWidth(),tex:getHeight())) 1455 | local gbqc = gbtexman:getItem("GB_QuadControl") 1456 | local qlist = gbqc:getItem("LB_TM_QuadList") 1457 | qlist:clear() 1458 | local qx,qy,qw,qh = qc:getItem(1):getViewport() 1459 | qlist:addItem(qx..","..qy..","..qw..","..qh) 1460 | gbqc:getItem("SP_QViewport_X").value = qx 1461 | gbqc:getItem("SP_QViewport_Y").value = qy 1462 | gbqc:getItem("SP_QViewport_W").value = qw 1463 | gbqc:getItem("SP_QViewport_H").value = qh 1464 | gbqc:getItem("SP_QViewport_X"):changeValue() 1465 | local offs = gbtexman:getItem("GB_TM_Offset") 1466 | offs:getItem("B_TM_Offset_CenterImage"):click(1) 1467 | end 1468 | end 1469 | 1470 | local ltmdiv = gbtexman:addItem(Line:new("L_TM_Div")) 1471 | ltmdiv:setPosition(170,4) 1472 | ltmdiv:setSize(1,300) 1473 | 1474 | local btmrefr = gbtexman:addItem(Button:new("B_TM_Refresh")) 1475 | btmrefr:setSize(160,24) 1476 | btmrefr:setPosition(4,4) 1477 | btmrefr.caption = "Reload list" 1478 | function btmrefr:click(b) 1479 | if b == 1 then 1480 | local ic = uim:getItem("IC_Textures") 1481 | local lfs = love.filesystem 1482 | local files = lfs.getDirectoryItems("particles/") 1483 | for i,v in ipairs(files) do 1484 | local str = "particles/"..v 1485 | local tex,texname = str,v 1486 | ic:addItem(tex,texname) 1487 | end 1488 | local lbt = page:getItem("GB_Tex_Manager"):getItem("LB_TextureList") 1489 | lbt:clear() 1490 | for i = 1,ic:getCount() do 1491 | local item,name = ic:getItem(i) 1492 | lbt:addItem(name) 1493 | end 1494 | end 1495 | end 1496 | btmrefr:click(1) 1497 | 1498 | 1499 | local ltexcapt = gbtexman:addItem(Label:new("L_TextureCaption")) 1500 | ltexcapt.caption = "Texture: 1x1" 1501 | ltexcapt.wrap = false 1502 | ltexcapt:setPosition(182,4) 1503 | 1504 | local imgtex = gbtexman:addItem(Image:new("IM_TM_Texture")) 1505 | imgtex:setPosition(178,36) 1506 | imgtex.showBorder = true 1507 | local img_tex = uim:getItem("IC_Textures"):getItem("default") 1508 | imgtex:setImage(img_tex) 1509 | 1510 | local imgcross = gbtexman:addItem(Cross:new("CR_Cross")) 1511 | imgcross:setPosition(178,36) 1512 | imgcross.centerOnPos = true 1513 | imgcross.blendMode = "replace" 1514 | 1515 | local quadrect = gbtexman:addItem(Rectangle:new("R_QuadRect")) 1516 | quadrect.mode = "line" 1517 | quadrect.colorFill = {255,0,0,255} 1518 | quadrect:setPosition(178,36) 1519 | quadrect:setSize(1,1) 1520 | 1521 | local gbtmoff = gbtexman:addItem(GroupBox:new("GB_TM_Offset")) 1522 | gbtmoff.caption = "Offset" 1523 | gbtmoff:setSize(148,42) 1524 | 1525 | local boffcent = gbtmoff:addItem(Button:new("B_TM_Offset_CenterImage")) 1526 | boffcent.caption = "Tex. center" 1527 | boffcent:setPosition(66,4) 1528 | boffcent:setSize(80,16) 1529 | function boffcent:click(b) 1530 | if b == 1 then 1531 | local spox = gbtmoff:getItem("SP_TM_OffsetX") 1532 | local spoy = gbtmoff:getItem("SP_TM_OffsetY") 1533 | local img = uim:getItem("IC_Textures"):getItem(lbtex:getSelected()) 1534 | spox.value,spoy.value = img:getWidth()/2,img:getHeight()/2 1535 | spox:changeValue() 1536 | end 1537 | end 1538 | 1539 | local boffcentq = gbtmoff:addItem(Button:new("B_TM_Offset_CenterQuad")) 1540 | boffcentq.caption = "Quad center" 1541 | boffcentq:setPosition(66,22) 1542 | boffcentq:setSize(80,16) 1543 | boffcentq.active = false 1544 | function boffcentq:click(b) 1545 | if b == 1 then 1546 | local qlb = gbtexman:getItem("GB_QuadControl"):getItem("LB_TM_QuadList") 1547 | local qcol = page:getItem("C_Quads") 1548 | if gbtexman:getItem("CB_TM_UseQuads").checked == true then 1549 | local qx,qy,qw,qh = qcol:getItem(qlb.index):getViewport() 1550 | local spox = gbtmoff:getItem("SP_TM_OffsetX") 1551 | local spoy = gbtmoff:getItem("SP_TM_OffsetY") 1552 | spox.value,spoy.value = qw/2,qh/2 1553 | spox:changeValue() 1554 | end 1555 | end 1556 | end 1557 | 1558 | local spoffx = gbtmoff:addItem(Spin:new("SP_TM_OffsetX")) 1559 | spoffx.caption = "X" 1560 | 1561 | spoffx.leftCaption = true 1562 | spoffx.allowMult = true 1563 | spoffx:setPosition(16,4) 1564 | function spoffx:changeValue() 1565 | local sox,soy,cross,im = gbtmoff:getItem("SP_TM_OffsetX"),gbtmoff:getItem("SP_TM_OffsetY"),gbtexman:getItem("CR_Cross"),gbtexman:getItem("IM_TM_Texture") 1566 | page:getItem("ParticleEmitter").ps:setOffset(sox.value,soy.value) 1567 | cross:setPosition(im.x+sox.value,im.y+soy.value) 1568 | end 1569 | 1570 | local spoffy = gbtmoff:addItem(Spin:new("SP_TM_OffsetY")) 1571 | spoffy.caption = "Y" 1572 | spoffy.leftCaption = true 1573 | spoffy.allowMult = true 1574 | spoffy:setPosition(16,22) 1575 | function spoffy:changeValue() 1576 | local sox,soy,cross,im = gbtmoff:getItem("SP_TM_OffsetX"),gbtmoff:getItem("SP_TM_OffsetY"),gbtexman:getItem("CR_Cross"),gbtexman:getItem("IM_TM_Texture") 1577 | page:getItem("ParticleEmitter").ps:setOffset(sox.value,soy.value) 1578 | cross:setPosition(im.x+sox.value,im.y+soy.value) 1579 | end 1580 | 1581 | gbtmoff:setPosition(4,324) 1582 | 1583 | local cbuseq = gbtexman:addItem(CheckBox:new("CB_TM_UseQuads")) 1584 | cbuseq.caption = "Use quads" 1585 | cbuseq:setPosition(4,372) 1586 | function cbuseq:click(b) 1587 | if b == 1 then 1588 | gbtexman:getItem("GB_TM_Offset"):getItem("B_TM_Offset_CenterQuad").active = self.checked 1589 | if self.checked == false then 1590 | gbtexman:getItem("GB_QuadControl"):hide() 1591 | page:getItem("ParticleEmitter").ps:setQuads() 1592 | else 1593 | gbtexman:getItem("GB_QuadControl"):show() 1594 | local cquads = page:getItem("C_Quads") 1595 | page:getItem("ParticleEmitter").ps:setQuads(unpack(cquads.items)) 1596 | end 1597 | end 1598 | end 1599 | 1600 | local gbqctrl = gbtexman:addItem(GroupBox:new("GB_QuadControl")) 1601 | gbqctrl.caption = "Quad control" 1602 | gbqctrl:setSize(164,276) 1603 | gbqctrl:hide() 1604 | 1605 | local lqviewport = gbqctrl:addItem(Label:new("L_QViewport")) 1606 | lqviewport.caption = "Viewport:" 1607 | lqviewport:setPosition(4,4) 1608 | lqviewport.wrap = false 1609 | 1610 | local spqvpx = gbqctrl:addItem(Spin:new("SP_QViewport_X")) 1611 | spqvpx.caption = "X" 1612 | spqvpx.leftCaption = true 1613 | spqvpx.allowMult = true 1614 | spqvpx:setPosition(16,24) 1615 | function spqvpx:changeValue() 1616 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1617 | local cquads = page:getItem("C_Quads") 1618 | local qlb = gbqctrl:getItem("LB_TM_QuadList") 1619 | if #qlb.items>0 then 1620 | cquads:getItem(qlb.index):setViewport(qx.value,qy.value,qw.value,qh.value) 1621 | page:getItem("ParticleEmitter").ps:setQuads(unpack(cquads.items)) 1622 | local str = qx.value..","..qy.value..","..qw.value..","..qh.value 1623 | qlb:setItemValue(qlb.index,str) 1624 | local qr = gbtexman:getItem("R_QuadRect") 1625 | qr:setPosition(gbtexman:getItem("IM_TM_Texture").x+qx.value,gbtexman:getItem("IM_TM_Texture").y+qy.value) 1626 | qr:setSize(qw.value,qh.value) 1627 | end 1628 | end 1629 | 1630 | local spqvpy = gbqctrl:addItem(Spin:new("SP_QViewport_Y")) 1631 | spqvpy.caption = "Y" 1632 | spqvpy.leftCaption = true 1633 | spqvpy.allowMult = true 1634 | spqvpy:setPosition(16,42) 1635 | function spqvpy:changeValue() 1636 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1637 | local cquads = page:getItem("C_Quads") 1638 | local qlb = gbqctrl:getItem("LB_TM_QuadList") 1639 | if #qlb.items>0 then 1640 | cquads:getItem(qlb.index):setViewport(qx.value,qy.value,qw.value,qh.value) 1641 | page:getItem("ParticleEmitter").ps:setQuads(unpack(cquads.items)) 1642 | local str = qx.value..","..qy.value..","..qw.value..","..qh.value 1643 | qlb:setItemValue(qlb.index,str) 1644 | local qr = gbtexman:getItem("R_QuadRect") 1645 | qr:setPosition(gbtexman:getItem("IM_TM_Texture").x+qx.value,gbtexman:getItem("IM_TM_Texture").y+qy.value) 1646 | qr:setSize(qw.value,qh.value) 1647 | end 1648 | end 1649 | 1650 | 1651 | local spqvpw = gbqctrl:addItem(Spin:new("SP_QViewport_W")) 1652 | spqvpw.caption = "W" 1653 | spqvpw.maxdec = 0 1654 | spqvpw.leftCaption = true 1655 | spqvpw.allowMult = true 1656 | spqvpw:setPosition(96,24) 1657 | function spqvpw:changeValue() 1658 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1659 | local cquads = page:getItem("C_Quads") 1660 | local qlb = gbqctrl:getItem("LB_TM_QuadList") 1661 | if #qlb.items>0 then 1662 | cquads:getItem(qlb.index):setViewport(qx.value,qy.value,qw.value,qh.value) 1663 | page:getItem("ParticleEmitter").ps:setQuads(unpack(cquads.items)) 1664 | local str = qx.value..","..qy.value..","..qw.value..","..qh.value 1665 | qlb:setItemValue(qlb.index,str) 1666 | local qr = gbtexman:getItem("R_QuadRect") 1667 | qr:setPosition(gbtexman:getItem("IM_TM_Texture").x+qx.value,gbtexman:getItem("IM_TM_Texture").y+qy.value) 1668 | qr:setSize(qw.value,qh.value) 1669 | end 1670 | end 1671 | 1672 | local spqvph = gbqctrl:addItem(Spin:new("SP_QViewport_H")) 1673 | spqvph.caption = "H" 1674 | spqvph.leftCaption = true 1675 | spqvph.maxdec = 0 1676 | spqvph.allowMult = true 1677 | spqvph:setPosition(96,42) 1678 | function spqvph:changeValue() 1679 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1680 | local cquads = page:getItem("C_Quads") 1681 | local qlb = gbqctrl:getItem("LB_TM_QuadList") 1682 | if #qlb.items>0 then 1683 | cquads:getItem(qlb.index):setViewport(qx.value,qy.value,qw.value,qh.value) 1684 | page:getItem("ParticleEmitter").ps:setQuads(unpack(cquads.items)) 1685 | local str = qx.value..","..qy.value..","..qw.value..","..qh.value 1686 | qlb:setItemValue(qlb.index,str) 1687 | local qr = gbtexman:getItem("R_QuadRect") 1688 | qr:setPosition(gbtexman:getItem("IM_TM_Texture").x+qx.value,gbtexman:getItem("IM_TM_Texture").y+qy.value) 1689 | qr:setSize(qw.value,qh.value) 1690 | end 1691 | end 1692 | 1693 | 1694 | bqadd = gbqctrl:addItem(Button:new("B_TM_AddQuad")) 1695 | bqadd:setSize(48,24) 1696 | bqadd:setPosition(4,64) 1697 | bqadd.caption = "Add" 1698 | function bqadd:click(b) 1699 | if b == 1 then 1700 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1701 | gbqctrl:getItem("LB_TM_QuadList"):addItem(qx.value..","..qy.value..","..qw.value..","..qh.value) 1702 | gbqctrl:getItem("LB_TM_QuadList"):last() 1703 | local tex = uim:getItem("IC_Textures"):getItem(gbtexman:getItem("LB_TextureList"):getSelected()) 1704 | page:getItem("C_Quads"):addItem(love.graphics.newQuad(qx.value,qy.value,qw.value,qh.value,tex:getWidth(),tex:getHeight())) 1705 | end 1706 | end 1707 | 1708 | bqrem = gbqctrl:addItem(Button:new("B_TM_AddQuad")) 1709 | bqrem:setSize(52,24) 1710 | bqrem:setPosition(56,64) 1711 | bqrem.caption = "Remove" 1712 | function bqrem:click(b) 1713 | if b == 1 then 1714 | local lbql = gbqctrl:getItem("LB_TM_QuadList") 1715 | local ql = page:getItem("C_Quads") 1716 | ql:deleteItem(lbql.index) 1717 | lbql:clear() 1718 | for i=1,ql:getCount() do 1719 | local qx,qy,qw,qh = ql:getItem(i):getViewport() 1720 | lbql:addItem(qx..","..qy..","..qw..","..qh) 1721 | end 1722 | 1723 | end 1724 | end 1725 | 1726 | bqset = gbqctrl:addItem(Button:new("B_TM_AddQuad")) 1727 | bqset:setSize(48,24) 1728 | bqset:setPosition(112,64) 1729 | bqset.caption = "Tex" 1730 | function bqset:click(b) 1731 | if b == 1 then 1732 | local qc = page:getItem("C_Quads") 1733 | local ql = gbqctrl:getItem("LB_TM_QuadList") 1734 | local tex = uim:getItem("IC_Textures"):getItem(gbtexman:getItem("LB_TextureList"):getSelected()) 1735 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1736 | qx.value,qy.value,qw.value,qh.value = 0,0,tex:getWidth(),tex:getHeight() 1737 | qx:changeValue() 1738 | end 1739 | end 1740 | 1741 | lbqlist = gbqctrl:addItem(ListBox:new("LB_TM_QuadList")) 1742 | lbqlist:setSize(156,176) 1743 | lbqlist:setPosition(4,96) 1744 | function lbqlist:click(b) 1745 | if b == 1 then 1746 | local qx,qy,qw,qh = gbqctrl:getItem("SP_QViewport_X"),gbqctrl:getItem("SP_QViewport_Y"),gbqctrl:getItem("SP_QViewport_W"),gbqctrl:getItem("SP_QViewport_H") 1747 | local quad = page:getItem("C_Quads"):getItem(lbqlist.index) 1748 | qx.value,qy.value,qw.value,qh.value = quad:getViewport() 1749 | qx:changeValue() 1750 | end 1751 | end 1752 | 1753 | gbqctrl:setPosition(4,410) 1754 | gbtexman:setPosition(248,24) 1755 | 1756 | local gbmisc = page:addItem(GroupBox:new("GB_Misc")) 1757 | gbmisc.caption = "Miscellaneous" 1758 | gbmisc:setSize(210,32) 1759 | 1760 | local bbmode = gbmisc:addItem(Button:new("B_ShowBlendMode")) 1761 | bbmode.caption = "Blend mode" 1762 | bbmode:setSize(80,24) 1763 | bbmode:setPosition(4,4) 1764 | function bbmode:click(b) if b == 1 then if gbmisc:getItem("LB_BlendMode").visible == true then gbmisc:getItem("LB_BlendMode"):hide() else gbmisc:getItem("LB_BlendMode"):show() end end end 1765 | 1766 | local lbbmode = gbmisc:addItem(ListBox:new("LB_BlendMode")) 1767 | lbbmode:addItem(love.graphics.getBlendMode()) 1768 | lbbmode:addItem("add") 1769 | lbbmode:addItem("subtract") 1770 | lbbmode:addItem("screen") 1771 | lbbmode:addItem("replace") 1772 | lbbmode:addItem("multiply") 1773 | lbbmode:setSize(128,128) 1774 | lbbmode:setPosition(4,-100) 1775 | function lbbmode:click(b) 1776 | page:getItem("ParticleEmitter").mode = lbbmode:getSelected() 1777 | end 1778 | lbbmode:hide() 1779 | 1780 | --------------------------------------------------------------------------- 1781 | -- save 1782 | --------------------------------------------------------------------------- 1783 | local bsaveparticle = gbmisc:addItem(Button:new("B_SaveParticle")) 1784 | bsaveparticle:setPosition(86,4) 1785 | bsaveparticle.caption = "Save" 1786 | bsaveparticle:setSize(48,24) 1787 | function bsaveparticle:click(b) 1788 | if b == 1 then 1789 | local em = page:getItem("ParticleEmitter").ps 1790 | saveparticle( em, spsrange, spcrange, szspins, rbclgr, cbuseq, gbtexman, lbbmode, gbimode ) 1791 | end 1792 | end 1793 | 1794 | local codelabel = gbmisc:addItem(Label:new("L_Codelist")) 1795 | codelabel:setPosition(gbmisc.w+32,gbmisc.y-384) 1796 | codelabel:setSize(512,512) 1797 | codelabel.align = "left" 1798 | codelabel.caption = "No code generated\nPress \"Code\" button to generate it and copy to clipboard" 1799 | codelabel.visible = false 1800 | 1801 | local bgetcode = gbmisc:addItem(Button:new("B_GetCode")) 1802 | bgetcode:setPosition(bsaveparticle.w+2+86,4) 1803 | bgetcode.caption = "Code" 1804 | bgetcode:setSize(48,24) 1805 | function bgetcode:click(b) 1806 | if b == 1 then 1807 | local em = page:getItem("ParticleEmitter").ps 1808 | local code = "" 1809 | code = code.."emitter = love.graphics.newParticleSystem(tex,"..em:getBufferSize()..")\n" 1810 | code = code.."emitter:setDirection("..em:getDirection()..")\n" 1811 | local as,asx,asy = em:getAreaSpread() 1812 | code = code.."emitter:setAreaSpread(\""..as.."\","..asx..","..asy..")\n" 1813 | code = code.."emitter:setEmissionRate("..em:getEmissionRate()..")\n" 1814 | code = code.."emitter:setEmitterLifetime("..em:getEmitterLifetime()..")\n" 1815 | local elaxmin,elaymin,elaxmax,elaymax = em:getLinearAcceleration() 1816 | code = code.."emitter:setLinearAcceleration("..elaxmin..","..elaymin..","..elaxmax..","..elaymax..")\n" 1817 | local pttlmin,pttlmax = em:getParticleLifetime() 1818 | code = code.."emitter:setParticleLifetime("..pttlmin..","..pttlmax..")\n" 1819 | local praccmin,praccmax = em:getRadialAcceleration() 1820 | code = code.."emitter:setRadialAcceleration("..praccmin..","..praccmax..")\n" 1821 | local protmin,protmax = em:getRotation() 1822 | code = code.."emitter:setRotation("..protmin..","..protmax..")\n" 1823 | local ptgamin,ptgamax = em:getTangentialAcceleration() 1824 | code = code.."emitter:setTangentialAcceleration("..ptgamin..","..ptgamax..")\n" 1825 | local pspdmin,pspdmax = em:getSpeed() 1826 | code = code.."emitter:setSpeed("..pspdmin..","..pspdmax..")\n" 1827 | local pspinmin,pspinmax = em:getSpin() 1828 | code = code.."emitter:setSpin("..pspinmin..","..pspinmax..")\n" 1829 | code = code.."emitter:setSpinVariation("..em:getSpinVariation()..")\n" 1830 | local pldmin,pldmax = em:getLinearDamping() 1831 | code = code.."emitter:setLinearDamping("..pldmin..","..pldmax..")\n" 1832 | code = code.."emitter:setSpread("..em:getSpread()..")\n" 1833 | code = code.."emitter:setRelativeRotation("..tostring(em:hasRelativeRotation())..")\n" 1834 | local ox,oy = em:getOffset() 1835 | code = code.."emitter:setOffset("..ox..","..oy..")\n" 1836 | local sizes = "" 1837 | for i=1,spsrange.value do 1838 | sizes = sizes..szspins[i].value 1839 | if i~=spsrange.value then sizes = sizes.."," end 1840 | end 1841 | code = code.."emitter:setSizes("..sizes..")\n" 1842 | code = code.."emitter:setSizeVariation("..em:getSizeVariation()..")\n" 1843 | local colors = "" 1844 | for i=1,spcrange.value do 1845 | for j=1,4 do 1846 | colors = colors..rbclgr[i].color[j] 1847 | if j >= 4 and i>=spcrange.value then colors = colors.."" else colors = colors.."," end 1848 | end 1849 | colors = colors.." " 1850 | end 1851 | code = code.."emitter:setColors("..colors..")\n" 1852 | if cbuseq.checked == true and #lbqlist.items>0 then 1853 | local texw,texh = em:getTexture():getWidth(),em:getTexture():getHeight() 1854 | local qlist = "" 1855 | local qvarlist = "" 1856 | for i=1,#lbqlist.items do 1857 | qlist = qlist.."local q"..i.." = love.graphics.newQuad("..lbqlist.items[i]..","..texw..","..texh..")\n" 1858 | qvarlist = qvarlist.."q"..i 1859 | if i<#lbqlist.items then qvarlist = qvarlist.."," end 1860 | end 1861 | code = code..qlist 1862 | code = code.."emitter:setQuads("..qvarlist..")".."\n" 1863 | end 1864 | love.system.setClipboardText(code) 1865 | codelabel.caption = code 1866 | end 1867 | end 1868 | 1869 | local bshowcode = gbmisc:addItem(CheckBox:new("B_ShowCode")) 1870 | bshowcode.buttonStyle = true 1871 | bshowcode.checked = false 1872 | bshowcode:setPosition(bgetcode.x+bgetcode.w+2,bgetcode.y) 1873 | bshowcode:setSize(20,24) 1874 | bshowcode.caption = ">" 1875 | function bshowcode:click(b) if b == 1 then codelabel.visible = self.checked end end 1876 | 1877 | gbmisc:setPosition(252,love.graphics.getHeight()-36) 1878 | 1879 | --------------------------------------------------------------------------- 1880 | -- load 1881 | --------------------------------------------------------------------------- 1882 | local btexman2 = page:addItem(Button:new("B_FileMan_Show")) 1883 | btexman2.caption = "File manager" 1884 | btexman2:setSize(104,32) 1885 | btexman2:setPosition(133,735) 1886 | function btexman2:click(b) if b==1 then local tm = page:getItem("GB_File_Manager") if tm.visible == true then tm:hide() else tm:show() end end end 1887 | 1888 | local gbtexman2 = page:addItem(GroupBox:new("GB_File_Manager")) 1889 | gbtexman2.caption = "File manager" 1890 | gbtexman2:setSize(224,256) 1891 | gbtexman2:setPosition(428,434) 1892 | gbtexman2.showBorder = false 1893 | gbtexman2.cornerLT = true 1894 | gbtexman2.visible = true 1895 | 1896 | local lbfile = gbtexman2:addItem(ListBox:new("LB_FileList")) 1897 | lbfile:setSize(160,276) 1898 | lbfile:setPosition(184,38) 1899 | function lbfile:click(b) 1900 | if b == 2 then 1901 | print(lbfile:getSelected()) 1902 | local em = page:getItem("ParticleEmitter").ps 1903 | loadparticle( lbfile:getSelected(), uim, em, page ) 1904 | end 1905 | end 1906 | 1907 | local btmrefr2 = gbtexman2:addItem(Button:new("B_FM_Refresh")) 1908 | btmrefr2:setSize(160,24) 1909 | btmrefr2:setPosition(gbtexman2.x+5,gbtexman2.y+5) 1910 | btmrefr2.caption = "Reload list" 1911 | function btmrefr2:click(b) 1912 | if b == 1 then 1913 | local icf = uim:getItem("IC_Files") 1914 | local files = love.filesystem.getDirectoryItems("saves/") 1915 | icf:purge() 1916 | for i,filename in ipairs(files) do 1917 | print(i.. ". " .. filename) 1918 | icf:addItem(filename) 1919 | end 1920 | local lbt = page:getItem("GB_File_Manager"):getItem("LB_FileList") 1921 | lbt:clear() 1922 | for i = 1,icf:getCount() do 1923 | local filename = icf:getItem(i) 1924 | lbt:addItem(filename) 1925 | end 1926 | end 1927 | end 1928 | btmrefr2:click(1) 1929 | 1930 | lbfile:setPosition(btmrefr2.x,btmrefr2.y+btmrefr2.h+4) 1931 | end 1932 | -------------------------------------------------------------------------------- /particles/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/1.png -------------------------------------------------------------------------------- /particles/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/2.png -------------------------------------------------------------------------------- /particles/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/circle.png -------------------------------------------------------------------------------- /particles/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/cloud.png -------------------------------------------------------------------------------- /particles/ps_earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/ps_earth.png -------------------------------------------------------------------------------- /particles/ps_mystic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/ps_mystic.png -------------------------------------------------------------------------------- /particles/shockwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/shockwave.png -------------------------------------------------------------------------------- /particles/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/smoke.png -------------------------------------------------------------------------------- /particles/spark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/spark.png -------------------------------------------------------------------------------- /particles/twirl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/twirl.png -------------------------------------------------------------------------------- /particles/wisp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkdxdx/APE/53c86c0ed51e100ec301c19ff56dda14dc32f98f/particles/wisp.png -------------------------------------------------------------------------------- /ui_scripts/ui.lua: -------------------------------------------------------------------------------- 1 | require(ui_scrdir.."ui_element") 2 | require(ui_scrdir.."ui_uielement") 3 | require(ui_scrdir.."ui_button") 4 | require(ui_scrdir.."ui_label") 5 | require(ui_scrdir.."ui_groupbox") 6 | require(ui_scrdir.."ui_spin") 7 | require(ui_scrdir.."ui_shape") 8 | require(ui_scrdir.."ui_checkbox") 9 | require(ui_scrdir.."ui_pageswitch") 10 | require(ui_scrdir.."ui_radiobutton") 11 | require(ui_scrdir.."ui_progressbar") 12 | require(ui_scrdir.."ui_particleemitter") 13 | require(ui_scrdir.."ui_imagecollection") 14 | require(ui_scrdir.."ui_listbox") 15 | require(ui_scrdir.."ui_image") 16 | require(ui_scrdir.."ui_collection") 17 | require(ui_scrdir.."ui_timer") 18 | 19 | local l_gfx = love.graphics 20 | 21 | 22 | -- define UIManager "class": the one that handles all the buttons and labels,draws them, updates them and handles inputs 23 | UIManager = {} 24 | UIManager.__index = UIManager 25 | 26 | function UIManager:new() 27 | local self = {} 28 | setmetatable(self,UIManager) 29 | self.items = {} 30 | self.drawList = {} 31 | self.updateList = {} 32 | self.inputList = {} 33 | return self 34 | end 35 | 36 | function UIManager:addItem(item) 37 | table.insert(self.items,item) 38 | if item.updateable == true then 39 | table.insert(self.updateList,item) 40 | end 41 | if item.drawable == true then 42 | table.insert(self.drawList,item) 43 | end 44 | if item.input == true then 45 | table.insert(self.inputList,item) 46 | end 47 | item:oncreate() 48 | return item 49 | end 50 | 51 | function UIManager:getItem(name,deep) 52 | local c = #self.items 53 | if c>0 then 54 | for i=1,c do 55 | if self.items[i]:getName() == name then 56 | return self.items[i] 57 | elseif self.items[i].items ~= nil and deep == true then 58 | self.items[i]:getItem(name,deep) 59 | end 60 | end 61 | end 62 | return nil 63 | end 64 | 65 | -- element deep search 66 | 67 | function UIManager:draw() 68 | local dl = self.drawList 69 | local c = table.getn(dl) 70 | if c>0 then 71 | for i=1,c do 72 | if dl[i].visible == true then dl[i]:draw() end 73 | end 74 | end 75 | end 76 | 77 | function UIManager:update(dt) 78 | local ul = self.updateList 79 | local c = table.getn(ul) 80 | if c>0 then 81 | for i=1,c do 82 | if ul[i].active == true then ul[i]:update(dt) end 83 | end 84 | end 85 | end 86 | 87 | function UIManager:mousemoved(x,y,dx,dy) 88 | local ill = self.inputList 89 | for i,v in ipairs(ill) do 90 | if v.active == true then v:mousemoved(x,y,dx,dy) end 91 | end 92 | end 93 | 94 | function UIManager:mousepressed(x,y,b) 95 | for i,v in ipairs(self.inputList) do 96 | if v.active == true then v:mousepressed(x,y,b) end 97 | end 98 | end 99 | 100 | function UIManager:mousereleased(x,y,b) 101 | for i,v in ipairs(self.inputList) do 102 | if v.active == true then v:mousereleased(x,y,b) end 103 | end 104 | end 105 | 106 | function UIManager:wheelmoved(x,y) 107 | for i,v in ipairs(self.inputList) do 108 | if v.active == true then v:wheelmoved(x,y) end 109 | end 110 | end 111 | 112 | function UIManager:keypressed(key,isrepeat) 113 | for i,v in ipairs(self.inputList) do 114 | if v.active == true then v:keypressed(key,isrepeat) end 115 | end 116 | end 117 | 118 | function UIManager:keyreleased(key) 119 | for i,v in ipairs(self.inputList) do 120 | if v.active == true then v:keyreleased(key) end 121 | end 122 | end 123 | 124 | function UIManager:textinput(t) 125 | for i,v in ipairs(self.inputList) do 126 | if v.active == true then v:textinput(t) end 127 | end 128 | end 129 | 130 | function UIManager:onchangewindow(w,h) 131 | for i,v in ipairs(self.items) do 132 | v:onchangewindow(w,h) 133 | end 134 | end 135 | 136 | function UIManager:init() 137 | uistartup(self) 138 | end 139 | -------------------------------------------------------------------------------- /ui_scripts/ui_button.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | 4 | -- The Button class definition 5 | -- highlights when mouse over it 6 | -- does stuff if clicked 7 | -- has caption 8 | -- somewhat cute 9 | 10 | Button = {} 11 | Button.__index = Button 12 | Button.ident = "ui_button" 13 | Button.caption = "Button" 14 | Button.name = "Button" 15 | Button.showBorder = false 16 | function Button:new(name) 17 | local self = {} 18 | setmetatable(self,Button) 19 | if name ~= nil then self.name = name end 20 | return self 21 | end 22 | setmetatable(Button,{__index = UIElement}) -- inherits from UIElement class, inherits its input methods and stuff 23 | 24 | 25 | function Button:draw() 26 | local cr,cg,cb,ca = love.graphics.getColor() 27 | if self:isMouseOver() == true and self.active == true then 28 | l_gfx.setColor(self.colorHighlight) 29 | else 30 | l_gfx.setColor(self.colorFill) 31 | end 32 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 33 | if self.showBorder == true then 34 | l_gfx.setColor(self.colorLine) 35 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 36 | end 37 | if self.active == true then 38 | l_gfx.setColor(self.colorFont) 39 | else 40 | l_gfx.setColor(self.colorDisabledFill) 41 | end 42 | l_gfx.printf(self.caption,self.x,self.y+(self.h/2-7),self.w,"center") 43 | l_gfx.setColor(cr,cg,cb,ca) 44 | end -------------------------------------------------------------------------------- /ui_scripts/ui_checkbox.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- the checkbox class definition 4 | -- looks like a rectangle in a rectangle, changes its state on click and does stuff 5 | 6 | CheckBox = {} 7 | CheckBox.__index = CheckBox 8 | CheckBox.caption = "CheckBox" 9 | CheckBox.ident = "ui_checkbox" 10 | CheckBox.colorHighlight = {192,192,192,128} 11 | CheckBox.colorWeakFill = {64,64,64,128} 12 | CheckBox.name = "CheckBox" 13 | CheckBox.w = 16 14 | CheckBox.h = 16 15 | CheckBox.buttonStyle = false 16 | function CheckBox:new(name) 17 | local self = {} 18 | setmetatable(self,CheckBox) 19 | if name ~= nil then self.name = name end 20 | self.checked = false 21 | return self 22 | end 23 | setmetatable(CheckBox,{__index = UIElement}) 24 | 25 | function CheckBox:mousepressed(x,y,b) 26 | if self:isMouseOver(x,y) then 27 | self.checked = not(self.checked) -- toggle checked state on click 28 | self:click(b) -- and do stuff after it 29 | end 30 | end 31 | 32 | function CheckBox:draw() 33 | local cr,cg,cb,ca = love.graphics.getColor() 34 | if self.buttonStyle == true then 35 | l_gfx.setColor(self.colorLine) 36 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 37 | if self.checked == true then l_gfx.setColor(self.colorFill) else l_gfx.setColor(self.colorWeakFill) end 38 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 39 | l_gfx.setColor(self.colorFont) 40 | l_gfx.printf(self.caption,self.x,self.y+(self.h/2-7),self.w,"center") 41 | else 42 | l_gfx.setColor(self.colorHighlight) 43 | l_gfx.rectangle("line",self.x,self.y,16,16) 44 | if self.checked == true then 45 | l_gfx.setColor(self.colorHighlight) 46 | l_gfx.rectangle("fill",self.x+2,self.y+2,12,12) 47 | end 48 | l_gfx.setColor(self.colorFont) 49 | l_gfx.print(self.caption,self.x+18,self.y) 50 | end 51 | l_gfx.setColor(cr,cg,cb,ca) 52 | end -------------------------------------------------------------------------------- /ui_scripts/ui_collection.lua: -------------------------------------------------------------------------------- 1 | -- undrawable container element 2 | -- you can hold stuff in it, strings, numbers, tables, userdata etc 3 | -- has simple adding, deleting and getting interface 4 | -- UIManager's version of an array 5 | 6 | Collection = {} 7 | Collection.__index = Collection 8 | Collection.ident = "ui_collection" 9 | Collection.name = "Collection" 10 | Collection.updateable = false 11 | Collection.input = false 12 | Collection.drawable = false 13 | function Collection:new(name) 14 | local self = {} 15 | setmetatable(self,Collection) 16 | self.items = {} 17 | if name ~= nil then self.name = name end 18 | return self 19 | end 20 | setmetatable(Collection,{__index = Element}) 21 | 22 | 23 | -- adds element into collection and fires onadd event 24 | function Collection:addItem(item) 25 | table.insert(self.items,item) 26 | self:onadd() 27 | end 28 | 29 | 30 | -- if element is a table or userdata, you will receive a reference to it, in other cases you will create duplicate of that element in your variable 31 | function Collection:getItem(index) 32 | return self.items[index] 33 | end 34 | 35 | function Collection:deleteItem(index) 36 | table.remove(self.items,index) 37 | self:ondelete() 38 | end 39 | 40 | -- clears collection of anything 41 | function Collection:purge() 42 | for k,v in pairs(self.items) do self.items[k] = nil end 43 | end 44 | 45 | function Collection:getCount() 46 | return #self.items 47 | end 48 | 49 | function Collection:onadd() end 50 | function Collection:ondelete() end 51 | 52 | 53 | -- UIManager will try to update this collection, you should declare what should it do on every tick, 54 | --[[ 55 | local collection = Collection:new("UColl") 56 | function collection:update(dt) 57 | .. do stuff 58 | end 59 | ]] 60 | -- otherwise it will do nothing 61 | 62 | UpdateableCollection = {} 63 | UpdateableCollection.__index = UpdateableCollection 64 | UpdateableCollection.ident = "ui_updateablecollection" 65 | UpdateableCollection.name = "UpdateableCollection" 66 | UpdateableCollection.updateable = true 67 | UpdateableCollection.input = false 68 | UpdateableCollection.drawable = false 69 | function UpdateableCollection:new(name) 70 | local self = {} 71 | setmetatable(self,UpdateableCollection) 72 | self.items = {} 73 | return self 74 | end 75 | setmetatable(UpdateableCollection,{__index = Collection}) 76 | 77 | 78 | function UpdateableCollection:update(dt) 79 | local c = self:getCount() 80 | if c>0 then 81 | for i=1,c do 82 | self.items[i]:update(dt) 83 | end 84 | end 85 | end -------------------------------------------------------------------------------- /ui_scripts/ui_element.lua: -------------------------------------------------------------------------------- 1 | -- root element class 2 | -- defines the element itself, usually invisible and is usefull for elements 3 | -- which are invisible but have their purpose (e.g. Timer element, or Collection element) 4 | 5 | Element = {} 6 | Element.__index = Element 7 | 8 | Element.active = true -- putting this to true will make UIManager to handle this object, like updating, drawing, etc... 9 | Element.drawable = false -- putting this to true will make UIManager to try to draw this element by calling its draw() method 10 | Element.input = true -- this will make UIManager to send input to this object by firing according methods 11 | Element.updateable = false -- this will make UIManager to try to invoke update(dt) method 12 | Element.name = "Element" -- this identifier should be unique to every element you create, so then you can find it without storing a reference variable 13 | Element.ident = "ui_element" -- this is a type identifier, for, say "make all buttons go invisible" or something 14 | function Element:new(name) -- element instancing 15 | local self = {} 16 | setmetatable(self,Element) 17 | if name ~= nil then self.name = name end -- if name is not specified, it will use its default name 18 | return self 19 | end 20 | 21 | 22 | -- all the methods are usually fired by uimanager 23 | function Element:getName() return self.name end 24 | function Element:oncreate() end 25 | function Element:keypressed(key,isrepeat) end 26 | function Element:keyreleased(key) end 27 | function Element:mousepressed(x,y,b) end 28 | function Element:mousereleased(x,y,b) end 29 | function Element:wheelmoved(x,y) end 30 | function Element:mousemoved(x,y,dx,dy) end 31 | function Element:onchangewindow(w,h) end 32 | function Element:textinput(t) end 33 | 34 | 35 | Container = {} 36 | Container.__index = Container 37 | Container.name = "Container" 38 | Container.ident = "ui_container" 39 | function Container:new(name) 40 | local self = setmetatable({},Container) 41 | self.name = name or self.name 42 | return self 43 | end 44 | setmetatable(Container,{__index = Element}) 45 | 46 | function Container:addItem(item) 47 | table.insert(self.items,item) 48 | end 49 | 50 | function Container:getItem(name) 51 | local c = #self.items 52 | if c>0 then 53 | for i=1,c do 54 | if self.items[i]:getName() == item then return self.items[i] 55 | elseif self.items[i].items ~= nil then return self.items[i]:getItem(name) end 56 | end 57 | end 58 | return nil 59 | end 60 | 61 | function Container:deleteItem(name) 62 | local c = #self.items 63 | if c>0 then 64 | for k,v in pairs(self.items) do 65 | self.items[k] = nil 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /ui_scripts/ui_groupbox.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- Groupbox is a container element. it can hold other ui elements, they will move with it on every SetPosition call if its isContainer field is true 4 | -- also has caption, can be drawn borderless (showBorder = false) and can display corners 5 | -- this element also will try to update updateables, draw drawables and handle inputs if added elements are ones 6 | 7 | GroupBox = {} 8 | GroupBox.__index = GroupBox 9 | GroupBox.ident = "ui_groupbox" 10 | GroupBox.caption = "GroupBox" 11 | GroupBox.updateable = true 12 | GroupBox.stenciled = false 13 | GroupBox.name = "GroupBox" 14 | GroupBox.showBorder = true 15 | GroupBox.isContainer = true 16 | GroupBox.caption_xpad = 4 17 | GroupBox.caption_ypad = -16 18 | GroupBox.captionAlign = "center" 19 | GroupBox.colorBcgFill = {0,32,32,128} 20 | GroupBox.showBackground = false 21 | GroupBox.input = true 22 | 23 | function GroupBox:new(name) 24 | local self = {} 25 | setmetatable(self,GroupBox) 26 | self.items = {} 27 | self.drawList = {} 28 | self.updateList = {} 29 | self.inputList = {} 30 | -- topleft,topright,bottomright,bottomleft corners 31 | self.cornerLT = false 32 | self.cornerRT = false 33 | self.cornerRB = false 34 | self.cornerLB = false 35 | if name ~= nil then self.name = name end 36 | return self 37 | end 38 | -- Group box is a mix between UIManager and UIElement. You could make child of either, but i decided to redefine UIManager's methods instead of UIElement's fields 39 | setmetatable(GroupBox,{__index = UIElement}) 40 | 41 | function GroupBox:draw() 42 | local r,g,b,a = l_gfx.getColor() 43 | if self.showBackground == true then 44 | l_gfx.setColor(self.colorBcgFill) 45 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 46 | end 47 | if self.showBorder == true then 48 | l_gfx.setColor(self.colorLine) 49 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 50 | else 51 | local c_width,c_height = self.w/2,self.h/2 52 | if self.cornerLT == true then 53 | l_gfx.setColor(self.colorLine) 54 | l_gfx.line(self.x,self.y,self.x,self.y+c_height) 55 | l_gfx.line(self.x,self.y,self.x+c_width,self.y) 56 | end 57 | end 58 | l_gfx.setColor(self.colorFont) 59 | l_gfx.printf(self.caption,self.x,self.y+self.caption_ypad,self.w,self.captionAlign) 60 | local dl = self.drawList 61 | local c = table.getn(dl) 62 | if c>0 then 63 | for i=1,c do 64 | if dl[i].visible == true then dl[i]:draw() end 65 | end 66 | end 67 | l_gfx.setColor(r,g,b,a) 68 | end 69 | 70 | function GroupBox:update(dt) 71 | local ul = self.updateList 72 | local c = table.getn(ul) 73 | if c>0 then 74 | for i=1,c do 75 | if ul[i].active == true then ul[i]:update(dt) end 76 | end 77 | end 78 | end 79 | 80 | function GroupBox:mousemoved(x,y,dx,dy) 81 | local ill = self.inputList 82 | for i,v in ipairs(ill) do 83 | if v.active == true then v:mousemoved(x,y,dx,dy) end 84 | end 85 | end 86 | 87 | function GroupBox:mousepressed(x,y,b) 88 | local r 89 | if self:isMouseOver(x,y) == true then 90 | self:click(b) 91 | end 92 | for i,v in ipairs(self.inputList) do 93 | if v.active == true then local tr = v:mousepressed(x,y,b) if tr~=nil then r = tr end end 94 | end 95 | return r 96 | end 97 | 98 | function GroupBox:wheelmoved(x,y) 99 | local r 100 | for i,v in ipairs(self.inputList) do 101 | if v.active == true then local tr = v:wheelmoved(x,y) if tr~=nil then r = tr end end 102 | end 103 | return r 104 | end 105 | 106 | function GroupBox:mousereleased(x,y,b) 107 | local r 108 | if self:isMouseOver(x,y) == true then 109 | self:unclick(b) 110 | end 111 | for i,v in ipairs(self.inputList) do 112 | if v.active == true then local tr = v:mousereleased(x,y,b) if tr~=nil then r = tr end end 113 | end 114 | return r 115 | end 116 | 117 | function GroupBox:keypressed(key,isrepeat) 118 | for i,v in ipairs(self.inputList) do 119 | if v.active == true then v:keypressed(key,isrepeat) end 120 | end 121 | end 122 | 123 | function GroupBox:keyreleased(key) 124 | for i,v in ipairs(self.inputList) do 125 | if v.active == true then v:keyreleased(key) end 126 | end 127 | end 128 | 129 | function GroupBox:textinput(t) 130 | for i,v in ipairs(self.inputList) do 131 | if v.active == true then v:textinput(t) end 132 | end 133 | end 134 | 135 | function GroupBox:addItem(item) 136 | table.insert(self.items,item) 137 | if item.updateable == true then 138 | table.insert(self.updateList,item) 139 | end 140 | if item.drawable == true then 141 | table.insert(self.drawList,item) 142 | end 143 | if item.input == true then 144 | table.insert(self.inputList,item) 145 | end 146 | return item 147 | end 148 | 149 | function GroupBox:getItem(name,deep) 150 | local c = #self.items 151 | if c>0 then 152 | for i=1,c do 153 | if self.items[i]:getName() == name then 154 | return self.items[i] 155 | elseif self.items[i].items ~= nil and deep == true then 156 | self.items[i]:getItem(name,deep) 157 | end 158 | end 159 | end 160 | return nil 161 | end 162 | 163 | function GroupBox:setPosition(x,y) 164 | if self.isContainer == true then 165 | local dx,dy = (x or self.x) - self.x, (y or self.y) - self.y 166 | local c = #self.items 167 | if c>0 then 168 | for i=1,c do 169 | local e = self.items[i] 170 | e:setPosition(e.x+dx,e.y+dy) 171 | end 172 | end 173 | end 174 | self.x,self.y = x or self.x, y or self.y 175 | end 176 | 177 | function GroupBox:onchangewindow(w,h) 178 | for i,v in ipairs(self.items) do 179 | v:onchangewindow(w,h) 180 | end 181 | end -------------------------------------------------------------------------------- /ui_scripts/ui_image.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- this elements just draws an image which you can specify with SetImage method 4 | -- you can also set offset,scale,angle,skew and make it display its border 5 | Image = {} 6 | Image.__index = Image 7 | Image.ident = "ui_image" 8 | Image.name = "Image" 9 | Image.caption = "" 10 | Image.ox = 0 11 | Image.oy = 0 12 | Image.sx = 1 13 | Image.sy = 1 14 | Image.r = 0 15 | Image.kx = 0 16 | Image.ky = 0 17 | Image.showBorder = false 18 | Image.colorTint = {255,255,255,255} 19 | Image.mode = "alpha" 20 | function Image:new(name) 21 | local self = {} 22 | setmetatable(self,Image) 23 | if name ~= nil then self.name = name end 24 | return self 25 | end 26 | setmetatable(Image,{__index = UIElement}) 27 | 28 | function Image:draw() 29 | if self.img ~= nil then 30 | local r,g,b,a = l_gfx.getColor() 31 | l_gfx.setColor(self.colorTint) 32 | if self.stencil ~= nil then 33 | l_gfx.stencil(self.stencil) 34 | end 35 | local mode = l_gfx.getBlendMode() 36 | l_gfx.setBlendMode(self.mode) 37 | l_gfx.draw(self.img, self.x,self.y,self.r,self.sx,self.sy,self.ox,self.oy,self.kx,self.ky) 38 | l_gfx.setBlendMode(mode) 39 | l_gfx.setStencilTest() 40 | if self.showBorder == true then 41 | l_gfx.setColor(self.colorLine) 42 | local w,h = self.img:getWidth(),self.img:getHeight() 43 | l_gfx.rectangle("line",self.x-1,self.y-1,w+2,h+2) 44 | end 45 | l_gfx.setColor(r,g,b,a) 46 | end 47 | end 48 | 49 | 50 | function Image:setImage(img) 51 | if type(img) == "string" then 52 | self.img = l_gfx.newImage(img) 53 | else 54 | self.img = img 55 | end 56 | end 57 | 58 | QuadSlider = {} 59 | QuadSlider.__index = QuadSlider 60 | QuadSlider.name = "QuadSlider" 61 | QuadSlider.ident = "ui_quadslider" 62 | function QuadSlider:new(name) 63 | local self = setmetatable({},QuadSlider) 64 | self.name = name or self.name 65 | self.index = 1 66 | return self 67 | end 68 | setmetatable(QuadSlider,{__index = Image}) 69 | 70 | function QuadSlider:setQuads(q) 71 | self.quads = q 72 | end 73 | 74 | function QuadSlider:draw() 75 | if self.img ~= nil and self.quads ~= nil and self.index<=#self.quads then 76 | l_gfx.setColor(self.colorTint) 77 | l_gfx.draw(self.img, self.quads[self.index], self.x,self.y,self.r,self.sx,self.sy,self.ox,self.oy,self.kx,self.ky) 78 | if self.showBorder == true then 79 | l_gfx.setColor(self.colorLine) 80 | local w,h = self.img:getWidth(),self.img:getHeight() 81 | l_gfx.rectangle("line",self.x-1,self.y-1,w+2,h+2) 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /ui_scripts/ui_imagecollection.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- This undrawable element is a collection for image resource 4 | -- The thing is - it will try not to repeat resource creation if you try to get something from it, instead giving a reference on already created resource 5 | 6 | ImageCollection = {} 7 | ImageCollection.__index = ImageCollection 8 | ImageCollection.ident = "ui_imagecollection" 9 | ImageCollection.name = "ImageCollection" 10 | function ImageCollection:new(name) 11 | local self = {} 12 | setmetatable(self,ImageCollection) 13 | self.items = {} 14 | if name ~= nil then self.name = name end 15 | return self 16 | end 17 | setmetatable(ImageCollection,{__index = Element}) 18 | 19 | function ImageCollection:addItem(image,name) 20 | local c = self:getCount() 21 | if c>0 then 22 | 23 | for i=1,c do 24 | if self.items[i][2] == name then 25 | return self.items[i][1] 26 | end 27 | end 28 | local name = name or (#self.items+1) 29 | local img = l_gfx.newImage(image) 30 | table.insert(self.items,{img,name}) 31 | return img 32 | else 33 | local name = name or (#self.items+1) 34 | local img = l_gfx.newImage(image) 35 | table.insert(self.items,{img,name}) 36 | return img 37 | end 38 | end 39 | 40 | function ImageCollection:getItem(item) 41 | if type(item) == "number" then 42 | return self.items[item][1],self.items[item][2] 43 | elseif type(item) == "string" then 44 | local c = self:getCount() 45 | if c>0 then 46 | for i=1,c do 47 | if self.items[i][2] == item then 48 | return self.items[i][1],self.items[i][2] 49 | end 50 | end 51 | end 52 | end 53 | return nil 54 | end 55 | 56 | function ImageCollection:getCount() 57 | return #self.items 58 | end -------------------------------------------------------------------------------- /ui_scripts/ui_label.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- Your simple label element 4 | -- can wrap words 5 | -- can align words 6 | 7 | Label = {} 8 | Label.__index = Label 9 | Label.ident = "ui_label" 10 | Label.caption = "Label" 11 | Label.align = "center" 12 | Label.wrap = true 13 | Label.name = "Label" 14 | function Label:new(name) 15 | local self = {} 16 | setmetatable(self,Label) 17 | if name ~= nil then self.name = name end 18 | return self 19 | end 20 | setmetatable(Label,{__index = UIElement}) 21 | 22 | function Label:draw() 23 | local cr,cg,cb,ca = love.graphics.getColor() 24 | l_gfx.setColor(self.colorFont) 25 | if self.wrap == true then 26 | l_gfx.printf(self.caption,self.x,self.y,self.w,self.align) 27 | else 28 | l_gfx.print(self.caption,self.x,self.y) 29 | end 30 | l_gfx.setColor(cr,cg,cb,ca) 31 | end 32 | 33 | -- This label is updateable 34 | -- useful for displaying varying information, e.g. FPS: 35 | --[[ 36 | local fpslabel = RefreshingLabel:new("L_FPS") 37 | fpslabel:update(dt) 38 | fpslabel.caption = "FPS:"..love.timer.getFPS() 39 | end 40 | ]] 41 | RefreshingLabel = {} 42 | RefreshingLabel.__index = RefreshingLabel 43 | RefreshingLabel.ident = "ui_refreshinglabel" 44 | RefreshingLabel.name = "RefreshingLabel" 45 | RefreshingLabel.updateable = true 46 | function RefreshingLabel:new(name) 47 | local self = {} 48 | setmetatable(self,RefreshingLabel) 49 | if name ~= nil then self.name = name end 50 | return self 51 | end 52 | setmetatable(RefreshingLabel,{__index = Label}) 53 | 54 | TextBox = {} 55 | TextBox.__index = TextBox 56 | TextBox.name = "TextBox" 57 | TextBox.ident = "ui_textbox" 58 | TextBox.showBorder = true 59 | TextBox.fill = true 60 | function TextBox:new(name) 61 | local self = setmetatable({},TextBox) 62 | self.name = name or self.name 63 | return self 64 | end 65 | setmetatable(TextBox,{__index = Label}) 66 | 67 | function TextBox:alignBox() 68 | --local w,l = l_gfx.getFont():getWrap(self.caption,self.w) 69 | --self.h = l_gfx.getFont():getHeight()*l 70 | end 71 | 72 | 73 | 74 | function TextBox:draw() 75 | local cr,cg,cb,ca = love.graphics.getColor() 76 | if self.showBorder == true then 77 | l_gfx.setColor(self.colorLine) 78 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 79 | end 80 | if self.fill == true then 81 | l_gfx.setColor(self.colorFill) 82 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 83 | end 84 | l_gfx.setColor(self.colorFont) 85 | l_gfx.printf(self.caption,self.x,self.y,self.w,self.align) 86 | l_gfx.setColor(cr,cg,cb,ca) 87 | end -------------------------------------------------------------------------------- /ui_scripts/ui_listbox.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | local min = math.min 3 | 4 | -- simple list box with index and clickable string list 5 | -- usually linked to collection in its behavior 6 | 7 | ListBox = {} 8 | ListBox.__index = ListBox 9 | ListBox.ident = "ui_listbox" 10 | ListBox.name = "ListBox" 11 | ListBox.caption = "ListBox" 12 | ListBox.itemSpacing = 1 -- this will define a gap between items 13 | ListBox.itemCaptionAlign = "left" 14 | ListBox.itemCaptionPadding = 4 -- this will make text appear shifted 15 | ListBox.displayMax = 16 16 | ListBox.shift = 0 17 | ListBox.showBorder = false 18 | ListBox.showScroll = false 19 | ListBox.scrollWidth = 16 20 | 21 | function ListBox:new(name) 22 | local self = {} 23 | setmetatable(self,ListBox) 24 | self.items = {} 25 | self.index = 0 26 | self.itemHeight = 16 27 | if name ~= nil then self.name = name end 28 | return self 29 | end 30 | setmetatable(ListBox,{__index = UIElement}) 31 | 32 | function ListBox:mousepressed(x,y,b) 33 | if self:isMouseOver(x,y) then 34 | if b == 1 or b == 2 then 35 | local c = #self.items 36 | if c>0 then 37 | local sx,sy,sw,ih = self.x,self.y,self.w,self.itemHeight 38 | for i=(self.shift+1),(self.shift+math.min(self.displayMax,c)) do 39 | local factor = i-1-self.shift 40 | local ix,iy = sx,factor*ih+factor*self.itemSpacing 41 | if x>=sx and x<=sx+sw and y>=sy+iy and y<=sy+iy+ih then 42 | self.index = i 43 | break 44 | end 45 | end 46 | end 47 | self:click(b) 48 | end 49 | end 50 | end 51 | 52 | function ListBox:wheelmoved(x,y) 53 | if y > 0 then 54 | self.shift = self.shift - 1 55 | if self.shift<0 then self.shift = 0 end 56 | elseif y < 0 then 57 | if self.shift<(#self.items-self.displayMax) then 58 | self.shift = self.shift+1 59 | end 60 | end 61 | end 62 | 63 | -- this will return highlighted element 64 | function ListBox:getSelected() 65 | return self.items[self.index] 66 | end 67 | 68 | function ListBox:addItem(item,name) 69 | table.insert(self.items,item) 70 | if #self.items == 1 then self.index = 1 end 71 | end 72 | 73 | 74 | function ListBox:clear() 75 | for k,v in pairs(self.items) do self.items[k] = nil end 76 | self.index = 0 77 | end 78 | 79 | function ListBox:last() 80 | self.index = #self.items 81 | end 82 | 83 | function ListBox:first() 84 | if #self.items>0 then self.index = 1 else self.index = 0 end 85 | end 86 | 87 | function ListBox:setSize(w,h) 88 | self.w = w or self.w self.h = h or self.h 89 | self.displayMax = math.floor(self.h/(self.itemHeight+self.itemSpacing)) 90 | print(self.name.."|"..self.displayMax) 91 | end 92 | 93 | -- if you specify a number, it will return you an item as if you index an array, otherwise it will try to look for it by comparing strings 94 | function ListBox:getItem(item) 95 | local c = #self.items 96 | if c>0 then 97 | for i=1,c do 98 | if self.items[i]:getName() == name then 99 | return self.items[i] 100 | elseif self.items[i].items ~= nil and deep == true then 101 | self.items[i]:getItem(name,deep) 102 | end 103 | end 104 | end 105 | return nil 106 | end 107 | 108 | function ListBox:setItemValue(item,value) 109 | self.items[item] = value 110 | end 111 | 112 | function ListBox:draw() 113 | local cr,cg,cb,ca = l_gfx.getColor() 114 | if self.showBorder == true then 115 | l_gfx.setColor(self.colorLine) 116 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 117 | end 118 | local c = #self.items 119 | local fh = l_gfx.getFont():getHeight()/2 120 | if c>0 then 121 | local sx,sy,sw,ih = self.x,self.y,self.w,self.itemHeight 122 | for i=(self.shift+1),(self.shift+math.min(self.displayMax,c)) do 123 | if self.index == i then 124 | l_gfx.setColor(self.colorHighlight) 125 | else 126 | l_gfx.setColor(self.colorFill) 127 | end 128 | local factor = i-1-self.shift 129 | local space = factor*self.itemSpacing 130 | local ix,iy,iw = self.x, self.y+factor*self.itemHeight+space,self.w 131 | if self.showScroll == true then iw = iw - self.scrollWidth end 132 | local fpad = self.itemHeight/2-fh 133 | 134 | l_gfx.rectangle("fill",ix,iy,iw,self.itemHeight) 135 | l_gfx.setColor(self.colorFont) 136 | if type(self.items[i]) == "table" then 137 | l_gfx.printf(self.items[i][1],ix+self.itemCaptionPadding,iy+fpad,iw-self.itemCaptionPadding,self.itemCaptionAlign) 138 | elseif type(self.items[i]) == "string" then 139 | l_gfx.printf(self.items[i],ix+self.itemCaptionPadding,iy+fpad,iw-self.itemCaptionPadding,self.itemCaptionAlign) 140 | end 141 | end 142 | if self.showScroll == true then 143 | l_gfx.setColor(self.colorFill) 144 | l_gfx.rectangle("line",self.w+self.x-self.scrollWidth,self.y,self.scrollWidth,self.h) 145 | local h = self.h/(math.max(#self.items/self.displayMax,1)) 146 | l_gfx.rectangle("fill",self.w+self.x-self.scrollWidth,self.y+self.shift*self.displayMax,self.scrollWidth,h) 147 | end 148 | end 149 | l_gfx.setColor(cr,cg,cb,ca) 150 | end 151 | 152 | 153 | GaugeList = {} 154 | GaugeList.__index = GaugeList 155 | GaugeList.ident = "ui_gaugelist" 156 | GaugeList.name = "GaugeList" 157 | GaugeList.colorProgress = {0,160,160,128} 158 | function GaugeList:new(name) 159 | local self = {} 160 | setmetatable(self,GaugeList) 161 | self.items = {} 162 | self.index = 0 163 | self.itemHeight = 16 164 | if name ~= nil then self.name = name end 165 | return self 166 | end 167 | setmetatable(GaugeList,{__index = ListBox}) 168 | 169 | function GaugeList:addItem(item,val) 170 | table.insert(self.items,{item,val or 0}) 171 | if #self.items == 1 then self.index = 1 end 172 | end 173 | 174 | function GaugeList:setItemValue(item,value,value2) 175 | self.items[item][1] = value or self.items[item][1] 176 | self.items[item][2] = value2 or 0 177 | end 178 | 179 | function GaugeList:draw() 180 | if self.showBorder == true then 181 | l_gfx.setColor(self.colorLine) 182 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 183 | end 184 | local c = #self.items 185 | local fh = l_gfx.getFont():getHeight()/2 186 | if c>0 then 187 | local sx,sy,sw,ih = self.x,self.y,self.w,self.itemHeight 188 | for i=(self.shift+1),(self.shift+math.min(self.displayMax,c)) do 189 | if self.index == i then 190 | l_gfx.setColor(self.colorHighlight) 191 | else 192 | l_gfx.setColor(self.colorFill) 193 | end 194 | local factor = i-1-self.shift 195 | local space = factor*self.itemSpacing 196 | local ix,iy,iw = self.x, self.y+factor*self.itemHeight+space,self.w 197 | if self.showScroll == true then iw = iw - self.scrollWidth end 198 | local fpad = self.itemHeight/2-fh 199 | 200 | l_gfx.rectangle("fill",ix,iy,iw,self.itemHeight) 201 | l_gfx.setColor(self.colorProgress) 202 | local prg = min(self.items[i][2]/100,1) 203 | 204 | l_gfx.rectangle("fill",ix,iy,prg*iw,self.itemHeight) 205 | l_gfx.setColor(self.colorFont) 206 | if type(self.items[i][1]) == "table" then 207 | l_gfx.printf(self.items[i][1][1],ix+self.itemCaptionPadding,iy+fpad,iw-self.itemCaptionPadding,self.itemCaptionAlign) 208 | elseif type(self.items[i][1]) == "string" then 209 | l_gfx.printf(self.items[i][1],ix+self.itemCaptionPadding,iy+fpad,iw-self.itemCaptionPadding,self.itemCaptionAlign) 210 | end 211 | end 212 | if self.showScroll == true then 213 | l_gfx.setColor(self.colorFill) 214 | l_gfx.rectangle("line",self.w+self.x-self.scrollWidth,self.y,self.scrollWidth,self.h) 215 | local h = self.h/(math.max(#self.items/self.displayMax,1)) 216 | l_gfx.rectangle("fill",self.w+self.x-self.scrollWidth,self.y+self.shift*self.displayMax,self.scrollWidth,h) 217 | end 218 | end 219 | end 220 | 221 | function GaugeList:clear() 222 | for k,v in pairs(self.items) do 223 | self.items[k][1],self.items[k][2] = nil,nil 224 | self.items[k] = nil 225 | end 226 | self.index = 0 227 | end 228 | 229 | 230 | -------------------------------------------------------------------------------- /ui_scripts/ui_pageswitch.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- big and invisible container element 4 | -- used for paged interface, useless without page controller 5 | 6 | PageSwitch = {} 7 | PageSwitch.__index = PageSwitch 8 | PageSwitch.ident = "ui_pageswitch" 9 | PageSwitch.name = "PageSwitch" 10 | PageSwitch.updateable = true 11 | PageSwitch.updateAll = false -- if true, it will try to update all the pages in it, instead of only current one 12 | PageSwitch.isContainer = true 13 | function PageSwitch:new(name) 14 | local self = {} 15 | setmetatable(self,PageSwitch) 16 | self.pages = {} 17 | self.index = 0 18 | if name ~= nil then self.name = name end 19 | return self 20 | end 21 | setmetatable(PageSwitch,{__index = UIElement}) 22 | 23 | function PageSwitch:draw() 24 | local c = #self.pages 25 | if c>0 then 26 | self.pages[self.index]:draw() 27 | end 28 | end 29 | 30 | 31 | function PageSwitch:update(dt) 32 | local c = #self.pages 33 | if c>0 then 34 | if self.updateAll == true then 35 | for i=1,c do 36 | self.pages[i]:draw() 37 | end 38 | else 39 | self.pages[self.index]:update(dt) 40 | end 41 | end 42 | end 43 | 44 | -- to prevent input overlap, input is handled only for current page 45 | function PageSwitch:mousemoved(x,y) 46 | local c = #self.pages 47 | if c>0 then self.pages[self.index]:mousemoved(x,y) end 48 | end 49 | 50 | function PageSwitch:mousepressed(x,y,b) 51 | local c = #self.pages 52 | if c>0 then self.pages[self.index]:mousepressed(x,y,b) end 53 | end 54 | 55 | function PageSwitch:mousereleased(x,y,b) 56 | local c = #self.pages 57 | if c>0 then self.pages[self.index]:mousereleased(x,y,b) end 58 | end 59 | 60 | 61 | function PageSwitch:keypressed(key,isrepeat) 62 | local c = #self.pages 63 | if c>0 then self.pages[self.index]:keypressed(key,isrepeat) end 64 | end 65 | 66 | function PageSwitch:keyreleased(key) 67 | local c = #self.pages 68 | if c>0 then self.pages[self.index]:keyreleased(key,isrepeat) end 69 | end 70 | 71 | function PageSwitch:wheelmoved(x,y) 72 | local c = #self.pages 73 | if c>0 then self.pages[self.index]:wheelmoved(x,y) end 74 | end 75 | 76 | function PageSwitch:setPosition(x,y) 77 | if self.isContainer == true then 78 | local dx,dy = (x or self.x) - self.x, (y or self.y) - self.y 79 | local c = #self.pages 80 | if c>0 then 81 | for i=1,c do 82 | local e = self.pages[i] 83 | e:setPosition(e.x+dx,e.y+dy) 84 | end 85 | end 86 | end 87 | self.x,self.y = x or self.x, y or self.y 88 | end 89 | 90 | 91 | 92 | function PageSwitch:addPage(pg) 93 | if type(pg) == "table" then 94 | local indx = table.getn(self.pages)+1 95 | table.insert(self.pages,pg) 96 | self.index = indx 97 | return pg 98 | elseif type(pg) == "string" or pg == nil then 99 | local indx = table.getn(self.pages)+1 100 | local page = Page:new(pg or ("Page"..indx)) 101 | table.insert(self.pages,page) 102 | self.index = indx 103 | return page 104 | end 105 | 106 | end 107 | 108 | function PageSwitch:removePage(page) 109 | local c = #self.pages 110 | if c>0 then 111 | if page == nil then 112 | table.remove(self.pages,self.index) 113 | self:nextPage() 114 | else 115 | for i=1,c do 116 | if self.pages[i].name == page then 117 | table.remove(self.pages,i) 118 | self:nextPage() 119 | break 120 | end 121 | end 122 | end 123 | end 124 | end 125 | 126 | function PageSwitch:nextPage() 127 | local c = #self.pages 128 | self.index = self.index + 1 129 | if self.index>c then self.index = c end 130 | end 131 | 132 | function PageSwitch:prevPage() 133 | self.index = self.index-1 134 | if self.index <= 0 then self.index = 1 end 135 | end 136 | 137 | function PageSwitch:getItem(name) 138 | local c = #self.pages 139 | if c>0 then 140 | if page == nil then 141 | return self.pages[self.index] 142 | else 143 | for i=1,c do 144 | if self.pages[i].name == page then 145 | return self.pages[i] 146 | end 147 | end 148 | end 149 | end 150 | end 151 | 152 | -- the page is a copy of groupbox 153 | Page = {} 154 | Page.__index = Page 155 | Page.ident = "ui_page" 156 | Page.name = "Page" 157 | Page.caption = "Page" 158 | Page.showBorder = false 159 | function Page:new(name) 160 | local self = {} 161 | setmetatable(self,Page) 162 | self.items = {} 163 | self.drawList = {} 164 | self.updateList = {} 165 | self.inputList = {} 166 | if name ~= nil then self.name = name end 167 | return self 168 | end 169 | setmetatable(Page,{__index = GroupBox}) 170 | 171 | 172 | -- this element controls pageswitch if linked to one. has page flip, add and remove buttons 173 | PageSwitchController = {} 174 | PageSwitchController.__index = PageSwitchController 175 | PageSwitchController.ident = "ui_pageswitchcontroller" 176 | PageSwitchController.name = "PageSwitchController" 177 | PageSwitchController.caption_xpad = 132 178 | PageSwitchController.caption_ypad = 8 179 | function PageSwitchController:new(name) 180 | local self = {} 181 | setmetatable(self,PageSwitchController) 182 | if name ~= nil then self.name = name end 183 | 184 | local bnext = Button:new("ButtonNext") 185 | bnext.caption = ">" 186 | bnext:setSize(32,32) 187 | bnext:setPosition(32,0) 188 | 189 | local bprev = Button:new("ButtonPrev") 190 | bprev.caption = "<" 191 | bprev:setSize(32,32) 192 | bprev:setPosition(0,0) 193 | 194 | 195 | local badd = Button:new("ButtonAdd") 196 | badd.caption = "+" 197 | badd:setSize(32,32) 198 | badd:setPosition(64,0) 199 | 200 | 201 | local brem = Button:new("ButtonRemove") 202 | brem.caption = "-" 203 | brem:setSize(32,32) 204 | brem:setPosition(96,0) 205 | 206 | 207 | 208 | self.items = {bprev,bnext,badd,brem,labcount} 209 | self.drawList = {bprev,bnext,badd,brem,labcount} 210 | self.updateList = {} 211 | self.inputList = {bprev,bnext,badd,brem} 212 | self.w = 128 213 | self.caption = "0/0" 214 | return self 215 | end 216 | setmetatable(PageSwitchController,{__index = GroupBox}) 217 | 218 | function PageSwitchController:setPageSwitch(pgs) 219 | if pgs.ident == "ui_pageswitch" then 220 | self.pageswitch = pgs 221 | local psc = self 222 | local bp,bn,ba,br,lc = self.items[1],self.items[2],self.items[3],self.items[4],self.items[5] 223 | function bn:click(b) if b == "l" then pgs:nextPage() psc.caption = pgs.index.."/"..#pgs.pages end end 224 | function bp:click(b) if b == "l" then pgs:prevPage() psc.caption = pgs.index.."/"..#pgs.pages end end 225 | function ba:click(b) if b == "l" then pgs:addPage() psc.caption = pgs.index.."/"..#pgs.pages end end 226 | function br:click(b) if b == "l" then pgs:removePage() psc.caption = pgs.index.."/"..#pgs.pages end end 227 | end 228 | end 229 | 230 | 231 | -------------------------------------------------------------------------------- /ui_scripts/ui_particleemitter.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | 4 | -- particle emitter element 5 | -- made specifically for particle editor 6 | -- so it may not meet your needs 7 | 8 | ParticleEmitter = {} 9 | ParticleEmitter.__index = ParticleEmitter 10 | ParticleEmitter.ident = "ui_particleemitter" 11 | ParticleEmitter.name = "ParticleEmitter" 12 | ParticleEmitter.updateable = true 13 | ParticleEmitter.x = 0 14 | ParticleEmitter.y = 0 15 | ParticleEmitter.followMouse = false -- if true it will follow the cursor 16 | ParticleEmitter.mode = l_gfx.getBlendMode() 17 | function ParticleEmitter:new(name,tex) 18 | local self = {} 19 | setmetatable(self,ParticleEmitter) 20 | self.ps = l_gfx.newParticleSystem(tex,10) 21 | self.ps:setEmissionRate(1) 22 | self.ps:setEmitterLifetime(-1) 23 | self.ps:setSizes(1) 24 | self.ps:setParticleLifetime(1) 25 | if name ~= nil then self.name = name end 26 | return self 27 | end 28 | 29 | setmetatable(ParticleEmitter,{__index = UIElement}) 30 | 31 | function ParticleEmitter:draw() 32 | local bm = l_gfx.getBlendMode() 33 | l_gfx.setBlendMode(self.mode) 34 | l_gfx.draw(self.ps) 35 | l_gfx.setBlendMode(bm) 36 | end 37 | 38 | function ParticleEmitter:update(dt) 39 | self.ps:update(dt) 40 | end 41 | 42 | function ParticleEmitter:mousemoved(x,y) 43 | if self.followMouse == true then 44 | self.ps:moveTo(x,y) 45 | end 46 | end -------------------------------------------------------------------------------- /ui_scripts/ui_progressbar.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | local format,min,floor = string.format,math.min,math.floor 3 | 4 | -- an updateable element which can show values, or can be updated manually without defining its update behavior 5 | 6 | ProgressBar = {} 7 | ProgressBar.__index = ProgressBar 8 | ProgressBar.ident = "ui_progressbar" 9 | ProgressBar.name = "ProgressBar" 10 | ProgressBar.caption = "" 11 | ProgressBar.displayVal = true 12 | ProgressBar.updateable = true 13 | ProgressBar.leftCaption = false 14 | ProgressBar.asPercentage = false 15 | ProgressBar.w = 128 16 | function ProgressBar:new() 17 | local self = {} 18 | setmetatable(self,ProgressBar) 19 | if name ~= nil then self.name = name end 20 | self.value = 0 21 | self.max = 100 22 | self.showCaption = true 23 | return self 24 | end 25 | setmetatable(ProgressBar,{__index = UIElement}) 26 | 27 | function ProgressBar:draw() 28 | local cr,cg,cb,ca = love.graphics.getColor() 29 | l_gfx.setColor(self.colorFill) 30 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 31 | l_gfx.setColor(self.colorHighlight) 32 | local factor = min(1,self.value/self.max) 33 | l_gfx.rectangle("fill",self.x,self.y,self.w*factor,self.h) 34 | l_gfx.setColor(self.colorFont) 35 | if self.displayVal == true then 36 | if self.asPercentage == true then 37 | l_gfx.printf(floor(self.value/self.max*100).."%",self.x,self.y+(self.h/2-7),self.w,"center") 38 | else 39 | l_gfx.printf(self.value.."/"..self.max,self.x,self.y+(self.h/2-7),self.w,"center") 40 | end 41 | end 42 | if self.showCaption == true then 43 | if self.leftCaption == true then 44 | l_gfx.printf(self.caption,self.x-l_gfx:getFont():getWidth(self.caption),self.y+(self.h/2-7),self.w,"left") 45 | else 46 | l_gfx.printf(self.caption,self.x,self.y-16,self.w,"center") 47 | end 48 | end 49 | l_gfx.setColor(cr,cg,cb,ca) 50 | end 51 | -------------------------------------------------------------------------------- /ui_scripts/ui_radiobutton.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- RadioButton component is the one that can create groups of switches with only one switched on, usage: 4 | --[[ 5 | 6 | local rb1 = RadioButton:new("RB1") 7 | local rb2 = RadioButton:new("RB2") 8 | local rb3 = RadioButton:new("RB3") 9 | 10 | local rbgroup = {rb1,rb2,rb3} 11 | 12 | for i=1,3 do 13 | rbgroup[i]:setGroup(rbgroup) 14 | end 15 | 16 | -- clicking on one radiobutton will make it checked, and every other in this group unchecked 17 | ]] 18 | 19 | RadioButton = {} 20 | RadioButton.__index = RadioButton 21 | RadioButton.ident = "ui_radiobutton" 22 | RadioButton.name = "RadioButton" 23 | RadioButton.caption = "RadioButton" 24 | RadioButton.w = 16 25 | RadioButton.h = 16 26 | RadioButton.buttonStyle = false -- if true, it will be drawn as a button 27 | function RadioButton:new(name) 28 | local self = {} 29 | setmetatable(self,RadioButton) 30 | self.group = {self} -- group array this radiobutton belongs to 31 | self.checked = false 32 | if name ~= nil then self.name = name end 33 | return self 34 | end 35 | setmetatable(RadioButton,{__index = UIElement}) 36 | 37 | 38 | function RadioButton:mousepressed(x,y,b) 39 | if self:isMouseOver(x,y) then 40 | if b == 1 then 41 | local c = #self.group 42 | if c>0 then 43 | for i=1,c do 44 | if self.group[i].name ~= self.name then 45 | self.group[i].checked = false 46 | end 47 | end 48 | end 49 | self.checked = true 50 | end 51 | self:click(b) 52 | end 53 | end 54 | 55 | 56 | -- this sets radiobutton group 57 | function RadioButton:setGroup(group) 58 | self.group = group or {self} 59 | end 60 | 61 | -- and this gives an index of a checked radiobutton in this group 62 | function RadioButton:getGroupIndex() 63 | local c = #self.group 64 | if c>0 then 65 | for i=1,c do 66 | if self.group[i].checked == true then return i end 67 | end 68 | end 69 | end 70 | 71 | function RadioButton:draw() 72 | local cr,cg,cb,ca = love.graphics.getColor() 73 | if self.buttonStyle == true then 74 | if self.checked == true then 75 | l_gfx.setColor(self.colorHighlight) 76 | else 77 | l_gfx.setColor(self.colorFill) 78 | end 79 | if self.active == false then 80 | l_gfx.setColor(self.colorDisabledFill) 81 | end 82 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 83 | if self.active == true then 84 | l_gfx.setColor(self.colorFont) 85 | else 86 | l_gfx.setColor(self.colorFill) 87 | end 88 | l_gfx.printf(self.caption,self.x,self.y+(self.h/2-7),self.w,"center") 89 | else 90 | l_gfx.setColor(self.colorHighlight) 91 | l_gfx.circle("line",self.x+8,self.y+8,8,16) 92 | if self.checked == true then 93 | l_gfx.setColor(self.colorHighlight) 94 | l_gfx.circle("fill",self.x+8,self.y+8,6,12) 95 | end 96 | if self.active == true then 97 | l_gfx.setColor(self.colorFont) 98 | else 99 | l_gfx.setColor(self.colorFill) 100 | end 101 | l_gfx.print(self.caption,self.x+20,self.y+2) 102 | end 103 | l_gfx.setColor(cr,cg,cb,ca) 104 | end 105 | 106 | -- this element is specifically for particle editor 107 | -- its buttons are colored and are highlighted with a frame upon checking 108 | RadioColorPicker = {} 109 | RadioColorPicker.__index = RadioColorPicker 110 | RadioColorPicker.ident = "ui_radiocolorpicker" 111 | RadioColorPicker.name = "RadioColorPicker" 112 | RadioColorPicker.caption = "" 113 | RadioColorPicker.colorHighlight = {192,192,192,192} 114 | function RadioColorPicker:new(name) 115 | local self = {} 116 | setmetatable(self,RadioColorPicker) 117 | self.group = {self} 118 | self.checked = false 119 | self.color = {255,255,255,255} 120 | if name ~= nil then self.name = name end 121 | return self 122 | end 123 | setmetatable(RadioColorPicker,{__index = RadioButton}) 124 | 125 | function RadioColorPicker:draw() 126 | local cr,cg,cb,ca = love.graphics.getColor() 127 | if self.checked == true then 128 | l_gfx.setColor(self.colorHighlight) 129 | else 130 | l_gfx.setColor(self.colorFill) 131 | end 132 | if self.active == false then 133 | l_gfx.setColor(self.colorDisabledFill) 134 | end 135 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 136 | 137 | if self.active == true then 138 | l_gfx.setColor(self.color) 139 | else 140 | l_gfx.setColor(self.colorDisabledFill) 141 | end 142 | l_gfx.rectangle("fill",self.x+1,self.y+1,self.w-2,self.h-2) 143 | l_gfx.setColor(cr,cg,cb,ca) 144 | end 145 | 146 | function RadioColorPicker:setColor(r,g,b,a) 147 | self.color = {r or 255,g or 255,b or 255,a or 255} 148 | end -------------------------------------------------------------------------------- /ui_scripts/ui_shape.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | 3 | -- these are various UI shapes 4 | 5 | 6 | Rectangle = {} 7 | Rectangle.__index = Rectangle 8 | Rectangle.ident = "ui_rectangle" 9 | Rectangle.name = "Rectangle" 10 | Rectangle.centerOnPos = false 11 | function Rectangle:new(name) 12 | local self = {} 13 | setmetatable(self,Rectangle) 14 | self.mode = "fill" 15 | if name ~= nil then self.name = name end 16 | return self 17 | end 18 | 19 | setmetatable(Rectangle,{__index = UIElement}) 20 | 21 | function Rectangle:draw() 22 | local cr,cg,cb,ca = love.graphics.getColor() 23 | l_gfx.setColor(self.colorFill) 24 | if self.centerOnPos == true then 25 | l_gfx.rectangle(self.mode,self.x-self.w/2,self.y-self.h/2,self.w,self.h) 26 | else 27 | l_gfx.rectangle(self.mode,self.x,self.y,self.w,self.h) 28 | end 29 | l_gfx.setColor(cr,cg,cb,ca) 30 | end 31 | 32 | 33 | 34 | Circle = {} 35 | Circle.__index = Circle 36 | Circle.ident = "ui_circle" 37 | Circle.name = "Circle" 38 | Circle.centerOnPos = false 39 | function Circle:new() 40 | local self = {} 41 | setmetatable(self,Circle) 42 | self.mode = "fill" 43 | self.radius = 32 44 | self.segments = 20 45 | if name ~= nil then self.name = name end 46 | return self 47 | end 48 | 49 | setmetatable(Circle,{__index = UIElement}) 50 | 51 | function Circle:draw() 52 | local cr,cg,cb,ca = love.graphics.getColor() 53 | l_gfx.setColor(self.colorFill) 54 | if self.centerOnPos == true then 55 | l_gfx.circle(self.mode,self.x,self.y,self.radius,self.segments) 56 | else 57 | l_gfx.circle(self.mode,self.x+self.radius,self.y+self.radius,self.radius,self.segments) 58 | end 59 | l_gfx.setColor(cr,cg,cb,ca) 60 | end 61 | 62 | Line = {} 63 | Line.__index = Line 64 | Line.ident = "ui_line" 65 | Line.name = "Line" 66 | Line.horizontal = false 67 | function Line:new(name) 68 | local self = {} 69 | setmetatable(self,Line) 70 | if name ~= nil then self.name = name end 71 | return self 72 | end 73 | 74 | setmetatable(Line,{__index = UIElement}) 75 | 76 | function Line:draw() 77 | local cr,cg,cb,ca = love.graphics.getColor() 78 | l_gfx.setColor(self.colorFill) 79 | if self.horizontal == true then 80 | l_gfx.line(self.x.self.y,self.x+self.w,self.y) 81 | else 82 | l_gfx.line(self.x,self.y,self.x,self.y+self.h) 83 | end 84 | l_gfx.setColor(cr,cg,cb,ca) 85 | end 86 | 87 | Cross = {} 88 | Cross.__index = Cross 89 | Cross.name = "Cross" 90 | Cross.ident = "ui_cross" 91 | Cross.centerOnPos = false 92 | function Cross:new(name) 93 | local self = {} 94 | setmetatable(self,Cross) 95 | 96 | if name ~= nil then self.name = name end 97 | return self 98 | end 99 | setmetatable(Cross,{__index = UIElement}) 100 | 101 | function Cross:draw() 102 | local cr,cg,cb,ca = love.graphics.getColor() 103 | local bm = l_gfx.getBlendMode() 104 | l_gfx.setBlendMode(self.blendMode) 105 | l_gfx.setColor(self.colorLine) 106 | if self.centerOnPos == true then 107 | l_gfx.line(self.x-self.w/2,self.y,self.x+self.w/2,self.y) 108 | l_gfx.line(self.x,self.y-self.h/2,self.x,self.y+self.h/2) 109 | else 110 | l_gfx.line(self.x,self.y+self.h/2,self.x+self.w,self.y+self.h/2) 111 | l_gfx.line(self.x+self.w/2,self.y,self.x+self.w/2,self.y+self.h) 112 | end 113 | l_gfx.setBlendMode(bm) 114 | l_gfx.setColor(cr,cg,cb,ca) 115 | end 116 | 117 | Arc = {} 118 | Arc.__index = Arc 119 | Arc.name = "Arc" 120 | Arc.ident = "ui_arc" 121 | Arc.centerOnPos = true 122 | Arc.mode = "fill" 123 | Arc.segments = 20 124 | function Arc:new(name) 125 | local self = {} 126 | setmetatable(self,Arc) 127 | self.a1 = 0 128 | self.a2 = 0 129 | self.radius = 16 130 | if name ~= nil then self.name = name end 131 | return self 132 | end 133 | setmetatable(Arc,{__index = UIElement}) 134 | 135 | function Arc:draw(st) 136 | local self = st or self 137 | local cr,cg,cb,ca = love.graphics.getColor() 138 | l_gfx.setColor(self.colorFill) 139 | if self.centerOnPos == true then 140 | l_gfx.arc(self.mode,self.x,self.y,self.radius,self.a1,self.a2,self.segments) 141 | else 142 | l_gfx.arc(self.mode,self.x+self.radius,self.y+self.radius,self.radius,self.a1,self.a2,self.segments) 143 | end 144 | l_gfx.setColor(cr,cg,cb,ca) 145 | end 146 | 147 | function Arc:setAngle(a1,a2) 148 | self.a1,self.a2 = a1 or self.a1,a2 or self.a2 149 | end 150 | 151 | Polygon = {} 152 | Polygon.__index = Polygon 153 | Polygon.ident = "ui_polygon" 154 | Polygon.mode = "fill" 155 | Polygon.name = "Polygon" 156 | Polygon.colorFill = {32,64,64,48} 157 | function Polygon:new(name) 158 | local self = {} 159 | setmetatable(self,Polygon) 160 | if name ~= nil then self.name = name end 161 | return self 162 | end 163 | setmetatable(Polygon,{__index = UIElement}) 164 | 165 | function Polygon:setVertices(v) 166 | if #v%2 == 0 then 167 | self.v = v 168 | end 169 | end 170 | 171 | function Polygon:setPosition(x,y) 172 | local px,py = self.x,self.y 173 | local dx,dy = x-px,y-py 174 | if self.v ~= nil then 175 | for i=1,#self.v,2 do 176 | self.v[i] = self.v[i]+dx 177 | self.v[i+1] = self.v[i+1]+dy 178 | end 179 | end 180 | end 181 | 182 | function Polygon:draw() 183 | if self.v ~= nil then 184 | local r,g,b,a = l_gfx.getColor() 185 | l_gfx.setColor(self.colorFill) 186 | l_gfx.polygon(self.mode,self.v) 187 | l_gfx.setColor(r,g,b,a) 188 | end 189 | end -------------------------------------------------------------------------------- /ui_scripts/ui_spin.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | local mouse = love.mouse 3 | local tostring = tostring 4 | local format = string.format 5 | -- this is a spin element 6 | -- currently it's only way of control is by mousewheel, wheelup increases the value by a step, wheeldown decreases 7 | -- it also has mutlipliers, like precise multiplier, base multiplier, coarse and turbo multipliers 8 | -- if you press left shift, or left ctrl or left alt while mouse over the spin element, you will switch its step multiplier and show its value in a box to the right 9 | -- multipliers are redefinable on runtime as well as you can deny it from use step multipliers at all 10 | -- its changeValue() method is fired on every increase or decrease 11 | 12 | Spin = {} 13 | Spin.__index = Spin 14 | Spin.ident = "ui_spin" 15 | Spin.leftCaption = false 16 | Spin.w = 48 17 | Spin.h = 16 18 | Spin.caption = "Spin" 19 | Spin.name = "Spin" 20 | Spin.caption_xpad = -4 21 | Spin.caption_ypad = 0 22 | Spin.updateable = true 23 | Spin.maxdec = 1 24 | function Spin:new(name) 25 | local self = {} 26 | setmetatable(self,Spin) 27 | self.value = 0 28 | self.step = 1 29 | self.step_mult = 1 30 | self.isHeld = false 31 | self.allowMult = false 32 | self.displMult = false 33 | self.mult_coarse = 10 34 | self.mult_base = 1 35 | self.mult_precise = 0.1 36 | self.mult_turbo = 100 37 | self.held_timer = 0 38 | self.max = nil 39 | self.min = nil 40 | if name ~= nil then self.name = name end 41 | return self 42 | end 43 | setmetatable(Spin,{__index = UIElement}) 44 | 45 | function Spin:click(b) 46 | if b == 1 then 47 | self.isHeld = true 48 | local mx,my = mouse.getPosition() 49 | if self:isMouseOver() then 50 | if mx>=self.x+self.w/2 then self:increment() else self:decrement() end 51 | self:changeValue() 52 | end 53 | end 54 | end 55 | 56 | function Spin:wheelmoved(x,y) 57 | local mx,my = love.mouse.getPosition() 58 | if self:isMouseOver(mx,my) then 59 | if y > 0 then 60 | self:increment() 61 | self:changeValue() 62 | elseif y < 0 then 63 | self:decrement() 64 | self:changeValue() 65 | end 66 | end 67 | end 68 | 69 | function Spin:update(dt) 70 | if self.isHeld == true then 71 | self.held_timer = self.held_timer + dt 72 | if self.held_timer>0.5 then 73 | local mx,my = mouse.getPosition() 74 | if self:isMouseOver() then 75 | if mx>=self.x+self.w/2 then self:increment() else self:decrement() end 76 | self:changeValue() 77 | end 78 | end 79 | end 80 | end 81 | 82 | function Spin:unclick(b) 83 | if b == 1 then 84 | self.isHeld = false 85 | self.held_timer = 0 86 | end 87 | end 88 | 89 | function Spin:increment() 90 | if self.max ~= nil then 91 | if self.value<=self.max-self.step*self.step_mult then self.value = self.value + self.step*self.step_mult end 92 | else 93 | self.value = self.value + self.step*self.step_mult 94 | end 95 | end 96 | 97 | function Spin:decrement() 98 | if self.min ~= nil then 99 | if self.value>=self.min+self.step*self.step_mult then self.value = self.value - self.step*self.step_mult end 100 | else 101 | self.value = self.value - self.step*self.step_mult 102 | end 103 | end 104 | 105 | function Spin:keypressed(key) 106 | if self.allowMult == true then 107 | if key == "lshift" then 108 | self.step_mult = self.mult_coarse 109 | self.displMult = true 110 | elseif key == "lctrl" then 111 | self.step_mult = self.mult_precise 112 | self.displMult = true 113 | elseif key == "lalt" then 114 | self.step_mult = self.mult_turbo 115 | self.displMult = true 116 | end 117 | end 118 | end 119 | 120 | function Spin:keyreleased(key) 121 | if key == "lshift" or key == "lctrl" or key == "lalt" then 122 | self.step_mult = self.mult_base 123 | self.displMult = false 124 | end 125 | end 126 | 127 | function Spin:draw() 128 | local cr,cg,cb,ca = love.graphics.getColor() 129 | local dynwidth = self.w 130 | local deccnt = self.maxdec 131 | local v = format("%."..deccnt.."f",self.value) 132 | if self:isMouseOver() == true then 133 | if dynwidth=self.x+dynwidth/2 then 147 | l_gfx.rectangle("fill",self.x+dynwidth/2,self.y,dynwidth/2,self.h) 148 | else 149 | l_gfx.rectangle("fill",self.x,self.y,dynwidth/2,self.h) 150 | end 151 | if self.displMult == true then 152 | local str = "x"..self.step_mult*self.step 153 | l_gfx.setColor(self.colorFill) 154 | l_gfx.rectangle("fill",self.x+dynwidth,self.y+(self.h/2-7),l_gfx.getFont():getWidth(str)+2,14) 155 | l_gfx.setColor(self.colorHighlight) 156 | l_gfx.rectangle("line",self.x+dynwidth,self.y+(self.h/2-7),l_gfx.getFont():getWidth(str),14) 157 | l_gfx.setColor(self.colorFont) 158 | l_gfx.print(str,self.x+dynwidth,self.y+(self.h/2-7)) 159 | end 160 | l_gfx.setColor(self.colorFill) 161 | else 162 | 163 | end 164 | 165 | l_gfx.setColor(self.colorFont) 166 | while l_gfx.getFont():getWidth(tostring(v))>=dynwidth do 167 | deccnt = deccnt - 1 168 | if deccnt<0 then v = "..." break end 169 | v = format("%."..deccnt.."f",self.value) 170 | end 171 | local sh = self.h/2-7 172 | l_gfx.printf(v,self.x,self.y+sh,dynwidth,"center") 173 | if self.leftCaption == true then 174 | l_gfx.print(self.caption,self.x-l_gfx:getFont():getWidth(self.caption)+self.caption_xpad,self.y+(self.h/2-7)+self.caption_ypad) 175 | else 176 | l_gfx.print(self.caption,self.x+self.caption_xpad,self.y-14+self.caption_ypad) 177 | end 178 | 179 | l_gfx.setColor(cr,cg,cb,ca) 180 | end 181 | 182 | function Spin:changeValue() end 183 | function Spin:setValue(value) self.value = value end 184 | 185 | 186 | -- Editable spin, once hovered over, receives text input 187 | SpinEdit = {} 188 | SpinEdit.__index = SpinEdit 189 | SpinEdit.ident = "ui_spinedit" 190 | SpinEdit.name = "SpinEdit" 191 | -- Yes, it does look crude, but i cannot use dot or comma as assoc.array key like arr = {. = '.'} 192 | local ac = {} 193 | ac['1'] = '1' 194 | ac['2'] = '2' 195 | ac['3'] = '3' 196 | ac['4'] = '4' 197 | ac['5'] = '5' 198 | ac['6'] = '6' 199 | ac['7'] = '7' 200 | ac['8'] = '8' 201 | ac['9'] = '9' 202 | ac['0'] = '0' 203 | ac['.'] = '.' 204 | ac[','] = '.' 205 | ac['kp1'] = '1' 206 | ac['kp2'] = '2' 207 | ac['kp2'] = '3' 208 | ac['kp3'] = '3' 209 | ac['kp4'] = '4' 210 | ac['kp5'] = '5' 211 | ac['kp6'] = '6' 212 | ac['kp7'] = '7' 213 | ac['kp8'] = '8' 214 | ac['kp9'] = '9' 215 | ac['kp0'] = '0' 216 | SpinEdit.allowedChars = ac 217 | function SpinEdit:new(name) 218 | local self = setmetatable({},SpinEdit) 219 | self.name = name or self.name 220 | self.value = 0 221 | self.step = 1 222 | self.step_mult = 1 223 | self.isHeld = false 224 | self.allowMult = false 225 | self.displMult = false 226 | self.mult_coarse = 10 227 | self.mult_base = 1 228 | self.mult_precise = 0.1 229 | self.mult_turbo = 100 230 | self.held_timer = 0 231 | self.max = nil 232 | self.min = nil 233 | return self 234 | end 235 | setmetatable(SpinEdit,{__index = Spin}) 236 | 237 | function SpinEdit:keypressed(key) 238 | if self.allowMult == true then 239 | if key == "lshift" then 240 | self.step_mult = self.mult_coarse 241 | self.displMult = true 242 | elseif key == "lctrl" then 243 | self.step_mult = self.mult_precise 244 | self.displMult = true 245 | elseif key == "lalt" then 246 | self.step_mult = self.mult_turbo 247 | self.displMult = true 248 | end 249 | end 250 | if self:isMouseOver() == true then 251 | local rk = self.allowedChars[key] 252 | if rk ~= nil then 253 | 254 | 255 | end 256 | end 257 | end 258 | 259 | function SpinEdit:keyreleased(key) 260 | if key == "lshift" or key == "lctrl" or key == "lalt" then 261 | self.step_mult = self.mult_base 262 | self.displMult = false 263 | end 264 | end 265 | -------------------------------------------------------------------------------- /ui_scripts/ui_tabbedlist.lua: -------------------------------------------------------------------------------- 1 | TabbedList = {} 2 | TabbedList.__index = TabbedList 3 | TabbedList.ident = "ui_tabbedlist" 4 | TabbedList.name = "TabbedList" 5 | TabbedList.tabCount = 2 6 | function TabbedList:new(name) 7 | local self = setmetatable({},TabbedList) 8 | self.name = name or self.name 9 | return self 10 | end -------------------------------------------------------------------------------- /ui_scripts/ui_textfield.lua: -------------------------------------------------------------------------------- 1 | local l_gfx = love.graphics 2 | local utf8 = require("utf8") 3 | 4 | TextField = {} 5 | TextField.__index = TextField 6 | TextField.name = "TextField" 7 | TextField.ident = "ui_textfield" 8 | TextField.inContext = false 9 | TextField.limit = 26 10 | TextField.align = "left" 11 | function TextField:new(name) 12 | local self = setmetatable({},TextField) 13 | self.name = name or self.name 14 | self.text = "" 15 | return self 16 | end 17 | setmetatable(TextField,{__index = UIElement}) 18 | 19 | function TextField:draw() 20 | local cr,cg,cb,ca = l_gfx.getColor() 21 | l_gfx.setColor(self.colorFill) 22 | l_gfx.rectangle("fill",self.x,self.y,self.w,self.h) 23 | l_gfx.setColor(self.colorFont) 24 | l_gfx.printf(self.text,self.x,self.y,self.w,self.align) 25 | if self.inContext == true then 26 | l_gfx.setColor(self.colorLine) 27 | l_gfx.rectangle("line",self.x,self.y,self.w,self.h) 28 | end 29 | l_gfx.setColor(cr,cg,cb,ca) 30 | end 31 | 32 | function TextField:mousepressed(x,y,b) 33 | if self:isMouseOver(x,y) then 34 | if b == "l" then 35 | self.inContext = true 36 | end 37 | self:click(b) 38 | else 39 | self.inContext = false 40 | end 41 | end 42 | 43 | function TextField:keypressed(key,isrepeat) 44 | if self.inContext == true and #self.text>0 and key == "backspace" then 45 | local l = string.len(self.text) 46 | if l>0 then 47 | self.text = string.sub(self.text,1,l-1) 48 | end 49 | end 50 | end 51 | 52 | function TextField:textinput(t) 53 | if self.inContext == true and #self.text<=self.limit then 54 | self.text = self.text .. t 55 | end 56 | end 57 | 58 | function TextField:clear() 59 | self.text = "" 60 | end -------------------------------------------------------------------------------- /ui_scripts/ui_timer.lua: -------------------------------------------------------------------------------- 1 | -- An invisible timer element 2 | -- you can define its trigger function and then start it, and after an interval it will fire that function 3 | -- if single is false it will autoreload itself 4 | 5 | Timer = {} 6 | Timer.__index = Timer 7 | Timer.ident = "ui_timer" 8 | Timer.name = "Timer" 9 | Timer.updateable = true 10 | function Timer:new(name) 11 | local self = {} 12 | setmetatable(self,Timer) 13 | if name ~= nil then self.name = name end 14 | self.interval = 1 15 | self.t = 1 16 | self.isRunning = false 17 | self.single = true 18 | return self 19 | end 20 | setmetatable(Timer,{__index = Element}) 21 | 22 | function Timer:start() self.isRunning = true self.t = self.interval end 23 | function Timer:update(dt) 24 | if self.isRunning == true then 25 | self.t = self.t-dt 26 | if self.t<=0 then 27 | self.t = self.interval 28 | self:trigger() 29 | if self.single == true then 30 | self:pause() 31 | end 32 | end 33 | end 34 | end 35 | function Timer:pause() self.isRunning = false end 36 | function Timer:resume() self.isRunning = true end 37 | function Timer:stop() self.isRunning = false self.t = self.interval end 38 | function Timer:trigger() end -------------------------------------------------------------------------------- /ui_scripts/ui_uielement.lua: -------------------------------------------------------------------------------- 1 | -- this is a definition of drawable UI element 2 | local l_gfx = love.graphics 3 | 4 | UIElement = {} 5 | UIElement.__index = UIElement 6 | UIElement.x = 0 7 | UIElement.y = 0 8 | UIElement.w = 32 9 | UIElement.h = 32 10 | UIElement.visible = true -- UI manager will not draw this element if false 11 | UIElement.active = true -- UI manager will not update, will not handle inputs for this element if false 12 | UIElement.drawable = true -- initial definition: elements created with this flag true are inserted into UIManager's drawing loop 13 | UIElement.input = true 14 | UIElement.updateable = false 15 | UIElement.colorFill = {128,128,128,128} -- default fill color, you can change it in definition or at runtime for specific instance 16 | UIElement.colorDisabledFill = {32,32,32,128} 17 | UIElement.colorLine = {192,192,192,128} 18 | UIElement.colorFont = {255,255,255,255} 19 | UIElement.colorHardFill = {64,64,64,255} 20 | UIElement.colorHighlight = {192,192,192,128} 21 | UIElement.ident = "ui_uielement" 22 | UIElement.name = "UIElement" 23 | UIElement.caption_xpad = 0 -- if element has caption in it, it will draw caption with shift text's position with these coordinates 24 | UIElement.caption_ypad = 0 25 | UIElement.blendMode = "alpha" -- can be used for element to change its blend mode while drawing 26 | UIElement.font = l_gfx:getFont() 27 | function UIElement:new(name) 28 | local self = {} 29 | setmetatable(self,UIElement) 30 | if name ~= nil then self.name = name end 31 | return self 32 | end 33 | setmetatable(UIElement,{__index = Element}) -- drawable UIElement inherits stuff from Element 34 | 35 | function UIElement:update(dt) end 36 | function UIElement:draw() end -- since its a drawable class, it should have its draw method %) 37 | function UIElement:mousepressed(x,y,b) if self:isMouseOver(x,y) then return self:click(b) end end 38 | function UIElement:mousereleased(x,y,b) if self:isMouseOver(x,y) then return self:unclick(b) end end 39 | function UIElement:mousemoved(x,y,dx,dy) if self:isMouseOver(x,y) then self:hover() end end 40 | function UIElement:hover() end -- currently doesnt have its uses, maybe one day 41 | function UIElement:click(b) end -- this function defines your element's behaviour when user clicks on it 42 | function UIElement:unclick(b) end -- ... and when releases a mouse button 43 | function UIElement:isMouseOver(x,y) -- checks whether or not mouse pointer touches element's box (from its x,y position up to its width and height) 44 | local mx,my = love.mouse:getPosition() 45 | x = x or mx 46 | y = y or my 47 | if x>=self.x and x<=self.x+self.w and y>=self.y and y<=self.y+self.h then 48 | return true 49 | else 50 | return false 51 | end 52 | end 53 | function UIElement:hide(act) self.visible = false self.active = act or false end -- hides an element and makes it inactive if argument is not specified 54 | function UIElement:show(act) self.visible = true self.active = act or true end -- shows an element and makes it active if argument is not specified 55 | function UIElement:setPosition(x,y) self.x = x or self.x self.y = y or self.y end -- sets element's position 56 | function UIElement:setSize(w,h) self.w = w or self.w self.h = h or self.h end -- sets element box size 57 | function UIElement:getPosition() return self.x,self.y end 58 | 59 | 60 | -------------------------------------------------------------------------------- /utils.lua: -------------------------------------------------------------------------------- 1 | -- As a more advanced solution, we can write an iterator that traverses a table following the order of its keys. 2 | -- An optional parameter f allows the specification of an alternative order. It first sorts the keys into an array, 3 | -- and then iterates on the array. At each step, it returns the key and value from the original table: 4 | function pairsByKeys (t, f) 5 | local a = {} 6 | for n in pairs(t) do table.insert(a, n) end 7 | table.sort(a, f) 8 | local i = 0 -- iterator variable 9 | local iter = function () -- iterator function 10 | i = i + 1 11 | if a[i] == nil then 12 | return nil 13 | else 14 | return a[i], t[a[i]] 15 | end 16 | end 17 | return iter 18 | end 19 | 20 | -- Compatibility: Lua-5.1 21 | function split(str, pat) 22 | local t = {} -- NOTE: use {n = 0} in Lua-5.0 23 | local fpat = "(.-)" .. pat 24 | local last_end = 1 25 | local s, e, cap = str:find(fpat, 1) 26 | while s do 27 | if s ~= 1 or cap ~= "" then 28 | table.insert(t,cap) 29 | end 30 | last_end = e+1 31 | s, e, cap = str:find(fpat, last_end) 32 | end 33 | if last_end <= #str then 34 | cap = str:sub(last_end) 35 | table.insert(t, cap) 36 | end 37 | return t 38 | end 39 | 40 | -- serialize table 41 | local DUMP_IGNORE = {os = 1, io = 1, table = 1, _VERSION = 1, math = 1, love = 1, string = 1, package = 1, _G = 1, DUMP_IGNORE = 1, ndump = 1, xpcall = 1, unpack = 1, type = 1, require = 1, setmetatable = 1, next = 1, pairs = 1, ipairs = 1, dofile = 1, collectgarbage = 1, load = 1, loadfile = 1, loadstring = 1, module = 1, pcall = 1, gcinfo = 1, getmetatable = 1, error = 1, debug = 1, coroutine = 1, assert = 1, tonumber = 1, tostring = 1, setfenv = 1, arg = 1, argv = 1, dumpAll = 1, dumpUserData = 1, getfenv = 1, newproxy = 1, select = 1} 42 | function ndump(object, map, visited, prefix, ignoredMap) 43 | map = map or {} 44 | ignoredMap = ignoredMap or {} 45 | visited = visited or {} 46 | if object ~= nil and visited[object] == nil then 47 | visited[object] = true 48 | for k, v in pairs(object) do 49 | local useMap = not prefix and DUMP_IGNORE[tostring(k)] ~= nil and ignoredMap or map 50 | local child = nil 51 | local vtype = type(v) 52 | k = tostring(k) 53 | if vtype == "table" then 54 | child = v 55 | v = "{}" 56 | elseif vtype == "string" then 57 | v = "\"" .. string.gsub(string.gsub(v, "\\", "\\\\"),"\n", "\\n") .. "\"" 58 | elseif vtype == "function" then 59 | v = (prefix and (prefix .. "[\"" .. k .. "\"]") or k) .. " or function() end" 60 | elseif vtype == "userdata" then 61 | v = "{} --[[ " .. tostring(v) .. "]]--" 62 | end 63 | table.insert(useMap, (prefix and (prefix .. "[\"" .. k .. "\"]") or k) .. " = " .. tostring(v)) 64 | if child then 65 | ndump(child, useMap, visited, (prefix and (prefix .. "[\"" .. k .. "\"]") or k)) 66 | end 67 | end 68 | end 69 | if not prefix then 70 | local sf = function(a, b) 71 | return string.lower(a) < string.lower(b) 72 | end 73 | table.sort(map, sf) 74 | table.sort(ignoredMap, sf) 75 | return table.concat(map, "\n"), "-- " .. table.concat(ignoredMap, "\n-- ") 76 | end 77 | end 78 | --------------------------------------------------------------------------------