Advertisement
Guest User

npaintpro

a guest
Oct 18th, 2012
967
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 27.42 KB | None | 0 0
  1. local w,h = term.getSize()
  2. local inMenu = false
  3. local animated = false
  4.  
  5. local column = {}
  6. local lSel,rSel = colours.white,nil
  7. local sx,sy = 0,0
  8. --Change this to change default canvas colour
  9. local alphaC = colours.cyan
  10. local sFrame = 1
  11. local frames = { }
  12. local buffer = nil
  13. local selectrect = nil
  14. local rectblink = 0
  15.  
  16. local frameCount = 1
  17. local state = "paint"
  18. local brushsize = 3
  19. local record = false
  20. local animtime = 0.3
  21.  
  22. local hexnums = {[10] = "a", [11] = "b", [12] = "c", [13] = "d", [14] = "e" , [15] = "f"}
  23.  
  24. local logo = {
  25. "fcc              3   339";
  26. " fcc          9333    33";
  27. "  fcc        933 333  33";
  28. "   fcc       933  33  33";
  29. "    fcc      933   33 33";
  30. "     c88     333   93333";
  31. "     888     333    9333";
  32. "      333 3  333     939";
  33. }
  34. local mChoices = {"Save","Exit"}
  35. local helpTopics = {
  36.     [1] = {
  37.         name = "Paint Mode",
  38.         key = nil,
  39.         animonly = false,
  40.         message = "The default mode for NPaintPro, for painting pixels."
  41.         .." Controls here that are not overridden will apply for all other modes. Leaving a mode by selecting that mode "
  42.         .." again will always send the user back to paint mode.",
  43.         controls = {
  44.             { "Arrow keys", "Scroll the canvas" },
  45.             { "Left Click", "Paint/select left colour" },
  46.             { "Right Click", "Paint/select right colour" },
  47.             { "X Key", "Clear image on screen" },
  48.             { "Tab Key", "Hide selection rectangle if visible" },
  49.             { "Q Key", "Set alpha mask to left colour" },
  50.             { "W Key", "Set alpha mask to right colour" },
  51.             { "Number Keys", "Swich between frames 1-9" },
  52.             { "</> keys", "Move to the next/last frame" },
  53.             { "R Key", "Removes every frame after the current frame"}
  54.         }
  55.     },
  56.     [2] = {
  57.         name = "Brush Mode",
  58.         key = "b",
  59.         animonly = false,
  60.         message = "Brush mode allows painting a circular area of variable diameter rather than a single pixel, working in "..
  61.         "the exact same way as paint mode in all other regards.",
  62.         controls = {
  63.             { "Left Click", "Paints a brush blob with the left colour" },
  64.             { "Right Click", "Paints a brush blob with the right colour" },
  65.             { "Number Keys", "Changes the radius of the brush blob from 2-9" }
  66.         }
  67.     },
  68.     [3] = {
  69.         name = "Pippette Mode",
  70.         key = "p",
  71.         animonly = false,
  72.         message = "Pippette mode allows the user to click the canvas and set the colour clicked to the left or right "..
  73.         "selected colour, for later painting.",
  74.         controls = {
  75.             { "Left Click", "Sets clicked colour to the left selected colour" },
  76.             { "Right Click", "Sets clicked colour to the right selected colour" }
  77.         }
  78.     },
  79.     [4] = {
  80.         name = "Move Mode",
  81.         key = "m",
  82.         animonly = false,
  83.         message = "Mode mode allows the moving of the entire image on the screen. This is especially useful for justifying"..
  84.         " the image to the top-left for animations or game assets.",
  85.         controls = {
  86.             { "Left/Right Click", "Moves top-left corner of image to selected square" },
  87.             { "Arrow keys", "Moves image one pixel in any direction" }
  88.         }
  89.     },
  90.     [5] = {
  91.         name = "Flood Mode (NYI)",
  92.         key = "f",
  93.         animonly = false,
  94.         message = "Flood mode allows the changing of an area of a given colour to that of the selected colour. "..
  95.         "The tool uses a flood4 algorithm and will not fill diagonally. Transparency cannot be flood filled.",
  96.         controls = {
  97.             { "Left Click", "Flood fills selected area to left colour" },
  98.             { "Right Click", "Flood fills selected area to right colour" }
  99.         }
  100.     },
  101.     [6] = {
  102.         name = "Select Mode",
  103.         key = "s",
  104.         animonly = false,
  105.         message = "Select mode allows the creation and use of the selection rectangle, to highlight specific areas on "..
  106.         "the screen and perform operations on the selected area of the image. The selection rectangle can contain an "..
  107.         "image on the clipboard- if it does, the image will flash inside the rectangle, and the rectangle edges will "..
  108.         "be light grey instead of dark grey.",
  109.         controls = {
  110.             { "C Key", "Copy: Moves selection into the clipboard" },
  111.             { "X Key", "Cut: Clears canvas under the rectangle, and moves it into the clipboard" },
  112.             { "V Key", "Paste: Copys clipboard to the canvas" },
  113.             { "Z Key", "Empties clipboard" },
  114.             { "Left/Right Click", "Moves top-left corner of rectangle to selected pixel" },
  115.             { "Arrow Keys", "Moves rectangle one pixel in any direction" }
  116.         }
  117.     },
  118.     [7] = {
  119.         name = "Corner Select Mode",
  120.         key = nil,
  121.         animonly = false,
  122.         message = "If a selection rectangle isn't visible, this mode is selected automatically. It allows the "..
  123.         "defining of the corners of the rectangle- one the top-left and bottom-right corners have been defined, "..
  124.         "NPaintPro switches to selection mode. Note rectangle must be at least 2 pixels wide and high.",
  125.         controls = {
  126.             { "Left/Right Click", "Defines a corner of the selection rectangle" }
  127.         }
  128.     },
  129.     [8] = {
  130.         name = "Play Mode",
  131.         key = "space",
  132.         animonly = true,
  133.         message = "Play mode will loop through each frame in your animation at a constant rate. Editing tools are "..
  134.         "locked in this mode, and the coordinate display will turn green to indicate it is on.",
  135.         controls = {
  136.             { "</> Keys", "Increases/Decreases speed of the animation" },
  137.             { "Space Bar", "Returns to paint mode" }
  138.         }
  139.     },
  140.     [9] = {
  141.         name = "Record Mode",
  142.         key = "\\",
  143.         animonly = true,
  144.         message = "Record mode is not a true mode, but influences how other modes work. Changes made that modify the "..
  145.         "canvas in record mode will affect ALL frames in the animation. The coordinates will turn red to indicate that "..
  146.         "record mode is on.",
  147.         controls = {
  148.             { "", "Affects:" },
  149.             { "- Paint Mode", "" },
  150.             { "- Brush Mode", "" },
  151.             { "- Cut and Paste in Select Mode", ""},
  152.             { "- Move Mode", ""}
  153.         }
  154.     },
  155.     [10] = {
  156.         name = "File Mode",
  157.         keys = "ctrl",
  158.         animonly = false,
  159.         message = "Allows you to save changes or leave the program.",
  160.         controls = { }
  161.     }
  162. }
  163. local selectedHelp = nil
  164.  
  165. local toplim,botlim,leflim,riglim = nil,nil,nil,nil
  166.  
  167. local sPath = nil
  168.  
  169. --[[  Helpers  ]]--
  170.  
  171. local function getHexOf(colour)
  172.     if not colour then return " " end
  173.     local val = math.log(colour)/math.log(2)
  174.     if val > 9 then val = hexnums[val] end
  175.     return val
  176. end
  177.  
  178. local function getColourOf(hex)
  179.     if hex == " " then return nil end
  180.     local val = tonumber(hex)
  181.     if not val then
  182.         for k,v in pairs(hexnums) do
  183.             if v == hex then val = math.pow(2,k) end
  184.         end
  185.     else
  186.         val = math.pow(2,val)
  187.     end
  188.     return val
  189. end
  190.  
  191. local function updateImageLims()
  192.     toplim,botlim,leflim,riglim = nil,nil,nil,nil
  193.    
  194.     for y,_ in pairs(frames[sFrame]) do
  195.         for x,_ in pairs(frames[sFrame][y]) do
  196.             if frames[sFrame][y][x] ~= nil then
  197.                 if leflim == nil or x < leflim then leflim = x end
  198.                 if toplim == nil or y < toplim then toplim = y end
  199.                 if riglim == nil or x > riglim then riglim = x end
  200.                 if botlim == nil or y > botlim then botlim = y end
  201.             end
  202.         end
  203.     end
  204. end
  205.  
  206. local function updateTimer()
  207.     os.startTimer(0.5)
  208.     rectblink = (rectblink % 2) + 1
  209. end
  210.  
  211. --[[  Loading  ]]--
  212.  
  213. local function loadNFP(path)
  214.     sFrame = 1
  215.     frames[sFrame] = { }
  216.     if fs.exists(path) then
  217.         local file = io.open(path, "r" )
  218.         local sLine = file:read()
  219.         local num = 1
  220.         while sLine do
  221.             table.insert(frames[sFrame], num, {})
  222.             for i=1,#sLine do
  223.                 frames[sFrame][num][i] = getColourOf(string.sub(sLine,i,i))
  224.             end
  225.             num = num+1
  226.             sLine = file:read()
  227.         end
  228.         file:close()
  229.     end
  230. end
  231.  
  232. local function saveNFP(path)
  233.     local file = io.open(path, "w")
  234.     updateImageLims()
  235.     if not toplim then
  236.         file:close()
  237.         return
  238.     end
  239.     for y=1,botlim do
  240.         local line = ""
  241.         if frames[sFrame][y] then
  242.             for x=1,riglim do
  243.                 line = line..getHexOf(frames[sFrame][y][x])
  244.             end
  245.         end
  246.         file:write(line.."\n")
  247.     end
  248.     file:close()
  249. end
  250.  
  251. local function loadNFA(path)
  252.     frames[sFrame] = { }
  253.     if fs.exists(path) then
  254.         local file = io.open(path, "r" )
  255.         local sLine = file:read()
  256.         local num = 1
  257.         while sLine do
  258.             table.insert(frames[sFrame], num, {})
  259.             if sLine == "~" then
  260.                 sFrame = sFrame + 1
  261.                 frames[sFrame] = { }
  262.                 num = 1
  263.             else
  264.                 for i=1,#sLine do
  265.                     frames[sFrame][num][i] = getColourOf(string.sub(sLine,i,i))
  266.                 end
  267.                 num = num+1
  268.             end
  269.             sLine = file:read()
  270.         end
  271.         file:close()
  272.     end
  273.     framecount = sFrame
  274.     sFrame = 1
  275. end
  276.  
  277. local function saveNFA(path)
  278.     local file = io.open(path, "w")
  279.     updateImageLims()
  280.     if not toplim then
  281.         file:close()
  282.         return
  283.     end
  284.     for i=1,#frames do
  285.         for y=1,botlim do
  286.             local line = ""
  287.             if frames[i][y] then
  288.                 for x=1,riglim do
  289.                     line = line..getHexOf(frames[i][y][x])
  290.                 end
  291.             end
  292.             file:write(line.."\n")
  293.         end
  294.         if i < #frames then file:write("~\n") end
  295.     end
  296.     file:close()
  297. end
  298.  
  299. local function init()
  300.     if animated then loadNFA(sPath)
  301.     else loadNFP(sPath) end
  302.  
  303.     for i=0,15 do
  304.         table.insert(column, math.pow(2,i))
  305.     end
  306. end
  307.  
  308. --[[  Drawing  ]]--
  309.  
  310. local function drawLogo()
  311.     term.setBackgroundColour(colours.white)
  312.     term.clear()
  313.     for y=1,#logo do
  314.         for x=1,#logo[y] do
  315.             term.setCursorPos(w/2 - #logo[y]/2 + x, h/2 - #logo/2 + y)
  316.             local col = getColourOf(string.sub(logo[y], x, x))
  317.             if not col then col = 1 end
  318.             term.setBackgroundColour(col)
  319.             term.write(" ")
  320.         end
  321.     end
  322.     sleep(1)
  323.     term.setBackgroundColour(colours.white)
  324.     term.setTextColour(colours.black)
  325.     local msg = "NPaintPro"
  326.     term.setCursorPos(w/2 - #msg/2, h-3)
  327.     term.write(msg)
  328.     msg = "By NitrogenFingers"
  329.     term.setCursorPos(w/2 - #msg/2, h-2)
  330.     term.write(msg)
  331.    
  332.     os.pullEvent()
  333. end
  334.  
  335. local function drawCanvas()
  336.     term.setBackgroundColor(alphaC)
  337.     --term.clear()
  338.     --Picture first
  339.     for y=sy+1,sy+h-1 do
  340.         if frames[sFrame][y] then
  341.             for x=sx+1,sx+w-2 do
  342.                 if frames[sFrame][y][x] then
  343.                     term.setBackgroundColour(frames[sFrame][y][x])
  344.                 else
  345.                     term.setBackgroundColour(alphaC)
  346.                 end
  347.                 term.setCursorPos(x-sx,y-sy)
  348.                 term.write(" ")
  349.             end
  350.         else
  351.             for x=sx+1,sx+w-2 do
  352.                 term.setBackgroundColour(alphaC)
  353.                 term.setCursorPos(x-sx,y-sy)
  354.                 term.write(" ")
  355.             end
  356.         end
  357.     end
  358.    
  359.     --Then the buffer
  360.     if selectrect then
  361.         if buffer and rectblink == 1 then
  362.         for y=selectrect.y1, math.min(selectrect.y2, selectrect.y1 + buffer.height-1) do
  363.             for x=selectrect.x1, math.min(selectrect.x2, selectrect.x1 + buffer.width-1) do
  364.                 if buffer.contents[y-selectrect.y1+1][x-selectrect.x1+1] then
  365.                     term.setCursorPos(x+sx,y+sy)
  366.                     term.setBackgroundColour(buffer.contents[y-selectrect.y1+1][x-selectrect.x1+1])
  367.                     term.write(" ")
  368.                 end
  369.             end
  370.         end
  371.         end
  372.    
  373.         --This draws the "selection" box
  374.         local add = nil
  375.         if buffer then
  376.             term.setBackgroundColour(colours.lightGrey)
  377.         else
  378.             term.setBackgroundColour(colours.grey)
  379.         end
  380.         for i=selectrect.x1, selectrect.x2 do
  381.             add = (i + selectrect.y1 + rectblink) % 2 == 0
  382.             term.setCursorPos(i-sx,selectrect.y1-sy)
  383.             if add then term.write(" ") end
  384.             add = (i + selectrect.y2 + rectblink) % 2 == 0
  385.             term.setCursorPos(i-sx,selectrect.y2-sy)
  386.             if add then term.write(" ") end
  387.         end
  388.         for i=selectrect.y1 + 1, selectrect.y2 - 1 do
  389.             add = (i + selectrect.x1 + rectblink) % 2 == 0
  390.             term.setCursorPos(selectrect.x1-sx,i-sy)
  391.             if add then term.write(" ") end
  392.             add = (i + selectrect.x2 + rectblink) % 2 == 0
  393.             term.setCursorPos(selectrect.x2-sx,i-sy)
  394.             if add then term.write(" ") end
  395.         end
  396.     end
  397. end
  398.  
  399. local function drawInterface()
  400.     --Picker
  401.     for i=1,#column do
  402.         term.setCursorPos(w-1, i)
  403.         term.setBackgroundColour(column[i])
  404.         term.write("  ")
  405.     end
  406.     term.setCursorPos(w-1,#column+1)
  407.     term.setBackgroundColour(colours.black)
  408.     term.setTextColour(colours.red)
  409.     term.write("XX")
  410.     --Pallette
  411.     term.setCursorPos(w-1,h-1)
  412.     if not lSel then
  413.         term.setBackgroundColour(colours.black)
  414.         term.setTextColour(colours.red)
  415.         term.write("X")
  416.     else
  417.         term.setBackgroundColour(lSel)
  418.         term.setTextColour(lSel)
  419.         term.write(" ")
  420.     end
  421.     if not rSel then
  422.         term.setBackgroundColour(colours.black)
  423.         term.setTextColour(colours.red)
  424.         term.write("X")
  425.     else
  426.         term.setBackgroundColour(rSel)
  427.         term.setTextColour(rSel)
  428.         term.write(" ")
  429.     end
  430.     --Footer
  431.     if inMenu then return end
  432.    
  433.     term.setCursorPos(1, h)
  434.     term.setBackgroundColour(colours.lightGrey)
  435.     term.setTextColour(colours.grey)
  436.     term.clearLine()
  437.     term.write(string.upper(string.sub(state, 1, 1))..string.sub(state, 2, #state).." mode")
  438.     if state == "brush" then term.write(", size="..brushsize) end
  439.     term.write(" (H for help)")
  440.    
  441.     local coords="X:"..sx.." Y:"..sy
  442.     if animated then coords = coords.." Frame:"..sFrame.."/"..framecount.."   " end
  443.     term.setCursorPos(w-#coords+1,h)
  444.     if state == "play" then term.setBackgroundColour(colours.lime)
  445.     elseif record then term.setBackgroundColour(colours.red) end
  446.     term.write(coords)
  447.    
  448.     if animated then
  449.         term.setCursorPos(w-1,h)
  450.         term.setBackgroundColour(colours.grey)
  451.         term.setTextColour(colours.lightGrey)
  452.         term.write("<>")
  453.     end
  454. end
  455.  
  456. local function drawHelpScreen()
  457.     while true do
  458.         term.setBackgroundColour(colours.lightGrey)
  459.         term.clear()
  460.         if not selectedHelp then
  461.             term.setCursorPos(4, 1)
  462.             term.setTextColour(colours.brown)
  463.             term.write("Available modes (click for info):")
  464.             for i=1,#helpTopics do
  465.                 term.setCursorPos(2, 2 + i)
  466.                 term.setTextColour(colours.black)
  467.                 term.write(helpTopics[i].name)
  468.                 if helpTopics[i].key then
  469.                     term.setTextColour(colours.red)
  470.                     term.write(" ("..helpTopics[i].key..")")
  471.                 end
  472.             end
  473.             term.setCursorPos(4,h)
  474.             term.setTextColour(colours.black)
  475.             term.write("Press any key to exit")
  476.         else
  477.             term.setCursorPos(4,1)
  478.             term.setTextColour(colours.brown)
  479.             term.write(helpTopics[selectedHelp].name)
  480.             if helpTopics[selectedHelp].key then
  481.                 term.setTextColour(colours.red)
  482.                 term.write(" ("..helpTopics[selectedHelp].key..")")
  483.             end
  484.             term.setCursorPos(1,3)
  485.             term.setTextColour(colours.black)
  486.             print(helpTopics[selectedHelp].message.."\n")
  487.             for i=1,#helpTopics[selectedHelp].controls do
  488.                 term.setTextColour(colours.brown)
  489.                 term.write(helpTopics[selectedHelp].controls[i][1].." ")
  490.                 term.setTextColour(colours.black)
  491.                 print(helpTopics[selectedHelp].controls[i][2])
  492.             end
  493.         end
  494.        
  495.         local id,p1,p2 = os.pullEvent()
  496.        
  497.         if id == "timer" then updateTimer()
  498.         elseif id == "key" then
  499.             if selectedHelp then selectedHelp = nil
  500.             else break end
  501.         elseif id == "click" then
  502.             if not selectedHelp then
  503.                 if p2 >=3 and p2 <= 2+#helpTopics then
  504.                     selectedHelp = p2-2
  505.                 else break end
  506.             else
  507.                 selectedHelp = nil
  508.             end
  509.         end
  510.     end
  511. end
  512.  
  513. local function drawMessage(message)
  514.     term.setCursorPos(1,h)
  515.     term.setBackgroundColour(colours.lightGrey)
  516.     term.setTextColour(colours.grey)
  517.     term.clearLine()
  518.     term.write(message)
  519. end
  520.  
  521. --[[  Image tools ]]--
  522.  
  523. local function copyToBuffer(removeImage)
  524.     buffer = { width = selectrect.x2 - selectrect.x1 + 1, height = selectrect.y2 - selectrect.y1 + 1, contents = { } }
  525.     local containsSomething = false
  526.     for y=1,buffer.height do
  527.         buffer.contents[y] = { }
  528.         local f,l = sFrame,sFrame
  529.         if record then f,l = 1, framecount end
  530.        
  531.         for fra = f,l do
  532.             if frames[fra][selectrect.y1 + y - 1] then
  533.                 for x=1,buffer.width do
  534.                     buffer.contents[y][x] = frames[sFrame][selectrect.y1 + y - 1][selectrect.x1 + x - 1]
  535.                     if removeImage then frames[fra][selectrect.y1 + y - 1][selectrect.x1 + x - 1] = nil end
  536.                     if buffer.contents[y][x] then containsSomething = true end
  537.                 end
  538.             end
  539.         end
  540.     end
  541.     --I don't classify an empty buffer as a real buffer- confusing to the user.
  542.     if not containsSomething then buffer = nil end
  543. end
  544.  
  545. local function copyFromBuffer(removeBuffer)
  546.     if not buffer then return end
  547.  
  548.     for y=1,buffer.height do
  549.         local f,l = sFrame, sFrame
  550.         if record then f,l = 1, framecount end
  551.        
  552.         for fra = f,l do
  553.             if not frames[fra][selectrect.y1+y-1] then frames[fra][selectrect.y1+y-1] = { } end
  554.             for x=1,buffer.width do
  555.                 frames[fra][selectrect.y1+y-1][selectrect.x1+x-1] = buffer.contents[y][x]
  556.             end
  557.         end
  558.     end
  559.    
  560.     if removeBuffer then buffer = nil end
  561. end
  562.  
  563. local function moveImage(newx,newy)
  564.     updateImageLims()
  565.    
  566.     if not leflim or not toplim then return end
  567.     if newx <=0 or newy <=0 then return end
  568.     --And superimpose each pixel to new coordinates
  569.     local newlines = { }
  570.    
  571.     local f,l = sFrame,sFrame
  572.     if record then f,l = 1,framecount end
  573.    
  574.     for i=f,l do
  575.         for y,line in pairs(frames[i]) do
  576.             newlines[y-toplim+newy] = { }
  577.             for x,char in pairs(line) do
  578.                 newlines[y-toplim+newy][x-leflim+newx] = char
  579.             end
  580.         end
  581.         frames[i] = newlines
  582.     end
  583. end
  584.  
  585. local function clearImage()
  586.     inMenu = true
  587.     if not animated then
  588.         drawMessage("Clear image? Y/N: ")
  589.     elseif record then
  590.         drawMessage("Clear ALL frames? Y/N: ")
  591.     else
  592.         drawMessage("Clear current frame? Y/N :")
  593.     end
  594.     if string.find(string.upper(readInput(1)), "Y") then
  595.         local f,l = sFrame,sFrame
  596.         if record then f,l = 1,framecount end
  597.        
  598.         for i=f,l do
  599.             frames[i] = { }
  600.         end
  601.     end
  602.     inMenu = false
  603. end
  604.  
  605. --[[  Animation Tools  ]]--
  606.  
  607. local function playAnimation()
  608.     state = "play"
  609.     selectedrect = nil
  610.    
  611.     os.pullEvent("timer")
  612.     os.startTimer(animtime)
  613.     repeat
  614.         drawCanvas()
  615.         drawInterface()
  616.        
  617.         local id,key = os.pullEvent()
  618.        
  619.         if id=="timer" then
  620.             os.startTimer(animtime)
  621.             sFrame = (sFrame % framecount) + 1
  622.         elseif id=="key" then
  623.             if key == keys.comma and animtime > 0.1 then animtime = animtime - 0.05
  624.             elseif key == keys.period and animtime < 0.5 then animtime = animtime + 0.05
  625.             elseif key == keys.space then state = "paint" end
  626.         end
  627.     until state ~= "play"
  628.     os.startTimer(0.5)
  629. end
  630.  
  631. local function changeFrame(newframe)
  632.     inMenu = true
  633.     if not tonumber(newframe) then
  634.         term.setCursorPos(1,h)
  635.         term.setBackgroundColour(colours.lightGrey)
  636.         term.setTextColour(colours.grey)
  637.         term.clearLine()
  638.    
  639.         term.write("Go to frame: ")
  640.         sleep(0.1)
  641.         newframe = tonumber(readInput(1))
  642.         if not newframe or newframe <= 0 then return end
  643.     elseif newframe <= 0 then return end
  644.    
  645.     if newframe > framecount then
  646.         for i=framecount+1,newframe do
  647.             frames[i] = {}
  648.             for y,line in pairs(frames[sFrame]) do
  649.                 frames[i][y] = { }
  650.                 for x,v in pairs(line) do
  651.                     frames[i][y][x] = v
  652.                 end
  653.             end
  654.         end
  655.         framecount = newframe
  656.     end
  657.     sFrame = newframe
  658.     inMenu = false
  659. end
  660.  
  661. local function removeFramesAfter(frame)
  662.     inMenu = true
  663.     if frame==framecount then return end
  664.  
  665.     term.setCursorPos(1,h)
  666.     term.setBackgroundColour(colours.lightGrey)
  667.     term.setTextColour(colours.grey)
  668.     term.clearLine()
  669.    
  670.     term.write("Delete frames "..(frame+1).."-"..framecount.."? Y/N: ")
  671.     sleep(0.1)
  672.     local answer = string.upper(readInput(1))
  673.    
  674.     if string.find(answer, "Y") ~= 1 then return end
  675.    
  676.     for i=frame+1, framecount do
  677.         frames[i] = nil
  678.     end
  679.     framecount = frame
  680.     inMenu = false
  681. end
  682.  
  683. --[[  Interface  ]]--
  684.  
  685. local function accessMenu()
  686.     local selection = 1
  687.     term.setCursorPos(1,h)
  688.     term.setBackgroundColour(colours.lightGrey)
  689.     term.clearLine()
  690.    
  691.     while true do
  692.         term.setCursorPos(1,h)
  693.         term.setBackgroundColour(colours.lightGrey)
  694.         term.clearLine()
  695.         term.setTextColour(colours.black)
  696.         for k,v in pairs(mChoices) do
  697.             if selection==k then
  698.                 term.setTextColour(colours.red)
  699.                 local ox,_ = term.getCursorPos()
  700.                 term.write("["..string.rep(" ",#v).."]")
  701.                 term.setCursorPos(ox+1,h)
  702.                 term.setTextColour(colours.black)
  703.                 term.write(v)
  704.                 term.setCursorPos(term.getCursorPos()+1,h)
  705.             else
  706.                 term.write(" "..v.." ")
  707.             end
  708.         end
  709.         local id,key = os.pullEvent()
  710.         if id == "key" then
  711.             if key == keys.right and selection < #mChoices then
  712.                 selection = selection + 1
  713.             elseif key == keys.left and selection > 1 then
  714.                 selection = selection - 1
  715.             elseif key == keys.enter then
  716.                 if mChoices[selection]=="Save" then
  717.                     if animated then saveNFA(sPath)
  718.                     else saveNFP(sPath) end
  719.                     return false
  720.                 elseif mChoices[selection]=="Exit" then return true
  721.                 end
  722.             elseif key == keys.leftCtrl then return false end
  723.         elseif id == "timer" then updateTimer() drawCanvas() drawInterface()end
  724.     end
  725. end
  726.  
  727. function readInput(lim)
  728.     sleep(0.01)
  729.     term.setCursorBlink(true)
  730.  
  731.     local inputString = ""
  732.     local ox,oy = term.getCursorPos()
  733.     term.write(" ")
  734.     term.setCursorPos(ox, oy)
  735.     if not lim or type(lim) ~= "number" or lim < 1 then lim = w - ox end
  736.    
  737.     while true do
  738.         local id,key = os.pullEvent()
  739.        
  740.         if id == "key" and key == 14 and #inputString > 0 then
  741.             inputString = string.sub(inputString, 1, #inputString-1)
  742.             term.setCursorPos(ox + #inputString,oy)
  743.             term.write(" ")
  744.         elseif id == "key" and key == 28 and inputString ~= string.rep(" ", #inputString) then
  745.             break
  746.         elseif id == "char" and #inputString < lim then
  747.             inputString = inputString..key
  748.         elseif id == "timer" then
  749.             updateTimer()
  750.             drawCanvas()
  751.             drawInterface()
  752.         end
  753.         term.setCursorPos(ox,oy)
  754.         term.write(inputString)
  755.         term.setCursorPos(ox + #inputString, oy)
  756.     end
  757.    
  758.     while string.sub(inputString, 1, 1) == " " do
  759.         inputString = string.sub(inputString, 2, #inputString)
  760.     end
  761.     while string.sub(inputString, #inputString, #inputString) == " " do
  762.         inputString = string.sub(inputString, 1, #inputString-1)
  763.     end
  764.     term.setCursorBlink(false)
  765.    
  766.     return inputString
  767. end
  768.  
  769. local function handleEvents()
  770.     os.startTimer(0.5)
  771.     while true do
  772.         drawCanvas()
  773.         drawInterface()
  774.         local id,p1,p2,p3 = os.pullEvent()
  775.         if id=="timer" then
  776.             updateTimer()
  777.         elseif id=="click" then
  778.             if p1 >=w-1 and p2 < #column+1 then
  779.                 if p3==1 then lSel = column[p2]
  780.                 else rSel = column[p2] end
  781.             elseif p1 >=w-1 and p2==#column+1 then
  782.                 if p3==1 then lSel = nil
  783.                 else rSel = nil end
  784.             elseif p1==w-1 and p2==h and animated then
  785.                 changeFrame(sFrame-1)
  786.             elseif p1==w and p2==h and animated then
  787.                 changeFrame(sFrame+1)
  788.             elseif p1 < w-1 and p2 <= h-1 then
  789.                 if state=="pippette" then
  790.                     if p3==1 then
  791.                         if frames[sFrame][p2+sy] and frames[sFrame][p2+sy][p1+sx] then
  792.                             lSel = frames[sFrame][p2+sy][p1+sx]
  793.                         end
  794.                     elseif p3==2 then
  795.                         if frames[sFrame][p2+sy] and frames[sFrame][p2+sy][p1+sx] then
  796.                             rSel = frames[sFrame][p2+sy][p1+sx]
  797.                         end
  798.                     end
  799.                 elseif state=="move" then
  800.                     moveImage(p1,p2)
  801.                 elseif state=="corner select" then
  802.                     if not selectrect then
  803.                         selectrect = { x1=p1, x2=p1, y1=p2, y2=p2 }
  804.                     elseif selectrect.x1 ~= p1 and selectrect.y1 ~= p2 then
  805.                         if p1<selectrect.x1 then selectrect.x1 = p1
  806.                         else selectrect.x2 = p1 end
  807.                        
  808.                         if p2<selectrect.y1 then selectrect.y1 = p2
  809.                         else selectrect.y2 = p2 end
  810.                        
  811.                         state = "select"
  812.                     end
  813.                 elseif state=="select" then
  814.                     local swidth = selectrect.x2 - selectrect.x1
  815.                     local sheight = selectrect.y2 - selectrect.y1
  816.                    
  817.                     selectrect.x1 = p1
  818.                     selectrect.y1 = p2
  819.                     selectrect.x2 = p1 + swidth
  820.                     selectrect.y2 = p2 + sheight
  821.                 else
  822.                     local f,l = sFrame,sFrame
  823.                     if record then f,l = 1,framecount end
  824.                     local bwidth = 0
  825.                     if state == "brush" then bwidth = brushsize-1 end
  826.                
  827.                     for i=f,l do
  828.                         for x = math.max(1,p1+sx-bwidth),p1+sx+bwidth do
  829.                             for y = math.max(1,p2+sy-bwidth), p2+sy+bwidth do
  830.                                 if math.abs(x - (p1+sx)) + math.abs(y - (p2+sy)) <= bwidth then
  831.                                     if not frames[i][y] then frames[i][y] = {} end
  832.                                     if p3==1 then frames[i][y][x] = lSel
  833.                                     else frames[i][y][x] = rSel end
  834.                                 end
  835.                             end
  836.                         end
  837.                     end
  838.                 end
  839.             end
  840.         elseif id=="key" then
  841.             if p1==keys.leftCtrl then
  842.                 if accessMenu() then break end
  843.             elseif p1==keys.h then drawHelpScreen()
  844.             elseif p1==keys.x then
  845.                 --Cut feature
  846.                 if state=="select" then
  847.                     copyToBuffer(true)
  848.                 else
  849.                     clearImage()
  850.                 end
  851.             elseif p1==keys.c and state=="select" then
  852.                 copyToBuffer(false)
  853.             elseif p1==keys.v then
  854.                 if state~="select" then
  855.                     selectrect = { x1=1,x2=buffer.width,y1=1,y2=buffer.height }
  856.                 else
  857.                     copyFromBuffer(false)
  858.                 end
  859.             elseif p1==keys.z and state=="select" then
  860.                 buffer = nil
  861.             elseif p1==keys.s then
  862.                 if state=="corner select" or state=="select" then
  863.                     state = "paint"
  864.                 elseif selectrect and selectrect.x1 ~= selectrect.x2 then
  865.                     state = "select"
  866.                 else
  867.                     state = "corner select"
  868.                 end
  869.             elseif p1==keys.tab then
  870.                 selectrect = nil
  871.                 if state == "select" then state = "corner select" end
  872.             elseif p1==keys.q then
  873.                 if lSel then alphaC = lSel end
  874.             elseif p1==keys.w then
  875.                 if rSel then alphaC = rSel end
  876.             elseif p1==keys.b then
  877.                 if state=="brush" then state = "paint"
  878.                 else state = "brush" end
  879.             elseif p1==keys.m then
  880.                 if state=="move" then state = "paint"
  881.                 else state = "move" end
  882.             elseif p1==keys.backslash and animated then
  883.                 record = not record
  884.             elseif p1==keys.p then
  885.                 if state=="pippette" then state = "paint"
  886.                 else state = "pippette" end
  887.             elseif p1==keys.g and animated then
  888.                 changeFrame()
  889.             elseif p1==keys.period and animated then
  890.                 changeFrame(sFrame+1)
  891.             elseif p1==keys.comma and animated then
  892.                 changeFrame(sFrame-1)
  893.             elseif p1==keys.r and animated then
  894.                 removeFramesAfter(sFrame)
  895.             elseif p1==keys.space and animated then
  896.                 playAnimation()
  897.             elseif p1==keys.left then
  898.                 if state == "move" then
  899.                     updateImageLims()
  900.                     moveImage(leflim-1,toplim)
  901.                 elseif state=="select" and selectrect.x1 > 1 then
  902.                     selectrect.x1 = selectrect.x1-1
  903.                     selectrect.x2 = selectrect.x2-1
  904.                 elseif sx > 0 then sx=sx-1 end
  905.             elseif p1==keys.right then
  906.                 if state == "move" then
  907.                     updateImageLims()
  908.                     moveImage(leflim+1,toplim)
  909.                 elseif state=="select" then
  910.                     selectrect.x1 = selectrect.x1+1
  911.                     selectrect.x2 = selectrect.x2+1
  912.                 else sx=sx+1 end
  913.             elseif p1==keys.up then
  914.                 if state == "move" then
  915.                     updateImageLims()
  916.                     moveImage(leflim,toplim-1)
  917.                 elseif state=="select" and selectrect.y1 > 1 then
  918.                     selectrect.y1 = selectrect.y1-1
  919.                     selectrect.y2 = selectrect.y2-1
  920.                 elseif sy > 0 then sy=sy-1 end
  921.             elseif p1==keys.down then
  922.                 if state == "move" then
  923.                     updateImageLims()
  924.                     moveImage(leflim,toplim+1)
  925.                 elseif state=="select" then
  926.                     selectrect.y1 = selectrect.y1+1
  927.                     selectrect.y2 = selectrect.y2+1
  928.                 else sy=sy+1 end
  929.             end
  930.         elseif id=="char" and tonumber(p1) then
  931.             if state=="brush" and tonumber(p1) > 1 then
  932.                 brushsize = tonumber(p1)
  933.             elseif tonumber(p1) > 0 then
  934.                 changeFrame(tonumber(p1))
  935.             end
  936.         end
  937.     end
  938. end
  939.  
  940. --[[  Main  ]]--
  941.  
  942. --Taken almost directly from edit (for consistency)
  943. local tArgs = {...}
  944.  
  945. local ca = 1
  946.  
  947. if tArgs[ca] == "-a" then
  948.     animated = true
  949.     ca = ca + 1
  950. end
  951.  
  952. if #tArgs < ca then
  953.     print("Usage: npaintpro [-a] <path>")
  954.     return
  955. end
  956.  
  957. sPath = shell.resolve(tArgs[ca])
  958. local bReadOnly = fs.isReadOnly(sPath)
  959. if fs.exists(sPath) then
  960.     if fs.isDir(sPath) then
  961.         print("Cannot edit a directory.")
  962.         return
  963.     elseif string.find(sPath, ".nfp") ~= #sPath-3 and string.find(sPath, ".nfa") ~= #sPath-3 then
  964.         print("Can only edit .nfp and nfa files:",string.find(sPath, ".nfp"),#sPath-3)
  965.         return
  966.     end
  967.    
  968.     if string.find(sPath, ".nfa") == #sPath-3 then
  969.         animated = true
  970.     end
  971.    
  972.     if string.find(sPath, ".nfp") == #sPath-3 and animated then
  973.         print("Convert to nfa? Y/N")
  974.         if string.find(string.lower(io.read()), "y") then
  975.             local nsPath = string.sub(sPath, 1, #sPath-1).."a"
  976.             fs.move(sPath, nsPath)
  977.             sPath = nsPath
  978.         else
  979.             animated = false
  980.         end
  981.     end
  982. else
  983.     if not animated and string.find(sPath, ".nfp") ~= #sPath-3 then
  984.         sPath = sPath..".nfp"
  985.     elseif animated and string.find(sPath, ".nfa") ~= #sPath-3 then
  986.         sPath = sPath..".nfa"
  987.     end
  988. end
  989.  
  990. if not term.isColour() then
  991.     print("For colour computers only")
  992.     return
  993. end
  994.  
  995. drawLogo()
  996. init()
  997. handleEvents()
  998.  
  999. term.setBackgroundColour(colours.black)
  1000. shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement