Advertisement
theoriginalbit

Loading Screen v2.0

Jan 12th, 2013
1,038
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.42 KB | None | 0 0
  1. --[[
  2. Author: TheOriginalBIT
  3. Version: 2.0.4
  4. Created: 03 Jan 2013
  5. Last Update: 17 Feb 2013
  6.  
  7. License:
  8.  
  9. COPYRIGHT NOTICE
  10. Copyright © 2013 Joshua Asbury a.k.a TheOriginalBIT [theoriginalbit@gmail.com]
  11.  
  12. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
  13. associated documentation files (the "Software"), to deal in the Software without restriction,
  14. including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15. copies of the Software, and to permit persons to whom the Software is furnished to do so,
  16. subject to the following conditions:
  17.  
  18. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  19. -Visible credit is given to the original author.
  20. -The software is distributed in a non-profit way.
  21.  
  22. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  23. WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  24. COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  25. ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. ]]--
  27.  
  28. --[[ PRIVATE CLASS VARIABLES AND FUNCTIONS ]]--
  29.  
  30. local version = 2.0
  31. local apiIdentifier = string.format( "Loading Bar API v%.2f Object", version )
  32. local sizeX, sizeY = term.getSize()
  33. local isAdvanced = term.isColor and term.isColor()
  34. local usedIds = {}
  35.  
  36. local function generateId()
  37.     if #usedIds > 999 then error( "Maximum load bars reached, you have used 999, how?! Tell me on the forum post! Better yet, SHOW ME!" ) end
  38.     local objId = #usedIds
  39.     while usedIds[ objId ] do
  40.         objId = objId + 1
  41.         if objId > 999 then
  42.             objId = 1
  43.         end
  44.     end
  45.     usedIds[ objId ] = true
  46.     return objId
  47. end
  48.  
  49. local function modeToText( mode )
  50.     if mode == STANDARD then
  51.         return "STANDARD"
  52.     elseif mode == BAR_ONLY then
  53.         return "BAR_ONLY"
  54.     elseif mode == ASCII_BAR_ONLY then
  55.         return "ASCII_BAR_ONLY"
  56.     elseif mode == LOGO_IS_LOAD then
  57.         return "LOGO_IS_LOAD"
  58.     elseif mode == LOGO_IS_OVERLAY then
  59.         return "LOGO_IS_OVERLAY"
  60.     elseif mode == ASCII then
  61.         return "ASCII"
  62.     else
  63.         return "UNKNOWN"
  64.     end
  65. end
  66.  
  67. local function hexLookup( char )
  68.     local value = tonumber(char, 16)
  69.     return ( value and math.pow(2, value) or false )
  70. end
  71.  
  72. local function clearScreen( color )
  73.     term.setCursorPos(1,1)
  74.     if isAdvanced then
  75.         term.setBackgroundColor( color or colors.blue )
  76.     end
  77.     term.clear()
  78. end
  79.  
  80. local function isValidMode( mode )
  81.     return mode ~= nil and ( mode >= STANDARD and mode <= ASCII )
  82. end
  83.  
  84. local function isValidParams( mode, tLogo, count, barWidth, y, loadCol, openingMsg, headMsg, footMsg )
  85.     if mode == STANDARD then
  86.         if type( tLogo ) ~= "table" then
  87.             return false, "Logo must be table, got "..type( tLogo )
  88.         elseif type( count ) ~= "number" then
  89.             return false, "Event count must be a number, got "..type( count )
  90.         elseif type( barWidth ) ~= "number" then
  91.             return false, "Bar width must be a number, got "..type( barWidth )
  92.         elseif type( y ) ~= "number" then
  93.             return false, "Y position must be a number, got "..type( y )
  94.         elseif type( loadCol ) ~= "number" then
  95.             return false, "Load bar color must be a number, got "..type( loadCol )
  96.         elseif type( loadCol ) == "number" and ( ( loadCol < 1 ) or ( loadCol > ( 2^16 ) - 1 ) ) then
  97.             return false, "Invalid load bar color, must be in range 1-65535, got "..loadCol
  98.         elseif type( openingMsg ) ~= "string" and type( openingMsg ) ~= "nil" then
  99.             return false, "Invalid opening message, expected string or nil, got "..type( openingMsg )
  100.         elseif type( headMsg ) ~= "string" and type( headMsg ) ~= "nil" then
  101.             return false, "Invalid header message, expected string or nil, got "..type( headMsg )
  102.         elseif type( footMsg ) ~= "string" and type( footMsg ) ~= "nil" then
  103.             return false, "Invalid footer message, expected string or nil, got "..type( footMsg )
  104.         end
  105.        
  106.         return true, nil
  107.     elseif mode == BAR_ONLY then
  108.         if type( tLogo ) ~= "nil" then
  109.             return false, "Logo must be nil, got "..type( tLogo )
  110.         elseif type( count ) ~= "number" then
  111.             return false, "Event count must be a number, got "..type( count )
  112.         elseif type( barWidth ) ~= "number" then
  113.             return false, "Bar width must be a number, got "..type( barWidth )
  114.         elseif type( y ) ~= "number" then
  115.             return false, "Y position must be a number, got "..type( y )
  116.         elseif type( loadCol ) ~= "number" then
  117.             return false, "Load bar color must be a number, got "..type( loadCol )
  118.         elseif type( loadCol ) == "number" and ( ( loadCol < 1 ) or ( loadCol > ( 2^16 ) - 1 ) ) then
  119.             return false, "Invalid load bar color, must be in range 1-65535, got "..loadCol
  120.         elseif type( openingMsg ) ~= "string" and type( openingMsg ) ~= "nil" then
  121.             return false, "Invalid opening message, expected string or nil, got "..type( openingMsg )
  122.         elseif type( headMsg ) ~= "string" and type( headMsg ) ~= "nil" then
  123.             return false, "Invalid header message, expected string or nil, got "..type( headMsg )
  124.         elseif type( footMsg ) ~= "string" and type( footMsg ) ~= "nil" then
  125.             return false, "Invalid footer message, expected string or nil, got "..type( footMsg )
  126.         end
  127.        
  128.         return true, nil
  129.     elseif mode == ASCII_BAR_ONLY then
  130.         if type( tLogo ) ~= "nil" then
  131.             return false, "Logo must be nil, got "..type( tLogo )
  132.         elseif type( count ) ~= "number" then
  133.             return false, "Event count must be a number, got "..type( count )
  134.         elseif type( barWidth ) ~= "number" then
  135.             return false, "Bar width must be a number, got "..type( barWidth )
  136.         elseif type( y ) ~= "number" then
  137.             return false, "Y position must be a number, got "..type( y )
  138.         elseif type( loadCol ) ~= "nil" then
  139.             return false, "Load bar color must be nil, got "..type( loadCol )
  140.         elseif type( loadCol ) == "number" and ( ( loadCol < 1 ) or ( loadCol > ( 2^16 ) - 1 ) ) then
  141.             return false, "Invalid load bar color, must be in range 1-65535, got "..loadCol
  142.         elseif type( openingMsg ) ~= "string" and type( openingMsg ) ~= "nil" then
  143.             return false, "Invalid opening message, expected string or nil, got "..type( openingMsg )
  144.         elseif type( headMsg ) ~= "string" and type( headMsg ) ~= "nil" then
  145.             return false, "Invalid header message, expected string or nil, got "..type( headMsg )
  146.         elseif type( footMsg ) ~= "string" and type( footMsg ) ~= "nil" then
  147.             return false, "Invalid footer message, expected string or nil, got "..type( footMsg )
  148.         end
  149.        
  150.         return true, nil
  151.     elseif mode == LOGO_IS_LOAD or mode == LOGO_IS_OVERLAY then
  152.         if type( tLogo ) ~= "table" then
  153.             return false, "Logo must be table, got "..type( tLogo )
  154.         elseif type( count ) ~= "number" then
  155.             return false, "Event count must be a number, got "..type( count )
  156.         elseif type( barWidth ) ~= "nil" then
  157.             return false, "Bar width must be a nil, got "..type( barWidth )
  158.         elseif type( y ) ~= "number" then
  159.             return false, "Y position must be a number, got "..type( y )
  160.         elseif mode == LOGO_IS_LOAD and type( loadCol ) ~= "number" then
  161.             return false, "Load bar color must be a number, got "..type( loadCol )
  162.         elseif mode == LOGO_IS_OVERLAY and type( loadCol ) ~= "nil" then
  163.             return false, "Load bar color must be nil, got "..type( loadCol )
  164.         elseif type( loadCol ) == "number" and ( ( loadCol < 1 ) or ( loadCol > ( 2^16 ) - 1 ) ) then
  165.             return false, "Invalid load bar color, must be in range 1-65535, got "..loadCol
  166.         elseif type( openingMsg ) ~= "string" and type( openingMsg ) ~= "nil" then
  167.             return false, "Invalid opening message, expected string or nil, got "..type( openingMsg )
  168.         elseif type( headMsg ) ~= "string" and type( headMsg ) ~= "nil" then
  169.             return false, "Invalid header message, expected string or nil, got "..type( headMsg )
  170.         elseif type( footMsg ) ~= "string" and type( footMsg ) ~= "nil" then
  171.             return false, "Invalid footer message, expected string or nil, got "..type( footMsg )
  172.         end
  173.        
  174.         return true, nil
  175.     elseif mode == ASCII then
  176.         if type( tLogo ) ~= "table" then
  177.             return false, "Logo must be table, got "..type( tLogo )
  178.         elseif type( count ) ~= "number" then
  179.             return false, "Event count must be a number, got "..type( count )
  180.         elseif type( barWidth ) ~= "number" then
  181.             return false, "Bar width must be a number, got "..type( barWidth )
  182.         elseif type( y ) ~= "number" then
  183.             return false, "Y position must be a number, got "..type( y )
  184.         elseif type( loadCol ) ~= "nil" then
  185.             return false, "Load bar color must be a nil, got "..type( loadCol )
  186.         elseif type( loadCol ) == "number" and ( ( loadCol < 1 ) or ( loadCol > ( 2^16 ) - 1 ) ) then
  187.             return false, "Invalid load bar color, must be in range 1-65535, got "..loadCol
  188.         elseif type( openingMsg ) ~= "string" and type( openingMsg ) ~= "nil" then
  189.             return false, "Invalid opening message, expected string or nil, got "..type( openingMsg )
  190.         elseif type( headMsg ) ~= "string" and type( headMsg ) ~= "nil" then
  191.             return false, "Invalid header message, expected string or nil, got "..type( headMsg )
  192.         elseif type( footMsg ) ~= "string" and type( footMsg ) ~= "nil" then
  193.             return false, "Invalid footer message, expected string or nil, got "..type( footMsg )
  194.         end
  195.        
  196.         return true, nil
  197.     end
  198.    
  199.     return false, "Something went seriously wrong, please report this bug with the code 169:"..tostring( mode ).." and your call to this api."
  200. end
  201.  
  202. local function getYFromMode( mode, logo, y )   
  203.     if mode == STANDARD or mode == BAR_ONLY or mode == ASCII_BAR_ONLY or mode == ASCII then
  204.         return ( y + 1 > sizeY and (y - 1) < 1) and sizeY - 4 or y
  205.     elseif mode == LOGO_IS_LOAD or mode == LOGO_IS_OVERLAY then
  206.         return math.ceil( sizeY / 2 - #logo / 2 )
  207.     end
  208. end
  209.  
  210. local function getWidthFromMode( mode, logo, width )
  211.     if mode == STANDARD or mode == BAR_ONLY or mode == ASCII_BAR_ONLY or mode == ASCII then
  212.         return width or 20
  213.     elseif mode == LOGO_IS_LOAD or mode == LOGO_IS_OVERLAY then
  214.         return logo[1] ~= nil and #logo[1] or 20
  215.     end
  216. end
  217.  
  218. local function draw_header_footer( data )
  219.     if isAdvanced then
  220.         term.setBackgroundColor( colors.white )
  221.         term.setTextColor( colors.black )
  222.     end
  223.     term.setCursorPos( math.ceil( sizeX / 2 ) - math.ceil( string.len( data.headerMessage ) / 2 ), 1 )
  224.     write( data.headerMessage )
  225.     term.setCursorPos( math.ceil( sizeX / 2 ) - math.ceil( string.len( data.footerMessage ) / 2 ), sizeY )
  226.     write( data.footerMessage )
  227. end
  228.  
  229. local function draw_box( data )
  230.     for x = 1, data.barWidth + 2 do
  231.         for y = 1, 3 do
  232.             term.setCursorPos( (sizeX - data.barWidth - 1) / 2 + x - 1, data.yPos + y -2 )
  233.             local char = " "
  234.             if x == 1 and y == 1 or x == data.barWidth + 2 and y == 1 then
  235.                 char = "."
  236.             elseif x == 1 and y == 3 or x == data.barWidth + 2 and y == 3 then
  237.                 char = "'"
  238.             elseif x == 1 or x == data.barWidth + 2 then
  239.                 char = "|"
  240.             elseif y == 1 then
  241.                 char = "_"
  242.             elseif y == 3 then
  243.                 char = "-"
  244.             end
  245.            
  246.             write( char )
  247.         end
  248.     end
  249. end
  250.  
  251. local function draw_progress_bar( self )
  252.     if self.mode == ASCII_BAR_ONLY or self.mode == ASCII then
  253.         draw_box( self.data )
  254.     end
  255.    
  256.     for i = 1, self.data.barWidth do
  257.         term.setCursorPos( (sizeX - self.data.barWidth) / 2 + i, self.data.yPos)
  258.         local char = ' '
  259.         if i < (( self.data.currentEventCount / self.data.totalEventCount ) * self.data.barWidth + 1) then
  260.             if isAdvanced and self.data.barColor then
  261.                 term.setBackgroundColor( self.data.barColor )
  262.             else
  263.                 char = '|'
  264.             end
  265.         else
  266.             if isAdvanced and self.data.barColor then
  267.                 term.setBackgroundColor( colors.gray )
  268.             end
  269.         end
  270.        
  271.         write( char )
  272.     end
  273. end
  274.  
  275. local function draw_message( self )
  276.     if self.data.progressMessage then
  277.         term.setCursorPos( sizeX / 2 - self.data.progressMessage:len() / 2, sizeY - 3 )
  278.         if isAdvanced then
  279.             term.setTextColor( colors.black )
  280.             term.setBackgroundColor( colors.white )
  281.         end
  282.         term.clearLine()
  283.         term.write( self.data.progressMessage )
  284.     end
  285. end
  286.  
  287. local function drawScreen( self )
  288.     if not ( self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY ) then
  289.         draw_header_footer( self.data )
  290.     end
  291.    
  292.     for row = 1, #self.data.logo do
  293.         for col = 1, self.data.logo[row]:len() do
  294.             term.setCursorPos( math.ceil( sizeX / 2 - #self.data.logo[1] / 2 ) + col, self.data.isLogoLoad and self.data.yPos + row or (math.ceil( sizeY / 2 - #self.data.logo / 2 ) + row) - 2  )
  295.             local char = self.data.logo[row]:sub( col, col )
  296.             if char ~= " " then
  297.                 if not self.data.isLogoLoad or col > ( ( self.data.currentEventCount / self.data.totalEventCount ) * self.data.logo[1]:len() + ( self.data.currentEventCount == 0 and self.data.currentEventCount or 1 ) ) then
  298.                     if not ( self.mode == LOGO_IS_OVERLAY ) then
  299.                         term.setBackgroundColor( hexLookup( char ) )
  300.                     else
  301.                         term.setBackgroundColor( colors.lightGray )
  302.                     end
  303.                 else
  304.                     if not ( self.mode == LOGO_IS_OVERLAY ) then
  305.                         term.setBackgroundColor( self.data.barColor )
  306.                     else
  307.                         term.setBackgroundColor( hexLookup( char ) )
  308.                     end
  309.                 end
  310.                 write(" ")
  311.             end
  312.         end
  313.         print()
  314.     end
  315.    
  316.     if not self.data.isLogoLoad then
  317.         draw_progress_bar( self )
  318.     end
  319.    
  320.     if not ( self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY ) then
  321.         draw_message( self )
  322.     end
  323. end
  324.  
  325. local function drawScreenASCII( self )
  326.     if not ( self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY ) then
  327.         draw_header_footer( self.data )
  328.     end
  329.    
  330.     for row = 1, #self.data.logo do
  331.         for col = 1, self.data.logo[row]:len() do
  332.             term.setCursorPos( math.ceil( sizeX / 2 - #self.data.logo[1] / 2 ) + col, (math.ceil( sizeY / 2 - #self.data.logo / 2 ) + row) - 2  )
  333.             local char = self.data.logo[row]:sub( col, col )
  334.             if char ~= " " then
  335.                 if col > ( ( self.data.currentEventCount / self.data.totalEventCount ) * self.data.logo[1]:len() + ( self.data.currentEventCount == 0 and self.data.currentEventCount or 1 ) ) then
  336.                     write( char )
  337.                 end
  338.             end
  339.         end
  340.         print()
  341.     end
  342.    
  343.     draw_progress_bar( self )
  344.    
  345.     if not ( self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY ) then
  346.         draw_message( self )
  347.     end
  348. end
  349.  
  350. --[[ PUBLIC CLASS VARIABLES AND FUNCTIONS ]]--
  351.  
  352. function getVersion() return version end
  353.  
  354. function getApiIdentifier() return apiIdentifier end
  355.  
  356. --[[ OBJECT ]]--
  357.  
  358. load = {}
  359.  
  360. load.__index = load
  361. load.__tostring = function( self ) return apiIdentifier end
  362. load.__gc = function( self ) usedIds[ self.id ] = nil self.data.logo = nil self.data = nil self = nil end
  363.  
  364. --[[ GETTERS ]]--
  365.  
  366. function load.getId( self )
  367.     return self.id
  368. end
  369.  
  370. function load.getCurrentProgress( self )
  371.     return self.data.currentEventCount
  372. end
  373.  
  374. function load.getFormattedProgress( self )
  375.     return self.data.currentEventCount.."/"..self.data.totalEventCount
  376. end
  377.  
  378. --[[ SETTERS ]]--
  379.  
  380. function load.setMessage( self, msg )
  381.     if not self.data then error( "Object does not exist", 2 ) end
  382.     self.data.progressMessage = msg
  383.     os.queueEvent( "load_update_"..self.id )
  384. end
  385.  
  386. --[[ CONTRUCTORS / DESTRUCTORS ]]--
  387.  
  388. function init( lmode, tLogo, count, width, y, loadColor, openingMsg, headMsg, footMsg )
  389.     if not isValidMode( lmode ) then error( "Bad argument: Invalid mode: "..type( lmode ).." : "..tostring( lmode ), 2 ) end
  390.     if not isAdvanced and ( lmode ~= ASCII_BAR_ONLY and lmode ~= ASCII ) then error( "Invalid mode: Can only run in ASCII mode, attempted "..modeToText( lmode ), 2 ) end
  391.    
  392.     local valid, errorMsg = isValidParams( lmode, tLogo, count, width, y, loadColor, openingMsg, headMsg, footMsg )
  393.     if not valid then error( "Bad argument: Invalid paramters for mode: "..modeToText( lmode ).." : "..errorMsg, 2 ) end
  394.    
  395.     local loadObject = { id = generateId(), data = {
  396.         isLogoLoad = ( lmode == LOGO_IS_LOAD or lmode == LOGO_IS_OVERLAY ),
  397.         isAscii = ( lmode == ASCII_BAR_ONLY or lmode == ASCII ),
  398.         isBarOnly = ( lmode == BAR_ONLY or lmode == ASCII_BAR_ONLY ),
  399.         logo = type( tLogo ) == "table" and tLogo or {},
  400.         barWidth  = getWidthFromMode( lmode, tLogo, width ),
  401.         yPos = getYFromMode( lmode, tLogo, y ),
  402.         barColor = loadColor,
  403.         totalEventCount = count,
  404.         currentEventCount = 0,
  405.         progressMessage = openingMsg or "",
  406.         headerMessage = headMsg or "",
  407.         footerMessage = footMsg or "",
  408.     }, mode = lmode }
  409.    
  410.     setmetatable( loadObject, load )
  411.    
  412.     return loadObject
  413. end
  414.  
  415. function load.dealloc( self )
  416.     usedIds[ self.id ] = nil
  417.     self.data.logo = nil
  418.     self.data = nil
  419.     self = nil
  420. end
  421.  
  422. --[[ OBJECT FUNCTIONS ]]--
  423.  
  424. function load.reset( self )
  425.     if not self.data then error( "Object does not exist", 2 ) end
  426.     self.data.currentEventCount = 0
  427. end
  428.  
  429. function load.forceStop( self )
  430.     if not self.data then error( "Object does not exist", 2 ) end
  431.     self.data.currentEventCount = self.data.totalEventCount
  432.     os.queueEvent( "load_update_"..self.id )
  433. end
  434.  
  435. function load.triggerUpdate( self, msg )
  436.     if not self.data then error( "Object does not exist", 2 ) end
  437.     self.data.currentEventCount = self.data.currentEventCount + 1
  438.     if msg and msg:len() ~= 0 then self.data.progressMessage = msg end
  439.     os.queueEvent( "load_update_"..self.id )
  440. end
  441.  
  442. function load.removeMessage( self )
  443.     if not self.data then error( "Object does not exist", 2 ) end
  444.     self.data.progressMessage = ""
  445.     os.queueEvent( "load_update_"..self.id )
  446. end
  447.  
  448. function load.forceDraw( self )
  449.     if not self.data then return end
  450.     if not ( self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY ) then error( "Object function can only be called in mode: BAR_ONLY and ASCII_BAR_ONLY", 2 ) end
  451.     draw_progress_bar( self )
  452. end
  453.  
  454. function load.run( self, cleanup )
  455.     if not self.data then error( "Object is not initialized", 2 ) end
  456.    
  457.     if self.mode ~= BAR_ONLY and self.mode ~= ASCII_BAR_ONLY then
  458.         clearScreen( colors.white )
  459.     end
  460.    
  461.     if self.data.isAscii then
  462.         drawScreenASCII( self )
  463.     else
  464.         drawScreen( self )
  465.     end
  466.  
  467.     while true do
  468.         if self.data.currentEventCount == self.data.totalEventCount then break end
  469.        
  470.         event = { os.pullEventRaw( "load_update_"..self.id ) }
  471.        
  472.         if self.mode == BAR_ONLY or self.mode == ASCII_BAR_ONLY then
  473.             draw_progress_bar( self )
  474.         elseif self.mode == ASCII then
  475.             drawScreenASCII( self )
  476.         else
  477.             drawScreen( self )
  478.         end
  479.     end
  480.    
  481.     if cleanup == true then self:dealloc() end
  482. end
  483.  
  484. STANDARD            = 1     -- Load screen with logo, text and bar
  485. BAR_ONLY            = 2     -- Progress bar only
  486. ASCII_BAR_ONLY      = 3     -- Progress bar only - in ascii
  487. LOGO_IS_LOAD        = 4     -- Load screen with logo and text - no bar, logo is progress indicator
  488. LOGO_IS_OVERLAY     = 5     -- Load screen with logo and text - no bar, logo is progress indicator ( initially greyed out )
  489. ASCII               = 6     -- Load screen with logo, text and bar - in ascii
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement