Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --Made By Maticzpl
- --is it CGI?
- --"Yeah idk it just reverses stacks
- -- I think it's fine" - LBPhacker
- --KEYS:
- -- CTRL + T Create a new animation / Finish animating
- -- CTRL + ENTER Edit existing animation
- -- , (comma) Add new empty frame
- -- . (dot) Duplicate current frame
- -- Left / Right Arrow - Navigate frames
- -- Backspace - Delete current frame
- if GooAnim then return end
- GooAnim = {
- started = false,
- frameCount = 0,
- currentFrame = 0,
- frameInterval = 5,
- usedType = 0,
- safeLimit = 0,
- loadingMode = false,
- partsPrev = 999999,
- partsNow = 999999,
- animSize = {x = 0,y = 0},
- offset = {x = 10,y = 10},
- frameData = {},
- version = 2
- }
- local g = GooAnim
- function GooAnim.createAnimation(sizex,sizey)
- if g.started then
- local res = tpt.confirm("Start new animation?", "Do you want to abandon the current animation and start a new one?","Yes")
- if not res then return end
- end
- tpt.set_pause(1)
- tpt.decorations_enable(1)
- g.frameCount = 1
- g.frameData = {}
- g.animSize.x = sizex
- g.animSize.y = sizey
- g.started = true
- sim.createBox( g.offset.x,
- g.offset.y,
- g.offset.x + g.animSize.x,
- g.offset.y + g.animSize.y,
- g.usedType
- )
- end
- -- function GooAnim.showFrameCounter()
- -- print("Frame: "..g.currentFrame.. " / "..(g.frameCount - 1).." (max "..(g.safeLimit - 1)..")")
- -- end
- function GooAnim.saveFrame(remove)
- local frame = {}
- for x = g.offset.x, g.animSize.x + g.offset.x, 1 do
- for y = g.offset.y, g.animSize.y + g.offset.y, 1 do
- local rx = x - g.offset.x
- local ry = y - g.offset.y
- local part = sim.partID(x,y)
- if part == nil then
- print(x,y)
- end
- local deco = sim.partProperty(part,'dcolour')
- if frame[ry] == nil then
- frame[ry] = {}
- end
- frame[ry][rx] = deco
- if remove then
- sim.partKill(part)
- end
- end
- end
- g.frameData[g.currentFrame] = frame
- end
- function GooAnim.loadFrame(frameIndex)
- for x = g.offset.x, g.animSize.x + g.offset.x, 1 do
- for y = g.offset.y, g.animSize.y + g.offset.y, 1 do
- local rx, ry = x - g.offset.x , y - g.offset.y
- local frame = g.frameData[frameIndex]
- if frame[ry] == nil or frame[ry][rx] == nil then
- local part = sim.partCreate(-3,x,y,elem.DEFAULT_PT_BRCK)
- else
- local deco = frame[ry][rx]
- local part = sim.partCreate(-3,x,y,g.usedType)
- sim.partProperty(part,'dcolour',deco)
- end
- end
- end
- end
- -- Thanks to mad-cow for this function!
- local function GetAllPartsInRegion(x1, y1, x2, y2)
- -- builts a map of particles in a region.
- -- WARN: Misbehaves if partile order hasn't been reloaded since it relies on sim.parts()
- -- Save the returned value and provide it to .GetAllPartsInPos(x,y,region) to reduce computational complexity
- -- or you can just index the returned value youself, idc
- local result = {}
- local width = sim.XRES
- if x2 < x1 then
- x1,x2 = x2,x1
- end
- if y2 < y1 then
- y1,y2 = y2,y1
- end
- for part in sim.parts() do
- local px, py = sim.partPosition(part)
- px = math.floor(px+0.5) -- Round pos
- py = math.floor(py+0.5)
- local idx = math.floor(px + (py * width))
- if px >= x1 and px <= x2 and py >= y1 and py <= y2 then
- if not result[idx] then
- result[idx] = {}
- end
- table.insert(result[idx], part)
- end
- end
- return result
- end
- -- Yes, i did just copy those from my subframe chipmaker script
- local function ReorderParticles()
- local particles = {}
- local width = sim.XRES
- for part in sim.parts() do
- local x = math.floor(sim.partProperty(part,'x') + 0.5);
- local y = math.floor(sim.partProperty(part,'y') + 0.5);
- local particleData = {}
- particleData.type = sim.partProperty(part,'type');
- particleData.temp = sim.partProperty(part,'temp');
- particleData.ctype = sim.partProperty(part,'ctype');
- particleData.tmp = sim.partProperty(part,'tmp');
- particleData.tmp2 = sim.partProperty(part,'tmp2');
- particleData.tmp3 = sim.partProperty(part,'pavg0');
- particleData.tmp4 = sim.partProperty(part,'pavg1');
- particleData.life = sim.partProperty(part,'life');
- particleData.vx = sim.partProperty(part,'vx');
- particleData.vy = sim.partProperty(part,'vy');
- particleData.dcolour = sim.partProperty(part,'dcolour');
- particleData.flags = sim.partProperty(part,'flags');
- local index = math.floor(x + (y * width))
- if particles[index] == nil then
- particles[index] = {}
- end
- table.insert(particles[index],particleData)
- --particles[index][#particles[index]] = particleData
- sim.partKill(part)
- end
- for i = sim.XRES * sim.YRES, 0, -1 do
- local stack = particles[i]
- if stack ~= nil then
- for j = #stack, 1, -1 do
- local part = stack[j]
- local x = math.floor(i % sim.XRES)
- local y = math.floor((i - x) / sim.XRES)
- local id = sim.partCreate(-3,x,y,28)
- sim.partProperty(id,'type',part.type);
- sim.partProperty(id,'temp',part.temp);
- sim.partProperty(id,'ctype',part.ctype);
- sim.partProperty(id,'tmp',part.tmp);
- sim.partProperty(id,'tmp2',part.tmp2);
- sim.partProperty(id,'pavg0',part.tmp3);
- sim.partProperty(id,'pavg1',part.tmp4);
- sim.partProperty(id,'life',part.life);
- sim.partProperty(id,'vx',part.vx);
- sim.partProperty(id,'vy',part.vy);
- sim.partProperty(id,'dcolour',part.dcolour)
- sim.partProperty(id,'flags',part.flags);
- end
- end
- end
- end
- local function ArrayGCD(arr)
- local function gcd(a, b)
- if (a == 0) then
- return b;
- end
- return gcd(b % a, a);
- end
- local result = arr[1];
- for i = 2, #arr, 1 do
- result = gcd(arr[i], result);
- if(result == 1) then
- return 1;
- end
- end
- return result;
- end
- function GooAnim.toFrame(frameIndex)
- if frameIndex < 0 or frameIndex >= GooAnim.frameCount then
- return
- end
- GooAnim.saveFrame(true)
- g.currentFrame = frameIndex
- GooAnim.loadFrame(frameIndex)
- end
- function GooAnim.nextFrame(clone,placeAfterCurrent)
- GooAnim.saveFrame(not clone)
- if placeAfterCurrent then
- g.currentFrame = g.currentFrame + 1
- for i = g.frameCount - 1, g.currentFrame, -1 do
- g.frameData[i+1] = g.frameData[i]
- end
- else
- g.currentFrame = g.frameCount
- end
- if not clone then
- sim.createBox( g.offset.x,
- g.offset.y,
- g.offset.x + g.animSize.x,
- g.offset.y + g.animSize.y,
- g.usedType
- )
- end
- g.frameCount = g.frameCount + 1
- end
- function GooAnim.deleteFrame(frameIndex)
- if g.frameCount < 2 then
- print("Can't delete this frames")
- return
- end
- if g.currentFrame >= g.frameCount - 1 then
- g.currentFrame = g.currentFrame - 1
- end
- g.frameCount = g.frameCount - 1
- if frameIndex == 0 then
- for i = 0, g.frameCount - 1, 1 do
- g.frameData[i] = g.frameData[i+1]
- end
- g.frameData[g.frameCount] = nil
- else
- table.remove(g.frameData,frameIndex)
- end
- sim.clearRect(g.offset.x,g.offset.y,g.animSize.x+1,g.animSize.y+1)
- g.loadFrame(g.currentFrame)
- end
- function GooAnim.finishAnimation()
- local confirm = tpt.confirm("Finish animation?", "Do you want to finish the animation?","Yes")
- if not confirm then
- return
- end
- GooAnim.saveFrame()
- sim.clearSim()
- sim.createWallBox(g.offset.x, g.offset.y, g.animSize.x + g.offset.x, g.animSize.y + g.offset.y, 12)
- for y = g.offset.y, g.animSize.y + g.offset.y, 1 do
- for x = g.offset.x, g.animSize.x + g.offset.x, 1 do
- for i = g.frameCount - 1, 0 , -1 do
- local frame = g.frameData[i]
- if frame ~= nil then
- local rx = x - g.offset.x
- local ry = y - g.offset.y
- local deco = frame[ry][rx]
- local part = nil
- local prev = 1
- if i-prev >= 0 then
- while true do
- while i-prev >= 0 and g.frameData[i-prev] == nil do
- prev = prev + 1
- end
- if i-prev < 0 then
- part = sim.partCreate(-3,x,y,g.usedType)
- sim.partProperty(part,'dcolour',deco)
- break
- end
- if g.frameData[i-prev][ry][rx] == deco then
- break
- else
- part = sim.partCreate(-3,x,y,g.usedType)
- sim.partProperty(part,'dcolour',deco)
- break
- end
- end
- else
- part = sim.partCreate(-3,x,y,g.usedType)
- sim.partProperty(part,'dcolour',deco)
- end
- if part ~= nil then
- local lasting = 1
- if g.frameData[i+lasting] ~= nil then
- while g.frameData[i+lasting][ry][rx] == deco do
- lasting = lasting + 1
- if g.frameData[i+lasting] == nil then
- break
- end
- end
- end
- sim.partProperty(part,'life',(i + lasting) * g.frameInterval);
- end
- end
- end
- end
- end
- GooAnim.started = false
- sim.takeSnapshot()
- sim.saveStamp(g.offset.x,g.offset.y,g.animSize.x,g.animSize.y)
- print("Animation saved in stamps")
- end
- function GooAnim.loadAnimation()
- print("Loading animation")
- g.offset = nil
- local furthestX = 0
- local furthestY = 0
- local maxLife = 0
- local allLifeValues = {}
- for part in sim.parts() do
- local x,y = sim.partPosition(part)
- x = math.floor(x)
- y = math.floor(y)
- if sim.partProperty(part,'life') > maxLife then
- maxLife = sim.partProperty(part,'life')
- end
- table.insert(allLifeValues,sim.partProperty(part,'life'))
- if x > furthestX then
- furthestX = x
- end
- if y > furthestY then
- furthestY = y
- end
- if g.offset == nil then
- g.offset = {x=x,y=y}
- end
- end
- g.frameCount = 0
- g.animSize.x = furthestX - g.offset.x
- g.animSize.y = furthestY - g.offset.y
- g.started = true
- g.frameData = {}
- g.safeLimit = math.floor(235008 / (tonumber(g.animSize.x) * tonumber(g.animSize.y)))
- ReorderParticles()
- local stacks = GetAllPartsInRegion(g.offset.x,g.offset.y,furthestX,furthestY)
- g.frameInterval = ArrayGCD(allLifeValues)
- g.frameCount = maxLife / g.frameInterval
- for k, stack in pairs(stacks) do
- local rx, ry = sim.partPosition(stack[0] or stack[1])
- rx = math.floor(rx + 0.5) - g.offset.x
- ry = math.floor(ry + 0.5) - g.offset.y
- local frames = 0
- local offset = 0
- for j, part in pairs(stack) do
- offset = offset - 1
- local part = stack[#stack - j + 1]
- g.usedType = sim.partProperty(part,'type')
- local life = sim.partProperty(part,'life')
- local toSkip = math.floor(life / g.frameInterval) - j - (offset + 1)
- for s = 0, toSkip, 1 do
- offset = offset + 1
- if g.frameData[j-1+offset] == nil then
- g.frameData[j-1+offset] = {}
- end
- if g.frameData[j-1+offset][ry] == nil then
- g.frameData[j-1+offset][ry] = {}
- end
- if g.frameData[j-1+offset][ry][rx] == nil then
- g.frameData[j-1+offset][ry][rx] = sim.partProperty(part,'dcolour')
- end
- end
- end
- if frames > g.frameCount then
- g.frameCount = frames
- end
- end
- sim.clearSim()
- local prevInterval = g.frameInterval
- g.frameInterval = tonumber(tpt.input("Enter New Frame Interval","Do you want to change the frame interval?",g.frameInterval))
- if g.frameInterval == nil then
- g.frameInterval = prevInterval
- end
- g.currentFrame = 0
- GooAnim.loadFrame(0)
- end
- local function bindKeys()
- event.register(event.keypress, function(key,scan,rpt,shift,ctrl,alt)
- if shift or alt then return end
- if GooAnim.started and not ctrl then
- if key == 44 and not rpt then -- < to next frame
- local afterCurrent = false
- if g.currentFrame ~= g.frameCount - 1 then
- afterCurrent = tpt.confirm("Where to put the frame?","Click 'Cancel' to put the frame as a new frame at the end. Click 'Ok' to insert a new frame after this one.","Ok")
- end
- GooAnim.nextFrame(false,afterCurrent)
- return false
- end
- if key == 46 and not rpt then -- > to clone frame
- local afterCurrent = false
- if g.currentFrame ~= g.frameCount - 1 then
- afterCurrent = tpt.confirm("Where to put the frame?","Click 'Cancel' to put the frame as a new frame at the end. Click 'Ok' to insert a new frame after this one.","Ok")
- end
- GooAnim.nextFrame(true,afterCurrent)
- return false
- end
- if key == 1073741903 then -- right arrow
- GooAnim.toFrame(GooAnim.currentFrame + 1 )
- end
- if key == 1073741904 then -- left arrow
- GooAnim.toFrame(GooAnim.currentFrame - 1 )
- end
- if key == 8 and not rpt then --Backspace
- GooAnim.deleteFrame(g.currentFrame)
- end
- end
- if key == 13 and ctrl and not GooAnim.started then -- CTRL + ENTER
- if tpt.confirm("Is the simulation clear?","Make sure the simulation is clear of everything except the animation you want to edit.","Yes") then
- GooAnim.loadAnimation()
- end
- end
- if key == 116 and ctrl then --CTRL + T to Start / Stop animation
- if GooAnim.started then
- GooAnim.finishAnimation()
- else
- if not tpt.confirm("New animation", "The whole simulation will be cleared, proceed?","Yes") then
- return
- end
- sim.clearSim()
- local x = tpt.input("Enter Width","Enter the target resolution for your animation",25)
- if x == "" then
- return
- end
- local y = tpt.input("Enter Height","Enter the target resolution for your animation",math.floor((x / 16) * 9))
- if y == "" then
- return
- end
- GooAnim.frameInterval = tpt.input("Enter Frame Interval","Enter the delay (in game frames) between an animation frame changes",5)
- local type = tpt.input("Enter Type","What particle type to use (other types than GOO can break the script)","GOO")
- type = string.upper(type)
- if elem["DEFAULT_PT_"..type] ~= nil then
- GooAnim.usedType = elem["DEFAULT_PT_"..type]
- else
- tpt.message_box("Wrong Type","The type '"..type.."' is not found")
- return
- end
- -- part limit around 235008
- GooAnim.safeLimit = math.floor(235008 / (tonumber(x) * tonumber(y)))
- if tonumber(x) * tonumber(y) >= 40000 then
- tpt.message_box("Warning","Big resolution animations may exceed the particle limit")
- end
- GooAnim.createAnimation(x,y)
- end
- return false
- end
- end)
- event.register(event.tick, function ()
- if g.started then
- local text = "Frame: "..g.currentFrame.. " / "..(g.frameCount - 1).." (max "..(g.safeLimit - 1).." worst case scenario)"
- local w,h = gfx.textSize(text)
- gfx.drawText(sim.XRES / 2 - (w / 2),sim.YRES - 50,text,255,255,255,255)
- local text = "[,] - New Empty Frame [.] - Clone Frame [CTRL + T] - Finish Animating"
- local w,h = gfx.textSize(text)
- gfx.drawText(sim.XRES / 2 - (w / 2),sim.YRES - 35,text,255,255,255,255)
- local text = "[<- / ->] - Change current frame [Backspace] - Delete current frame"
- local w,h = gfx.textSize(text)
- gfx.drawText(sim.XRES / 2 - (w / 2),sim.YRES - 20,text,255,255,255,255)
- end
- end)
- end
- bindKeys()
- -- TODO:
- -- Accurate max frame limit
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement