Advertisement
HydrantHunter

StarCaster

Jun 26th, 2014
716
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 73.04 KB | None | 0 0
  1. --[[   MoarPeripherals  ]]--
  2. --[[     StarCaster     ]]--
  3. --[[       by Dog       ]]--
  4. --[[        aka         ]]--
  5. --[[   HydrantHunter    ]]--
  6. --[[        and         ]]--
  7. --[[   TheOriginalBIT   ]]--
  8. --[[    pastebin:       ]]--
  9. --[[        mwdc6bK9    ]]--
  10.  
  11. local scVer = "1.1.00"
  12. --# Custom read, formatTime, newButton,
  13.   --# newNumberPicker, pickerChanged, calculateMinMax,
  14.   --# inventory filtering, and dyeToColor functions,
  15.   --# and LOTS of tutoring, courtesy of theoriginalbit
  16. local tArgs = { ... }
  17. local termX, termY = term.getSize()
  18. local launcher, launchTimer, screenTimer
  19. local filter, selectStarShape, selectStarEffects, inventoryStar, updateValue, updateVariable
  20. local effectsLookup = { "No Effect", "Twinkle", "Trail" }
  21. local shapes = { "Small Ball", "Large Ball", "Star", "Creeper Head", "Burst", "None" }
  22. local effects = { "No Effect", "Twinkle", "Trail", "Twinkle & Trail" }
  23. local runMode, operatingMode = "standard", "main"
  24. local showType, showPacing, finaleMode, goFinale = false, false, false, false
  25. local gettingHelp, wereDoneHere, popUp, badInventory, badWolf = false, false, false, false, false
  26. local starError, buildError, readyError, launchError, bPressed = "none", "none", "none", "none", "none"
  27. local minStars, maxStars, sessionNumber, currentRocket = 1, 7, 0, 1
  28. local numRockets, minHeight, maxHeight, minColors, maxColors = 25, 1, 3, 1, 8
  29. local shapeChance, effectChance, starsMade, abortCounter = 30, 30, 0, 0
  30. local TIMER_GRANULARITY, timerTicker, waitTime, pageNum, numPages = 0.10, 0, 0, 1, 1
  31. local readyRocketID, launchHeight, starColors, starShape, starEffect, numStars
  32. local rocketParts, starIDTable, rocketIDTable, errorLog = { }, { }, { }, { }
  33. local colorsList, effectsList, shapesList, guiElements = { }, { }, { }, { }
  34. local colorsAvailable, effectsAvailable, shapesAvailable = { }, { }, { }
  35.  
  36. local function clearScreen(bgColor)
  37.   term.setBackgroundColor(bgColor or colors.black)
  38.   term.clear()
  39. end
  40.  
  41. do
  42.   --# reference table for updating our values and variables as selected via picker
  43.   local valueUpdate = {
  44.     { "minHeight", function() return minHeight end, function(value) minHeight = value end };
  45.     { "maxHeight", function() return maxHeight end, function(value) maxHeight = value end };
  46.     { "minStars", function() return minStars end, function(value) minStars = value end };
  47.     { "maxStars", function() return maxStars end, function(value) maxStars = value end };
  48.     { "minColors", function() return minColors end, function(value) minColors = value end };
  49.     { "maxColors", function() return maxColors end, function(value) maxColors = value end };
  50.   }
  51.  
  52.   updateValue = function(name)
  53.     for _, v in pairs(valueUpdate) do
  54.       if v[1] == name then return v[2]() end
  55.     end
  56.   end
  57.  
  58.   updateVariable = function(name, value)
  59.     for _, v in pairs(valueUpdate) do
  60.       if v[1] == name then
  61.         v[3](value)
  62.         break
  63.       end
  64.     end
  65.   end
  66. end
  67.  
  68. local function calculateMinMax(min, max)
  69.   return math.min(min, max), math.max(min, max)
  70. end
  71.  
  72. local function pickerChanged()
  73.   local btns = guiElements.mainButtons
  74.   for i = #btns, 1, -2 do
  75.     local e = btns[i]
  76.     local e2 = btns[i - 1]
  77.     if e.getType() == "picker" and e2.getType() == "picker" then
  78.       e.setMinValue(e2.getValue())
  79.       e2.setMaxValue(e.getValue())
  80.       e2.render()
  81.     end
  82.   end
  83. end
  84.  
  85. local function newButton(x, y, w, h, text, bc, tc, action, name, b, callback)
  86.   w = type(w) == "number" and w or 1
  87.   h = math.max(type(h) == "number" and h or 1, 1)
  88.   b = (type(b) == "number" and b >= 1 and b <= 3) and b or 1
  89.   action = action or function() end
  90.   callback = type(callback) == "function" and callback or function() end
  91.   name = (type(name) == "string" and #name > 0) and name or "noName"
  92.   local hx = x + math.ceil((w - #text) / 2)
  93.   local hy = y + math.ceil(h / 2) - 1
  94.   local enabled = true
  95.   local dbc, dtc = colors.lightGray, colors.gray
  96.   return {
  97.     getType = function()
  98.       return "button"
  99.     end;
  100.     getName = function()
  101.       return name
  102.     end;
  103.     setDisabledColors = function(bc, tc)
  104.       dbc = bc
  105.       dtc = tc
  106.     end;
  107.     setText = function(t)
  108.       text = t or ""
  109.       hx = x + math.ceil((w - #text) / 2) --# recalculate offset
  110.     end;
  111.     setEnabled = function(e)
  112.       enabled = e == true
  113.     end;
  114.     render = function()
  115.       term.setBackgroundColor(enabled and bc or dbc)
  116.       term.setTextColor(enabled and tc or dtc)
  117.       for i = 0, h - 1 do
  118.         term.setCursorPos(x, y + i)
  119.         write(string.rep(' ', w))
  120.       end
  121.       term.setCursorPos(hx, hy)
  122.       write(text)
  123.     end;
  124.     processEvent = function(event, button, xPos, yPos)
  125.       if enabled and event == "mouse_click" and button == b and
  126.         xPos >= x and xPos <= (x + w - 1) and
  127.         yPos >= y and yPos <= (y + h - 1) then
  128.         action()
  129.         callback(name)
  130.       end
  131.     end;
  132.   }
  133. end
  134.  
  135. local function newNumberPicker(x, y, value, minVal, maxVal, name, enabled, callback)
  136.   local w = #tostring(maxVal) + 2
  137.   local bw = 3
  138.   minVal, maxVal = calculateMinMax(minVal, maxVal)
  139.   enabled = enabled == true
  140.   local minus = newButton(x, y, bw, 1, '-', colors.gray, colors.red, function() value = math.max(value - 1, minVal) bPressed = "minus" end, name, 1, callback)
  141.   local plus  = newButton(x + bw + w + 2, y, bw, 1, '+', colors.gray, colors.green, function() value = math.min(value + 1, maxVal) bPressed = "plus" end, name, 1, callback)
  142.   minus.setDisabledColors(colors.gray, colors.lightGray)
  143.   plus.setDisabledColors(colors.gray, colors.lightGray)
  144.   return {
  145.     getType = function()
  146.       return "picker"
  147.     end;
  148.     getName = function()
  149.       return name
  150.     end;
  151.     setEnabled = function(e)
  152.       enabled = e == true
  153.     end;
  154.     setMinValue = function(v)
  155.       minVal, maxVal = calculateMinMax(type(v) == "number" and v or minVal, maxVal)
  156.     end;
  157.     setMaxValue = function(v)
  158.       minVal, maxVal = calculateMinMax(minVal, type(v) == "number" and v or maxVal)
  159.     end;
  160.     setValue = function(v)
  161.       value = type(v) == "number" and v or value
  162.     end;
  163.     getValue = function()
  164.       return value
  165.     end;
  166.     render = function()
  167.       minus.setEnabled(enabled and value > minVal) --# update enabled status, this is really a bad place to do it...
  168.       plus.setEnabled(enabled and value < maxVal)  --# ...but it's the only reliable place that gets called frequently
  169.       minus.render()                               --# draw the - and + buttons
  170.       plus.render()
  171.       term.setBackgroundColor(colors.white)
  172.       term.setTextColor(colors.black)
  173.       term.setCursorPos(x + bw + 1, y)
  174.       write(string.rep(' ', w))
  175.       term.setCursorPos(x + bw + 1 + math.ceil((w - #tostring(value)) / 2), y)
  176.       write(value)                                 --# draw the value
  177.     end;
  178.     processEvent = function(event, button, xPos, yPos)
  179.       if event == "mouse_click" and button == 1 and yPos == y and enabled then --# reduces the amount of times the buttons need to process
  180.         minus.processEvent(event, button, xPos, yPos)
  181.         plus.processEvent(event, button, xPos, yPos)
  182.       end
  183.     end
  184.   }
  185. end
  186.  
  187. local function read( _mask, _history, _limit, _noTerminate )
  188.   if _mask and type(_mask) ~= "string" then
  189.     error("Invalid parameter #1: Expected string, got "..type(_mask), 2)
  190.   end
  191.   if _history and type(_history) ~= "table" then
  192.     error("Invalid parameter #2: Expected table, got "..type(_history), 2)
  193.   end
  194.   if _limit and type(_limit) ~= "number" then
  195.     error("Invalid parameter #3: Expected number, got "..type(_limit), 2)
  196.   end
  197.   if _noTerminate and type(_noTerminate) ~= "boolean" then
  198.     error("Invalid argument #4: Expected boolean, got "..nativeType(_noTerminate), 2)
  199.   end
  200.   term.setCursorBlink(true)
  201.   local input = ""
  202.   local pos = 0
  203.   local historyPos = nil
  204.   local pullEvent = _noTerminate and os.pullEventRaw or os.pullEvent
  205.   local sw, sh = term.getSize()
  206.   local sx, sy = term.getCursorPos()
  207.   local function redraw( _special )
  208.     local scroll = (sx + pos >= sw and (sx + pos) - sw or 0)
  209.     local replace = _special or _mask
  210.     local output = replace and (string.rep( replace, math.ceil(#input / #replace) - scroll )):sub(1, #input) or input:sub(scroll + 1)
  211.     term.setCursorPos( sx, sy )
  212.     term.write( output )
  213.     term.setCursorPos( sx + pos - scroll, sy )
  214.   end
  215.   local nativeScroll = term.scroll
  216.   term.scroll = function( _n ) local ok, err = pcall( function() return nativeScroll( _n ) end ) if ok then sy = sy - _n return err end error( err, 2 ) end
  217.   while true do
  218.     local event, code, mX, mY = pullEvent()
  219.     if event == "char" and (not _limit or #input < _limit) then
  220.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  221.       pos = pos + 1
  222.     elseif event == "paste" and (not _limit or #input < _limit) then
  223.       if _limit and #input + #code > _limit then
  224.         code = code:sub(1, _limit - #input)
  225.       end
  226.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  227.       pos = pos + #code
  228.     elseif event == "key" then
  229.       if code == keys.enter or code == keys.numPadEnter then
  230.         break
  231.       elseif code == keys.backspace and pos > 0 then
  232.         redraw(' ')
  233.         input = input:sub(1, math.max(pos - 1, 0))..input:sub(pos + 1)
  234.         pos = math.max(pos - 1, 0)
  235.       elseif code == keys.delete and pos < #input then
  236.         redraw(' ')
  237.         input = input:sub(1, pos)..input:sub(pos + 2)
  238.       elseif code == keys.home then
  239.         pos = 0
  240.       elseif code == keys["end"] then
  241.         pos = #input
  242.       elseif code == keys.left and pos > 0 then
  243.         pos = math.max(pos - 1, 0)
  244.       elseif code == keys.right and pos < #input then
  245.         pos = math.min(pos + 1, #input)
  246.       elseif _history and code == keys.up or code == keys.down then
  247.         redraw(' ')
  248.         if code == keys.up then
  249.           if not historyPos then
  250.             historyPos = #_history
  251.           elseif historyPos > 1 then
  252.             historyPos = historyPos - 1
  253.           end
  254.         else
  255.           if historyPos ~= nil and historyPos < #_history then
  256.             historyPos = math.max(historyPos + 1, #_history)
  257.           elseif historyPos == #_history then
  258.             historyPos = nil
  259.           end
  260.         end
  261.         if historyPos and #_history > 0 then
  262.           input = string.sub(_history[historyPos], 1, _limit) or ""
  263.           pos = #input
  264.         else
  265.           input = ""
  266.           pos = 0
  267.         end
  268.       end
  269.     elseif event == "mouse_click" and (mX < sx or mX >= sx + _limit) or (mY ~= sy) then
  270.       break
  271.     end
  272.     redraw(_mask)
  273.   end
  274.   term.scroll = nativeScroll
  275.   term.setCursorBlink(false)
  276.   if sy + 1 > sh then
  277.     term.scroll(sy + 1 - sh)
  278.     term.setCursorPos(1, sy)
  279.   else
  280.     term.setCursorPos(1, sy + 1)
  281.   end
  282.   return input
  283. end
  284.  
  285. local function formatTime(time)
  286.   local hour = math.floor(time)
  287.   local minute = math.floor((time - hour) * 60)
  288.   return string.format("%02d:%02d", hour, minute)
  289. end
  290.  
  291. local function getNumRockets()
  292.   term.setCursorPos(41, 5)
  293.   term.setBackgroundColor(colors.lightGray)
  294.   term.setTextColor(colors.white)
  295.   term.write("    ")
  296.   term.setCursorPos(41, 5)
  297.   local newCount = tonumber(read(nil, nil, 4))
  298.   numRockets = newCount and math.max(1, math.min(newCount, 1000)) or numRockets
  299.   for _, element in pairs(guiElements.mainButtons) do
  300.     if element.getType() == "button" and (element.getName() == "numRockets" or element.getName() == "rocketPop") then
  301.       element.setText(tostring(numRockets))
  302.       element.render()
  303.     end
  304.   end
  305. end
  306.  
  307. local function getChance(which, posX, posY)
  308.   term.setBackgroundColor(colors.lightGray)
  309.   term.setTextColor(colors.white)
  310.   term.setCursorPos(posX, posY)
  311.   term.write("   ")
  312.   term.setCursorPos(posX, posY)
  313.   local newChance = tonumber(read(nil, nil, 3))
  314.   if which == "shape" then
  315.     shapeChance = newChance and math.max(0, math.min(newChance, 100)) or shapeChance
  316.     for _, element in pairs(guiElements.mainButtons) do
  317.       if element.getType() == "button" and (element.getName() == "shapeChance" or element.getName() == "shapePop") then
  318.         element.setText(tostring(shapeChance))
  319.         element.render()
  320.       end
  321.     end
  322.   elseif which == "effect" then
  323.     effectChance = newChance and math.max(0, math.min(newChance, 100)) or effectChance
  324.     for _, element in pairs(guiElements.mainButtons) do
  325.       if element.getType() == "button" and (element.getName() == "effectChance" or element.getName() == "effectPop") then
  326.         element.setText(tostring(effectChance))
  327.         element.render()
  328.       end
  329.     end
  330.   end
  331. end
  332.  
  333. local function switchShowType()
  334.   showType = not showType
  335.   term.setCursorPos(17, 5)
  336.   term.setBackgroundColor(colors.black)
  337.   term.setTextColor(colors.orange)
  338.   term.write(showType and "Fancy   " or "Standard")
  339.   minHeight, maxHeight = 1, 3
  340.   if showType then
  341.     minStars, maxStars = 1, 7
  342.     minColors, maxColors = 1, 8
  343.     shapeChance, effectChance = 25, 25
  344.   else
  345.     minStars, maxStars = 1, 3
  346.     minColors, maxColors = 1, 3
  347.     shapeChance, effectChance = 30, 30
  348.   end
  349.   for _, element in pairs(guiElements.mainButtons) do
  350.     if element.getType() == "picker" then
  351.       element.setEnabled(showType)
  352.       element.setValue(updateValue(element.getName()))
  353.     elseif element.getType() == "button" then
  354.       if element.getName():find("show") then
  355.         element.setText(showType and "::" or "..")
  356.       elseif element.getName():find("shape") then
  357.         element.setText(tostring(shapeChance))
  358.       elseif element.getName():find("effect") then
  359.         element.setText(tostring(effectChance))
  360.       end
  361.       if element.getName():find("Chance") then
  362.         element.setEnabled(showType)
  363.       end
  364.     end
  365.     element.render()
  366.   end
  367.   term.setBackgroundColor(colors.lightGray)
  368.   term.setTextColor(showType and colors.white or colors.gray)
  369.   term.setCursorPos(23, 14)
  370.   term.write("%") --# Shape Chance
  371.   term.setCursorPos(44, 14)
  372.   term.write("%") --# Effect Chance
  373. end
  374.  
  375. local function drawPopUpBox(x, y, text)
  376.   term.setBackgroundColor(colors.white)
  377.   term.setTextColor(colors.gray)
  378.   local line = string.rep(" ", #text[1])
  379.   for i = 1, 5 do --# draw the box and write the text
  380.     term.setCursorPos(x, y + i - 1)
  381.     term.write((i == 1 or i == 5) and line or text[i - 1])
  382.   end
  383. end
  384.  
  385. local function drawPopUp(which)
  386.   popUp = true
  387.   if which == "shape" then       --# Shape Chance popup
  388.     drawPopUpBox(4, 9, { " The percentage chance ", " any star will have a  ", " special shape         " })
  389.   elseif which == "effect" then  --# Effect Chance popup
  390.     drawPopUpBox(24, 9, { " The percentage chance ", " any star will have a  ", " special effect        " })
  391.   elseif which == "pacing" then  --# Launch Timing popup
  392.     drawPopUpBox(5, 11, { " When ON, launches ", " the fireworks at  ", " a more rapid pace " })
  393.   elseif which == "finale" then  --# Finale Mode popup
  394.     drawPopUpBox(4, 13, { " When ON, launches   ", " the last 1/3 of the ", " fireworks rapidly   " })
  395.   elseif which == "type" then    --# Show Type popup
  396.     drawPopUpBox(18, 6, { " Fancy allows you  ", " to customize your ", " firework show     " })
  397.   elseif which == "rockets" then --# Number of Fireworks popup
  398.     drawPopUpBox(31, 6, { " Choose to launch ", " from 1 to 1000   ", " fireworks        " })
  399.   end
  400. end
  401.  
  402. local function drawSwitch(x, y, state)
  403.   term.setCursorPos(x, y)
  404.   term.setBackgroundColor(state and colors.green or colors.gray)
  405.   term.write("  ")
  406.   term.setBackgroundColor(state and colors.gray or colors.red)
  407.   term.write("  ")
  408. end
  409.  
  410. local function drawHeader()
  411.   clearScreen((operatingMode == "logs" or gettingHelp) and colors.white)
  412.   local title = "StarCaster"
  413.   if operatingMode == "show" then       --# Draw the header for active shows
  414.     term.setCursorPos(1, 1)
  415.     term.setTextColor(colors.yellow)
  416.     term.setBackgroundColor(colors.red)
  417.     term.write(string.rep(" ", math.floor(termX / 2) - math.floor(#title / 2)) .. title .. string.rep(" ", math.ceil(termX / 2) - math.ceil(#title / 2)))
  418.     term.setCursorPos(1, 2)
  419.     term.setBackgroundColor(colors.gray)
  420.     term.setTextColor(colors.lightGray)
  421.     local session = (sessionNumber > 9 and "Show  # " or "Show # ") .. sessionNumber
  422.     term.write(string.rep(" ", math.floor(termX / 2) - math.ceil(#session / 2)) .. session .. string.rep(" ", math.ceil(termX / 2) - math.floor(#session / 2)))
  423.     term.setCursorPos(termX - 9, 2)
  424.     term.setBackgroundColor(colors.lightGray)
  425.     term.setTextColor(colors.red)
  426.     term.write(" Cancel ")
  427.   else
  428.     term.setTextColor(colors.white)
  429.     for i = 1, 3 do                     --# Draw the main/help/log header with exit button
  430.       term.setCursorPos(1, i)
  431.       term.setBackgroundColor(colors.blue)
  432.       term.write(string.rep(" ", termX - 3))
  433.       term.setCursorPos(termX - 2, i)
  434.       term.setBackgroundColor(colors.red)
  435.       term.write(i == 2 and " X " or "   ")
  436.     end
  437.   end
  438.   if operatingMode == "main" and not gettingHelp then --# Draw the log-view burger on main screen
  439.     term.setTextColor(colors.white)
  440.     term.setBackgroundColor(colors.lightBlue)
  441.     for i = 1, 3 do
  442.       term.setCursorPos(1, i)
  443.       term.write(" - ")
  444.     end
  445.     term.setCursorPos(math.ceil(termX / 2 - math.floor(#title / 2) + 1), 2)
  446.     term.setBackgroundColor(colors.blue)
  447.     local nameColors = { colors.red, colors.yellow, colors.lime, colors.magenta }
  448.     for i = 1, 4 do                     --# Main screen - write 'Star' with each letter in a different color
  449.       term.setTextColor(nameColors[i])
  450.       term.write(title:sub(i, i))
  451.     end
  452.     term.setTextColor(colors.lightBlue)
  453.     term.write("Caster")                --# Main screen - write 'Caster'
  454.   elseif operatingMode ~= "show" then
  455.     if operatingMode == "logs" then
  456.       title = "StarCaster Error Log"
  457.     elseif gettingHelp then
  458.       title = "StarCaster Help"
  459.     end
  460.     term.setBackgroundColor(colors.blue)
  461.     term.setTextColor(colors.lightBlue)
  462.     term.setCursorPos(math.ceil((termX / 2) - math.floor(#title / 2) + 1), 2)
  463.     term.write(title)
  464.   end
  465. end
  466.  
  467. local function staticLaunchScreen()
  468.   drawHeader()
  469.   term.setCursorPos(2, 4)
  470.   term.setBackgroundColor(colors.black)
  471.   term.setTextColor(colors.gray)
  472.   term.write("Show Type ")
  473.   if not showType then
  474.     term.setTextColor(colors.lightGray)
  475.     if finaleMode then
  476.       term.write("Std")
  477.       term.setTextColor(colors.white)
  478.       term.write("/")
  479.       term.setTextColor(colors.lightGray)
  480.       term.write("Finale")
  481.     else
  482.       term.write("Standard")
  483.     end
  484.   else
  485.     local thisShow = "Fancy"
  486.     for i = 1, 5 do
  487.       term.setTextColor(2 ^ math.random(0, 14))
  488.       term.write(thisShow:sub(i, i))
  489.     end
  490.     if finaleMode then
  491.       term.setTextColor(colors.white)
  492.       term.write("/")
  493.       term.setTextColor(colors.lightGray)
  494.       term.write("Finale")
  495.     end
  496.   end
  497.   term.setTextColor(colors.gray)
  498.   term.setCursorPos(2, 6)
  499.   term.write("Rocket #")
  500.   term.setCursorPos(2, 8)
  501.   term.write("Height")
  502.   term.setCursorPos(2, 10)
  503.   term.write("Stars")
  504.   term.setTextColor(colors.white)
  505.   term.setCursorPos(25, 6)
  506.   term.write("Errors")
  507.   term.setTextColor(colors.gray)
  508.   term.setCursorPos(25, 7)
  509.   term.write("Star")
  510.   term.setCursorPos(25, 8)
  511.   term.write("Rocket")
  512.   term.setCursorPos(25, 9)
  513.   term.write("Ready")
  514.   term.setCursorPos(25, 10)
  515.   term.write("Launch")
  516.   term.setCursorPos(termX - 7, 4)
  517.   term.setTextColor(colors.white)
  518.   term.write("(")
  519.   term.setTextColor(colors.gray)
  520.   term.write(showPacing and "Quick" or "Slow")
  521.   term.setTextColor(colors.white)
  522.   term.write(")")
  523. end
  524.  
  525. local function displayLaunchInfo()
  526.   term.setBackgroundColor(colors.black)
  527.   term.setTextColor(colors.white)
  528.   term.setCursorPos(12, 6)
  529.   term.write(tostring(currentRocket)) --# current rocket
  530.   term.setTextColor(colors.gray)
  531.   term.write(" / ")
  532.   term.setTextColor(colors.white)
  533.   term.write(tostring(numRockets))    --# total # of rockets to launch (1-1000)
  534.   term.setCursorPos(12, 8)
  535.   term.write(tostring(launchHeight))  --# this rocket's launch height (1-3)
  536.   for tmpY = 12, termY do
  537.     term.setCursorPos(1, tmpY)
  538.     term.write(string.rep(" ", termX))
  539.   end
  540.   for i = 1, numStars do              --# display star attributes
  541.     term.setCursorPos(2, i + 11)
  542.     term.setTextColor(colors.gray)
  543.     term.write(tostring(i))           --# star #
  544.     term.setCursorPos(4, i + 11)
  545.     term.setTextColor(colors.white)
  546.     if shapesList[i] then term.write(shapes[shapesList[i] + 1]) end    --# shape
  547.     term.setCursorPos(18, i + 11)
  548.     if effectsList[i] then term.write(effects[effectsList[i] + 1]) end --# effects
  549.     term.setCursorPos(35, i + 11)
  550.     term.setTextColor(colors.gray)
  551.     term.write("Colors  ")
  552.     term.setTextColor(colors.white)
  553.     term.write(tostring(colorsList[i]))                                --# colors
  554.   end
  555.   term.setTextColor(colors.red)
  556.   term.setCursorPos(32, 7)
  557.   if starError then term.write(starError:sub(1, 19)) end     --# error in star manufacturing
  558.   term.setCursorPos(32, 8)
  559.   if buildError then term.write(buildError:sub(1, 19)) end   --# error in rocket manufacturing
  560.   term.setCursorPos(32, 9)
  561.   if readyError then term.write(readyError:sub(1, 19)) end   --# launcher not ready
  562.   term.setCursorPos(32, 10)
  563.   if launchError then term.write(launchError:sub(1, 19)) end --# error in launch
  564. end
  565.  
  566. local function mainScreen()
  567.   drawHeader()
  568.   term.setCursorPos(6, 5)
  569.   term.setBackgroundColor(colors.black)
  570.   term.setTextColor(colors.white)
  571.   term.write("Show type: ")
  572.   term.setTextColor(colors.orange)
  573.   term.write(showType and "Fancy" or "Standard")
  574.   term.setTextColor(colors.white)
  575.   term.setCursorPos(30, 5)
  576.   term.write("Fireworks: ")
  577.   term.setCursorPos(6, 8)
  578.   term.write("Rocket Height")
  579.   term.setCursorPos(6, 10)
  580.   term.write("Stars/Rocket")
  581.   term.setCursorPos(6, 12)
  582.   term.write("Colors/Star")
  583.   term.setCursorPos(6, 14)
  584.   term.write("Shape Chance:")
  585.   term.setCursorPos(6, 16)
  586.   term.write("Launch Timing")
  587.   term.setCursorPos(6, 18)
  588.   term.write("Finale Mode")
  589.   term.setCursorPos(26, 14)
  590.   term.write("Effect Chance:")
  591.   term.setCursorPos(22, 7)
  592.   term.setTextColor(colors.gray)
  593.   term.write("Minimum")
  594.   term.setCursorPos(36, 7)
  595.   term.write("Maximum")
  596.   term.setTextColor(colors.gray)
  597.   local word = "F1 Help"
  598.   for i = 8, 15 do               --# F1/Help text
  599.     term.setCursorPos(termX, i)
  600.     term.write(word:sub(i - 7, i - 7))
  601.   end
  602.   term.setBackgroundColor(colors.lightGray)
  603.   term.setTextColor(showType and colors.white or colors.gray)
  604.   term.setCursorPos(23, 14)
  605.   term.write("%")                --# shapeChance
  606.   term.setCursorPos(44, 14)
  607.   term.write("%")                --# effectChance
  608.   drawSwitch(20, 16, showPacing) --# launch timing switch
  609.   drawSwitch(20, 18, finaleMode) --# finale mode switch
  610.   for _, element in pairs(guiElements.mainButtons) do
  611.     if element.getType() == "button" then
  612.       if element.getName():find("Chance") then
  613.         element.setEnabled(showType)
  614.       end
  615.     end
  616.     element.render()
  617.   end
  618. end
  619.  
  620. local function logScreen()
  621.   term.setBackgroundColor(colors.white)
  622.   for i = 4, termY do
  623.     term.setCursorPos(1, i)
  624.     term.write(string.rep(" ", termX))
  625.   end
  626.   pageNum = math.min(pageNum, numPages)
  627.   if errorLog[1] then
  628.     numPages = math.ceil(#errorLog / 13)
  629.     local currentEntry = ((pageNum - 1) * 12) + pageNum
  630.     local yPos = 5
  631.     for i = currentEntry, #errorLog do                 --# display log data
  632.       term.setCursorPos(1, yPos)
  633.       term.setTextColor(errorLog[i]:sub(1, 1) == "[" and colors.gray or colors.black)
  634.       term.write(errorLog[i]:sub(1, 7))
  635.       term.setTextColor(errorLog[i]:sub(1, 1) == "[" and colors.lightGray or colors.black)
  636.       term.write(errorLog[i]:sub(8))
  637.       yPos = yPos + 1
  638.       if yPos == termY - 1 then break end
  639.     end
  640.     term.setCursorPos(1, termY)                        --# bottom row (for page buttons)
  641.     term.setBackgroundColor(colors.lightGray)
  642.     term.write(string.rep(" ", termX))
  643.     for _, element in pairs(guiElements.logButtons) do --# page buttons
  644.       element.render()
  645.     end
  646.     term.setTextColor(colors.white)                    --# pageNum of numPages
  647.     local pages = tostring(pageNum) .. " of " .. tostring(numPages)
  648.     term.setCursorPos(math.ceil(termX / 2) - math.floor(#pages / 2) + 1, termY)
  649.     term.write(pages)
  650.   else
  651.     term.setCursorPos(2, 5)
  652.     term.setTextColor(colors.black)
  653.     term.write("No log to display")
  654.   end
  655. end
  656.  
  657. local function helpScreen()
  658.   drawHeader()
  659.   --# Sidebar
  660.   term.setBackgroundColor(colors.gray)
  661.   term.setTextColor(colors.white)
  662.   for i = 4, termY do
  663.     term.setCursorPos(1, i)
  664.     term.write(string.rep(" ", 10))
  665.   end
  666.   --# Sidebar contents
  667.   term.setCursorPos(2, 7)
  668.   term.write("Show")
  669.   term.setCursorPos(2, 8)
  670.   term.write("  Types")
  671.   term.setCursorPos(2, 12)
  672.   term.write("Launch")
  673.   term.setCursorPos(2, 13)
  674.   term.write(" Timing")
  675.   term.setCursorPos(2, 15)
  676.   term.write("Finale")
  677.   term.setCursorPos(2, 16)
  678.   term.write("   Mode")
  679.   term.setCursorPos(2, 18)
  680.   term.write("Chances")
  681.   --# Help contents
  682.   term.setBackgroundColor(colors.white)
  683.   term.setTextColor(colors.black)
  684.   term.setCursorPos(15, 6)
  685.   term.write("Standard   1-3 stars/firework")
  686.   term.setCursorPos(15, 7)
  687.   term.setTextColor(colors.lightGray)
  688.   term.write("(fixed)    ")
  689.   term.setTextColor(colors.black)
  690.   term.write("1-3 colors/star")
  691.   term.setCursorPos(15, 9)
  692.   term.write("Fancy      1-7 stars/firework")
  693.   term.setCursorPos(15, 10)
  694.   term.setTextColor(colors.lightGray)
  695.   term.write("(custom)   ")
  696.   term.setTextColor(colors.black)
  697.   term.write("1-8 colors/star")
  698.   term.setCursorPos(15, 12)
  699.   term.write("Red        Loose and lazy")
  700.   term.setCursorPos(15, 13)
  701.   term.write("Green      Quick and tight")
  702.   term.setCursorPos(15, 15)
  703.   term.write("Fires the last third of the")
  704.   term.setCursorPos(15, 16)
  705.   term.write("fireworks in rapid succession")
  706.   term.setCursorPos(15, 18)
  707.   term.write("% chance of special shape/effect")
  708. end
  709.  
  710. local function commandLineHelp()
  711.   clearScreen()
  712.   term.setCursorPos(2, 2)
  713.   term.setTextColor(colors.lightBlue)
  714.   term.write("StarCaster Commandline Help")
  715.   term.setCursorPos(2, 4)
  716.   term.setTextColor(colors.white)
  717.   term.write("The following command line options are available")
  718.   term.setCursorPos(2, 7)
  719.   term.setTextColor(colors.lightGray)
  720.   term.write("fancy  - start in 'Fancy' mode")
  721.   term.setCursorPos(2, 9)
  722.   term.write("fast   - start with 'Quick' launch timing enabled")
  723.   term.setCursorPos(2, 11)
  724.   term.write("finale - start with 'Finale Mode' enabled")
  725.   term.setCursorPos(2, 13)
  726.   term.write("debug  - auto-start a fireworks show in debug mode")
  727.   term.setCursorPos(2, 15)
  728.   term.write("splash - display splash screen on startup")
  729.   term.setCursorPos(2, 17)
  730.   term.setTextColor(colors.gray)
  731.   term.write("Command line options may be combined")
  732.   term.setTextColor(colors.white)
  733.   term.setCursorPos(1, termY)
  734. end
  735.  
  736. local function clearTables()
  737.   for i = #colorsList, 1, -1 do
  738.     colorsList[i] = nil
  739.   end
  740.   for i = #shapesList, 1, -1 do
  741.     shapesList[i] = nil
  742.   end
  743.   for i = #effectsList, 1, -1 do
  744.     effectsList[i] = nil
  745.   end
  746.   for i = #starIDTable, 1, -1 do
  747.     starIDTable[i] = nil
  748.   end
  749.   for i = #rocketIDTable, 1, -1 do
  750.     rocketIDTable[i] = nil
  751.   end
  752.   starsMade = 0
  753.   starColors = 0
  754. end
  755.  
  756. local function clearInventory()
  757.   for k, v in pairs(effectsAvailable) do
  758.     effectsAvailable[k] = 0
  759.   end
  760.   for k, v in pairs(shapesAvailable) do
  761.     shapesAvailable[k] = 0
  762.   end
  763.   for k, v in pairs(colorsAvailable) do
  764.     colorsAvailable[k] = 0
  765.   end
  766.   for k, v in pairs(rocketParts) do
  767.     rocketParts[k] = 0
  768.   end
  769. end
  770.  
  771. local function clearErrors()
  772.   starError = "none               "
  773.   buildError = "none               "
  774.   readyError = "none               "
  775.   launchError = "none               "
  776. end
  777.  
  778. local function errorScreen()
  779.   launchTimer = nil
  780.   screenTimer = nil
  781.   clearScreen(colors.white)
  782.   term.setTextColor(colors.white)
  783.   term.setBackgroundColor(colors.red)
  784.   for i = 1, 3 do
  785.     term.setCursorPos(1, i)
  786.     term.write(string.rep(" ", termX))
  787.   end
  788.   term.setCursorPos(14, 2)
  789.   term.write("StarCaster Critical Error")
  790.   term.setTextColor(colors.red)
  791.   term.setBackgroundColor(colors.white)
  792.   term.setCursorPos(2, 5)
  793.   term.write("Firework show aborted.")
  794.   if badInventory then
  795.     term.setCursorPos(2, 7)
  796.     term.write("Missing critical components.")
  797.     term.setTextColor(colors.gray)
  798.     term.setCursorPos(2, 9)
  799.     term.write("Please ensure you have the following:")
  800.     term.setTextColor(colors.black)
  801.     term.setCursorPos(2, 11)
  802.     term.write("Gunpowder, paper, and dyes are required.")
  803.     term.setTextColor(colors.gray)
  804.     term.setCursorPos(2, 13)
  805.     term.write("Optional Items:")
  806.     term.setTextColor(colors.black)
  807.     term.setCursorPos(2, 15)
  808.     term.write("Effects: glowstone dust/diamonds")
  809.     term.setCursorPos(2, 16)
  810.     term.write("Shapes: gold nugget/feather/mob head/fire charge")
  811.   else
  812.     local num = (#errorLog > 0) and math.min(9, #errorLog) or 0
  813.     term.setCursorPos(2, 7)
  814.     if num > 0 then
  815.       term.write("The following are the last " .. num .. " log entries:")
  816.       term.setTextColor(colors.black)
  817.       local yPos = 8
  818.       for i = #errorLog - (num - 1), #errorLog do
  819.         yPos = yPos + 1
  820.         term.setCursorPos(2, yPos)
  821.         term.write(errorLog[i])
  822.       end
  823.     else
  824.       term.write("There are no errors to display.")
  825.     end
  826.   end
  827.   term.setTextColor(colors.lightGray)
  828.   term.setCursorPos(10, termY)
  829.   term.write("Click mouse button to continue")
  830. end
  831.  
  832. local function postError()
  833.   badWolf, badInventory = false, false
  834.   currentRocket, abortCounter, timerTicker, waitTime = 1, 0, 0, 0.10
  835.   operatingMode = "main"
  836.   clearTables()
  837.   clearErrors()
  838.   clearScreen()
  839.   term.setCursorPos(1, 1)
  840. end
  841.  
  842. local function drawTimer()
  843.   term.setBackgroundColor(colors.black)
  844.   term.setTextColor(colors.white)
  845.   term.setCursorPos(25, 4)
  846.   term.setTextColor(colors.gray)
  847.   term.write("Timer: ")
  848.   term.setTextColor(colors.white)
  849.   term.write(waitTime .. " / " .. timerTicker .. "   ")
  850. end
  851.  
  852. local function adjustTimer()
  853.   --# Set timing for next launch
  854.   if currentRocket >= math.ceil(numRockets * 0.66) + 1 and finaleMode and not wereDoneHere and not goFinale then goFinale = true end
  855.   if goFinale then
  856.     waitTime = 0.10 * math.random(3, 5)     --# = 0.30
  857.   else
  858.     if showPacing then
  859.       waitTime = 0.10 * math.random(7, 12)  --# 5, 10
  860.     else
  861.       waitTime = 0.10 * math.random(14, 19) --# 12, 17
  862.     end
  863.   end
  864. end
  865.  
  866. local function logError(errorMessage)
  867.   local logTime = formatTime(os.time())
  868.   errorLog[#errorLog + 1] = "[" .. logTime .. "] " .. errorMessage
  869. end
  870.  
  871. local function goodShot()
  872.   clearTables()
  873.   currentRocket = currentRocket + 1
  874.   if runMode == "debug" then
  875.     print("Launch: Successful")
  876.   else
  877.     clearErrors()
  878.   end
  879. end
  880.  
  881. local function badShot()
  882.   clearTables()
  883.   if runMode == "debug" then
  884.     print("Launch: " .. launchError)
  885.   else
  886.     logError(launchError)
  887.   end
  888.   waitTime = 0.10
  889. end
  890.  
  891. local function notReady()
  892.   clearTables()
  893.   readyError = #rocketIDTable > 0 and "Launcher not ready" or "No Rocket to Launch"
  894.   if runMode == "debug" then
  895.     print(readyError)
  896.   else
  897.     logError(readyError)
  898.   end
  899.   waitTime = 0.10
  900. end
  901.  
  902. local function countItems(list)
  903.   local count = 0
  904.   for _ in pairs(list) do
  905.     count = count + 1
  906.   end
  907.   return count
  908. end
  909.  
  910. do
  911.   local colorBurst = {
  912.     [1] = "White", [2] = "Orange",
  913.     [4] = "Magenta", [8] = "Light Blue",
  914.     [16] = "Yellow", [32] = "Lime",
  915.     [64] = "Pink", [128] = "Gray",
  916.     [256] = "Light Gray", [512] = "Cyan",
  917.     [1024] = "Purple", [2048] = "Blue",
  918.     [4096] = "Brown", [8192] = "Green",
  919.     [16384] = "Red", [32768] = "Black",
  920.   }
  921.  
  922.   inventoryStar = function(id)
  923.     starColors = 0
  924.     local thisStar
  925.     local tmpEffects = { }
  926.     if type(id) == "table" then                      --# if 'id' is a table then it's from a firework
  927.       thisStar = id                                  --# set thisStar to point to id table
  928.     else                                             --# otherwise 'id' is an actual id representing a physical star
  929.       thisStar = launcher.inspectFireworkStar(id)    --# get info on the star
  930.       starIDTable[#starIDTable + 1] = id             --# add star's ID to starIDTable
  931.     end
  932.     local foundShape = false
  933.     for _, element in pairs(thisStar) do             --# Begin processing the star
  934.       if not foundShape then
  935.         for k, v in pairs(shapes) do                 --# shapeLookup
  936.           if element:find(v:sub(1, 4)) then          --# if a match is found...
  937.             shapesList[#shapesList + 1] = k - 1      --# ...add shape to shapesList table...
  938.             foundShape = true                        --# ...and indicate that we've found the shape
  939.             break
  940.           end
  941.         end
  942.       end
  943.       for k, v in pairs(colorBurst) do               --# color lookup
  944.         if element:lower():gsub("%s*", "") == v:lower():gsub("%s*", "") then --# if a matching color is found...
  945.           starColors = colors.combine(starColors, k) --# ...add it to the starColors table
  946.           break
  947.         end
  948.       end
  949.       for k, v in pairs(effectsLookup) do            --# effect lookup
  950.         if element:find(v) then                      --# if a match is found...
  951.           tmpEffects[#tmpEffects + 1] = k - 1        --# ...add effect to tmpEffects table
  952.           break
  953.         end
  954.       end
  955.     end
  956.     if not foundShape then shapesList[#shapesList + 1] = 0 end --# ensure a valid shape
  957.     local newEffect
  958.     if #tmpEffects == 2 then                                   --# if the number of effects is 2...
  959.       newEffect = 3                                            --# ...set the effect to 'both'...
  960.     else
  961.       newEffect = tmpEffects[1]                                --# ...otherwise set the effect to the first (only)
  962.     end                                                        --# vvv tmpEffects table entry vvv
  963.     if not newEffect or #tmpEffects < 1 then newEffect = 0 end --# ensure a valid effect
  964.     effectsList[#effectsList + 1] = newEffect                  --# add effect to effectsList table
  965.     colorsList[#colorsList + 1] = starColors                   --# add colors to colorsList table
  966.   end
  967. end
  968.  
  969. local function inventoryFirework(id)
  970.   local fwStarEntries = { }
  971.   local thisFirework = launcher.inspectFireworkRocket(id) --# get info on the requested rocket id
  972.   numStars = 0
  973.   if thisFirework[1] then
  974.     launchHeight = tonumber(thisFirework[1]:sub(18, 18))
  975.     for i = 2, #thisFirework do                   --# This looks for the defined shape of each firework
  976.       for k, v in pairs(shapes) do                --#   and creates a table of star index positions that
  977.         if thisFirework[i]:find(v:sub(1, 4)) then --#     will allow us to parse the data 'star by star'.
  978.           fwStarEntries[#fwStarEntries + 1] = i   --# Store the line number of the main table entry as the
  979.           break                                   --#   next star entry index in fwStarEntries
  980.         end
  981.       end
  982.     end
  983.     numStars = #fwStarEntries                    --# the number of stars equals the number of star entries
  984.     for i = 1, numStars do                       --# process each star in sequence
  985.       local newStar = { }                        --# this table will hold the info for each star as it's inventoried
  986.       local startEntry = fwStarEntries[i]        --# set the 'start' entry
  987.       local stopEntry = fwStarEntries[i + 1] and fwStarEntries[i + 1] - 1 or #thisFirework --# set the 'stop' entry
  988.       local itemCount = 0                        --# initialize counter to track the number of items/star
  989.       for j = startEntry, stopEntry do           --# collect data for this star, ending at the entry before the first entry for the next star
  990.         itemCount = itemCount + 1                --# increment the item count for this star
  991.         newStar[itemCount] = thisFirework[j]     --# record the item (shape/color/effect)
  992.       end
  993.       inventoryStar(newStar)                     --# send the newStar table to the star inventory routine
  994.     end
  995.   else                                --# empty rocket (no color/shape/effect - only paper and gp)
  996.     launchHeight = 1                  --# provide generic launchHeight so the program doesn't choke
  997.     numStars = 1                      --# provide a star to display
  998.     shapesList[#shapesList + 1] = 5   --# provide generic shape to keep shapes table in sync
  999.     effectsList[#effectsList + 1] = 0 --# provide generic effect to keep effects table in sync
  1000.     colorsList[#colorsList + 1] = 0   --# provide generic color to keep colors table in sync
  1001.   end
  1002. end
  1003.  
  1004. do
  1005.   --# lookup table for shape/effect selection and assignment
  1006.   local ingredientValues = {
  1007.     { "item.fireball", 1 };
  1008.     { "item.goldnugget", 2 };
  1009.     { "item.skull.skeleton", 3 };
  1010.     { "item.skull.wither", 3 };
  1011.     { "item.skull.zombie", 3 };
  1012.     { "item.skull.creeper", 3 };
  1013.     { "item.skull.char", 3 };
  1014.     { "item.feather", 4 };
  1015.     { "item.yellowdust", 1 };
  1016.     { "item.diamond", 2 };
  1017.   }
  1018.  
  1019.   selectStarShape = function()
  1020.     starShape = 0
  1021.     local shapeWheel = { }
  1022.     if countItems(shapesAvailable) > 0 then         --# if there is at least 1 shape available in inventory...
  1023.       for k, v in pairs(shapesAvailable) do         --# ...go through the shapesAvailable table and...
  1024.         if v > 0 then                               --# ...if there is at least 1 shape...
  1025.           shapeWheel[#shapeWheel + 1] = k           --# ...add it to the shapeWheel table
  1026.         end
  1027.       end
  1028.       if #shapeWheel < 1 then                       --# if there are no shapes available...
  1029.         starShape = 0                               --# ...set the shape to default (small ball)...
  1030.       else                                          --# ...otherwise randomize for a possible shape
  1031.         starShape = math.random(1, 100) <= shapeChance and math.random(1, #shapeWheel) or 0 --# chance of special shape
  1032.       end
  1033.       if starShape > 0 then             --# if the starShape is 'special' then...
  1034.         local newStarShape = 0          --# set temp shape to 'Small Ball' in case the inventory of the shape generated is exhausted
  1035.         for i = 1, #ingredientValues do --#...find the shape in the ingredients list and decrement the inventory accordingly
  1036.           if shapeWheel[starShape]:find(ingredientValues[i][1]) and shapesAvailable[ingredientValues[i][1]] > 0 then
  1037.             shapesAvailable[ingredientValues[i][1]] = shapesAvailable[ingredientValues[i][1]] - 1
  1038.             newStarShape = ingredientValues[i][2]   --# set the shape value
  1039.             break
  1040.           end
  1041.         end
  1042.         starShape = newStarShape                    --# commit to the new shape
  1043.       end
  1044.     else
  1045.       starShape = 0                                 --# no special star shape generated (use Small Ball)
  1046.     end
  1047.     shapesList[#shapesList + 1] = starShape         --# add shape entry to shapesList table
  1048.     return true
  1049.   end
  1050.  
  1051.   selectStarEffects = function()
  1052.     starEffect = 0
  1053.     local effectWheel = { }
  1054.     if countItems(effectsAvailable) > 0 then        --# if there's at least 1 effect available in inventory...
  1055.       for k, v in pairs(effectsAvailable) do        --# ...go through the effectsAvailalbe table and...
  1056.         if v > 0 then                               --# ...if there is at least 1 effect...
  1057.           effectWheel[#effectWheel + 1] = k         --# ...add it to the effectWheel table
  1058.         end
  1059.       end
  1060.       if #effectWheel < 1 then                      --# if there are no effects available...
  1061.         starEffect = 0                              --# ...set the effect to 'none'...
  1062.       else                                          --# ...otherwise randomize for a possible effect
  1063.         starEffect = (math.random(1, 100) <= effectChance) and math.random(1, #effectWheel + 1) or 0 --# chance to generate either trail, sparkle, both, or none
  1064.       end
  1065.       if #effectWheel < 2 then starEffect = math.min(starEffect, #effectWheel) end  --# ensure a valid value
  1066.       if starEffect > 0 and starEffect <= #effectWheel then --# if a starEffect is generated...
  1067.         local newStarEffect = 0                             --# set temp effect to 'none' in case the inventory of the effect generated is exhausted
  1068.         for i = 1, #ingredientValues do                     --# ...find the effect in the ingredients list and decrement the inventory accordingly
  1069.           if effectWheel[starEffect]:find(ingredientValues[i][1]) and effectsAvailable[ingredientValues[i][1]] > 0 then
  1070.             effectsAvailable[ingredientValues[i][1]] = effectsAvailable[ingredientValues[i][1]] - 1
  1071.             newStarEffect = ingredientValues[i][2]  --# set the effect value
  1072.             break
  1073.           end
  1074.         end
  1075.         starEffect = newStarEffect                  --# commit to the new effect
  1076.       end
  1077.     else
  1078.       starEffect = 0                                --# no special star effect generated (use 'none')
  1079.     end
  1080.     effectsList[#effectsList + 1] = starEffect      --# add effect entry to effectsList table
  1081.     return true
  1082.   end
  1083. end
  1084.  
  1085. local function selectStarColors()
  1086.   starColors = 0
  1087.   local colorWheel = { }
  1088.   local colorsCount = countItems(colorsAvailable) --# determine the number of colors available
  1089.   if colorsCount > 0 then                         --# if there is at least 1 color available...
  1090.     local numColors = math.random(minColors, maxColors) --# ...choose a random color from the list
  1091.     local magicNumber = math.min(rocketParts["item.sulphur"] - launchHeight, colorsCount) --# set a base minimum based on gunpowder and # of colors available
  1092.     numColors = math.min(numColors, magicNumber)  --# adjust # of colors based on the magicNumber
  1093.     local debit = 0                               --# this tracks the number of crafting spaces required for shapes and effects
  1094.     if #shapesList > #colorsList then             --# if a special shape is assigned...
  1095.       debit = debit + 1                           --# ...increment debit counter to reduce number of colors used
  1096.     end
  1097.     if #effectsList > #colorsList then            --# if a special effect is assigned...
  1098.       if effectsList[#effectsList] == 3 then      --# parse the effect to see if it's 1 or 2 effects
  1099.         debit = debit + 2                         --# two effects results in a reduction of the number of colors by 2
  1100.       else
  1101.         debit = debit + 1                         --# one effect results in a reduction of the number of colors by 1
  1102.       end
  1103.     end
  1104.     numColors = math.min(numColors, 8 - debit)    --# adjust numColors downward (as necessary) so we don't try to use too many crafting slots
  1105.     for i = 1, numColors do                       --# begin generating the list of available colors
  1106.       for k, v in pairs(colorsAvailable) do       --#   from the main inventory
  1107.         if v > 0 then                             --# if the amount of the color selected in inventory is > 1...
  1108.           colorWheel[#colorWheel + 1] = k         --# ...add the entry to the colorWheel for processing
  1109.         end
  1110.       end
  1111.       local newColor = colorWheel[math.random(1, #colorWheel)]  --# generate a random color from the colorWheel
  1112.       starColors = colors.combine(starColors, newColor)         --# add the color to the current list of colors for this star
  1113.       colorsAvailable[newColor] = colorsAvailable[newColor] - 1 --# decrement the inventory entry for the color
  1114.     end
  1115.     colorsList[#colorsList + 1] = starColors                    --# add the combined color value to the colorList table
  1116.     return true
  1117.   else
  1118.     starError = "Make Star: Out of color"
  1119.     if runMode == "debug" then
  1120.       print(starError)
  1121.     else
  1122.       logError(starError)
  1123.     end
  1124.     badWolf, badInventory = true, true
  1125.     return false
  1126.   end
  1127. end
  1128.  
  1129. local function makeStar()
  1130.   if not selectStarShape() then return false end   --# if there is no defined shape, fail the process
  1131.   if not selectStarEffects() then return false end --# if there is no defined effect, fail the process
  1132.   if not selectStarColors() then return false end  --# if there is no defined color, fail the process
  1133.   return launcher.craftFireworkStar(starColors, starShape, starEffect) --# craft the star and return the result
  1134. end
  1135.  
  1136. local function assembleStars(numberToMake)
  1137.   local starFailures, starNum = 0, 1
  1138.   for i = 1, numberToMake * 3 do             --# this allows an average of 3 tries per star
  1139.     local starSuccess, starID = makeStar()   --# call the makeStar() process
  1140.     if starSuccess then                      --# if successful...
  1141.       rocketParts["item.sulphur"] = rocketParts["item.sulphur"] - 1 --# ...remove 1 gun powder from the resource list
  1142.       starIDTable[#starIDTable + 1] = starID --# update the starIDTable
  1143.       if runMode == "debug" then
  1144.         print("Make Star # " .. starNum)
  1145.       end
  1146.       starNum = starNum + 1     --# which star the builder is currently working on
  1147.       starsMade = starsMade + 1 --# how many stars have been made
  1148.       if starsMade >= numberToMake then return true end --# if all stars are complete return true
  1149.     else                        --# if the star isn't made for some reason increment the fail counter
  1150.       if starID then
  1151.         starError = starID
  1152.         if starError:find("gunpowder") then
  1153.           badWolf, badInventory = true, true
  1154.         end
  1155.       else
  1156.         starError = "Shape/Effect/Color failure"
  1157.       end
  1158.       if runMode == "debug" then
  1159.         print("Star#" .. starNum .. ": " .. starError)
  1160.       else
  1161.         logError(starNum .. ":" .. starError)
  1162.       end
  1163.       if badWolf then return false end
  1164.       starFailures = starFailures + 1
  1165.     end
  1166.   end
  1167.   if runMode == "debug" then
  1168.     print("Make Star: " .. starFailures .. " failures")
  1169.   else
  1170.     logError("Make Star: " .. starFailures .. " failures")
  1171.   end
  1172.   if starsMade > 1 then --# if the number of stars made is > 1...
  1173.     numStars = math.min(starsMade, numStars) --# ...set numStars equal to the lower value between starsMade and numStars
  1174.     return true         --# if at least one star was made allow the rocket to be finished
  1175.   else
  1176.     return false        --# no stars made - complete failure
  1177.   end
  1178. end
  1179.  
  1180. local function processStars()
  1181.   local starsBuilt = launcher.getFireworkStarIds() --# get the number of stars already existing
  1182.   if #starsBuilt > 0 then   --# query any pre-made stars
  1183.     for i = 1, #starsBuilt do
  1184.       inventoryStar(starsBuilt[i])
  1185.     end
  1186.   end
  1187.   starsMade = #starsBuilt   --# set the number of stars already made to equal the number of stars already existing
  1188.   local starsToBeMade = math.max(0, numStars - starsMade) --# adjust the number of stars to be made downward as necessary
  1189.   if runMode == "debug" then
  1190.     print("# of stars planned / built: " .. numStars .. " / " .. starsMade)
  1191.     print("# of stars to be made: " .. starsToBeMade)
  1192.   end
  1193.   if starsToBeMade < 1 then --# if there are no more stars to make
  1194.     return true             --# return true
  1195.   else                      --# otherwise...
  1196.     return(assembleStars(starsToBeMade)) --# make the number of stars remaining and return the result
  1197.   end
  1198. end
  1199.  
  1200. local function assembleFirework()
  1201.   local firework
  1202.   if runMode == "debug" then
  1203.     print("Start Rocket # " .. currentRocket)
  1204.   end
  1205.   for k, v in pairs(rocketParts) do
  1206.     if k:find("sulphur") and v >= launchHeight then --# double check if we have enough gunpowder (we already checked paper)
  1207.       firework, buildError = pcall(launcher.craftFireworkRocket, launchHeight, starIDTable) --# assemble the firework
  1208.       if firework then                              --# if the build was successful...
  1209.         rocketParts["item.sulphur"] = rocketParts["item.sulphur"] - launchHeight --# decrement the inventory
  1210.         rocketParts["item.paper"] = rocketParts["item.paper"] - 1
  1211.         if runMode == "debug" then
  1212.           print("Rocket # " .. currentRocket .. " built")
  1213.         end
  1214.         return true
  1215.       else                                          --# if the build was unsuccessful, log an error and return false
  1216.         if runMode == "debug" then
  1217.           print(buildError)
  1218.         else
  1219.           logError(buildError)
  1220.           if buildError:find("gunpowder") or buildError:find("paper") then
  1221.             badWolf, badInventory = true, true
  1222.           end
  1223.         end
  1224.         return false
  1225.       end
  1226.     end
  1227.   end
  1228.   badWolf, badInventory = true, true
  1229.   if runMode == "debug" then
  1230.     print("Abort Assembly: Missing critical materials")
  1231.   else
  1232.     logError("Abort Assembly: Missing critical materials")
  1233.   end
  1234.   return false
  1235. end
  1236.  
  1237. local function launchFirework()
  1238.   local fireThatSucker
  1239.   if launcher.canLaunch() then
  1240.     fireThatSucker, launchError = pcall(launcher.launch)
  1241.   else
  1242.     notReady()
  1243.     return false
  1244.   end
  1245.   if fireThatSucker then
  1246.     goodShot()
  1247.     return true
  1248.   else
  1249.     badShot()
  1250.     return false
  1251.   end
  1252. end
  1253.  
  1254. local function takeStock() --# get a listing of all the items in the inventory
  1255.   local items = { }
  1256.   for slot = 1, launcher.getInventorySize() do
  1257.     local stack = launcher.getStackInSlot(slot)
  1258.     if stack then --# construct a new table with only the info we need, the name, the qty, and the damage (to tell dyes apart)
  1259.       table.insert(items, { raw = stack.raw_name; qty = stack.qty; dmg = stack.dmg })
  1260.     end
  1261.   end
  1262.   return items
  1263. end
  1264.  
  1265. local function dyeToColor(stack)
  1266.   assert(stack.raw:find("item.dyepowder"), "item stack supplied is not a dye")
  1267.   return 2 ^ (15 - stack.dmg) --# CC colours are the opposite to MC colours, so in CC white is 0, but in MC it is 15, so we must flip it
  1268. end
  1269.  
  1270. do
  1271.   --# reference table for what we want, and what table it maps to
  1272.   local inventoryCrossRef = {
  1273.     { "item.paper", rocketParts };
  1274.     { "item.sulphur", rocketParts };
  1275.     { "item.dyepowder", colorsAvailable };
  1276.     { "item.yellowdust", effectsAvailable };
  1277.     { "item.diamond", effectsAvailable };
  1278.     { "item.goldnugget", shapesAvailable };
  1279.     { "item.fireball", shapesAvailable };
  1280.     { "item.feather", shapesAvailable };
  1281.     { "item.skull", shapesAvailable };
  1282.   }
  1283.  
  1284.   filter = function(stock) --# filter out the listings of everything that is in the launcher to check for what we have that we want
  1285.     for _, info in pairs(stock) do                --# for all the items
  1286.       for _, v in pairs(inventoryCrossRef) do     --# for all that we want
  1287.         if info.raw:find(v[1]) then               --# if the raw name contains what we're looking for i.e. item.dyepowder will be found in item.dyepowder.blue
  1288.           local ok, col = pcall(dyeToColor, info) --# attempt to convert it to a colour
  1289.           if ok then                              --# if converstion worked
  1290.             v[2][col] = (v[2][col] and v[2][col] or 0) + info.qty --# update the count of it
  1291.           else
  1292.             v[2][info.raw] = (v[2][info.raw] and v[2][info.raw] or 0) + info.qty --# update the count of the other item
  1293.           end
  1294.         end
  1295.       end
  1296.     end
  1297.   end
  1298. end
  1299.  
  1300. local function checkCreative()
  1301.   if launcher.isCreativeLauncher() then
  1302.     colorsAvailable = { [1] = 10000, [2] = 10000, [4] = 10000, [8] = 10000, [16] = 10000, [32] = 10000, [64] = 10000, [128] = 10000, [256] = 10000, [512] = 10000, [1024] = 10000, [2048] = 10000, [4096] = 10000, [8192] = 10000, [16384] = 10000, [32768] = 10000 }
  1303.     effectsAvailable = { ["item.diamond"] = 10000, ["item.yellowdust"] = 10000 }
  1304.     shapesAvailable = { ["item.feather"] = 10000, ["item.fireball"] = 10000, ["item.goldnugget"] = 10000, ["item.skull.skeleton"] = 10000 }
  1305.     rocketParts = { ["item.sulphur"] = 10000, ["item.paper"] = 10000 }
  1306.     return true
  1307.   end
  1308.   return false
  1309. end
  1310.  
  1311. local function checkInventory()
  1312.   clearInventory()
  1313.   if checkCreative() then return true end
  1314.   local gp, gs, co = false, false, false  --# gunpowder, paper, color (don't know why I used gs for paper)
  1315.   filter(takeStock()) --# at this point the item tables should contain everything that the inventory has, that we need
  1316.   for k, v in pairs(rocketParts) do
  1317.     if k:find("sulphur") and v > 1 then   --# need at least two GP - 1 for rocket, 1 for star
  1318.       gp = true
  1319.     end
  1320.     if k:find("paper") and v > 0 then     --# need at least 1 paper for the rocket
  1321.       gs = true
  1322.     end
  1323.   end
  1324.   if countItems(colorsAvailable) > 0 then --# need at least 1 color for a star
  1325.     co = true
  1326.   end
  1327.   if gp and gs and co then                --# if there are sufficient resources to build at least 1 rocket...
  1328.     return true                           --# ...return true
  1329.   end
  1330.   return false                            --# ...otherwise return false
  1331. end
  1332.  
  1333. local function minRequirements()
  1334.   local newMaxHeight = math.min(maxHeight, rocketParts["item.sulphur"] - 1) --# adjust max launchHeight downward to account for low gunpowder (leaving 1 gunpowder for a star)
  1335.   if newMaxHeight < 1 then                                   --# if there is < 1 gunpowder then error out
  1336.     if runMode == "debug" then
  1337.       print("Rocket: Insufficient gunpowder")
  1338.     else
  1339.       logError("Rocket: Insufficient gunpowder")
  1340.       badWolf, badInventory = true, true
  1341.     end
  1342.     return false
  1343.   end
  1344.   if rocketParts["item.paper"] < 1 then                      --# if there is < 1 paper then error out
  1345.     if runMode == "debug" then
  1346.       print("Rocket: Insufficient paper")
  1347.     else
  1348.       logError("Rocket: Insufficient paper")
  1349.       badWolf, badInventory = true, true
  1350.     end
  1351.     return false
  1352.   end
  1353.   local newMinHeight = minHeight
  1354.   if goFinale then newMinHeight = math.max(2, minHeight) end --# if the finale has started, raise the minimum launch height to 2 if it is 1
  1355.   newMinHeight = math.min(newMinHeight, newMaxHeight)        --# adjust the minimum height down as necessary
  1356.   launchHeight = math.random(newMinHeight, newMaxHeight)     --# set the launchHeight
  1357.   numStars = math.random(minStars, maxStars)                 --# determine the number of stars for the firework
  1358.   if launchHeight == 3 then numStars = math.min(numStars, 5) end --# adjust number of stars down as for launchHeight
  1359.   if launchHeight == 2 then numStars = math.min(numStars, 6) end --# adjust number of stars down for launchHeight
  1360.   numStars = math.min(numStars, rocketParts["item.sulphur"] - launchHeight) --# adjust number of stars down if gunpowder is low
  1361.   numStars = math.min(numStars, countItems(colorsAvailable)) --# adjust number of stars down if colors are low
  1362.   if numStars < 1 then                                       --# if the adjusted number of stars is < 1 error out
  1363.     if runMode == "debug" then
  1364.       print("Stars: Insufficient gunpowder or color")
  1365.     else
  1366.       logError("Stars: Insufficient gunpowder or color")
  1367.       badWolf, badInventory = true, true
  1368.     end
  1369.     return false
  1370.   end
  1371.   return true
  1372. end
  1373.  
  1374. local function workFlow()
  1375.   if badWolf then return false end
  1376.   rocketIDTable = launcher.getFireworkRocketIds() --# collect IDs of any rockets in the buffer
  1377.   if #rocketIDTable > 0 then            --# if there is at least 1 rocket in the buffer then get info and launch, otherwise generate a new one
  1378.     if runMode == "debug" then
  1379.       print("Using finished rocket")
  1380.     end
  1381.     inventoryFirework(rocketIDTable[1]) --# a rocket exists - get rocket info (launchHeight and star inventory) and launch
  1382.     if runMode == "debug" then
  1383.       print("Launching rocket")
  1384.     else
  1385.       displayLaunchInfo()
  1386.     end
  1387.     return launchFirework()             --# launch the pre-built rocket
  1388.   else                                  --# if no rocket is pre-existing, then build and launch a rocket
  1389.     if not minRequirements() then return false end --# sanity check - this ensures we have enough gunpowder, paper, and colors to launch at least one firework
  1390.     if processStars() then              --# create stars
  1391.       if runMode ~= "debug" then displayLaunchInfo() end
  1392.       if assembleFirework() then        --# assemble the rocket
  1393.         if launchFirework() then        --# launch the bird
  1394.           return true
  1395.         end
  1396.       end
  1397.       return false
  1398.     end
  1399.     return false
  1400.   end
  1401. end
  1402.  
  1403. local function launchControl()
  1404.   if badWolf then return false end
  1405.   local launchSuccess = workFlow()     --# process and launch the next firework
  1406.   if launchSuccess then                --# if the process was successful...
  1407.     if currentRocket > numRockets then --# ...check if the show is over then stop if it is
  1408.       if runMode == "debug" then
  1409.         runMode = "stop"
  1410.         operatingMode = "debug"
  1411.       else
  1412.         waitTime = 0
  1413.         wereDoneHere = true
  1414.         screenTimer = os.startTimer(2)
  1415.         return
  1416.       end
  1417.     else
  1418.       adjustTimer()                    --# ...if the show isn't over, adjust the timer and continue
  1419.     end
  1420.   else
  1421.     abortCounter = abortCounter + 1    --# a major failure of some sort (not show stopping) - increment the abort counter
  1422.     waitTime = 0.10
  1423.     if abortCounter >= 3 then          --# 3 major failures - time to stop the show
  1424.       if runMode == "debug" then
  1425.         print("Too many failures, aborting")
  1426.       else
  1427.         logError("Too many failures, aborting")
  1428.       end
  1429.       badWolf = true
  1430.     else
  1431.       if runMode == "debug" then
  1432.         print("Unable to launch " .. abortCounter .. " times")
  1433.       else
  1434.         logError("Unable to launch " .. abortCounter .. " times")
  1435.       end
  1436.     end
  1437.   end
  1438. end
  1439.  
  1440. local function showControl()
  1441.   if badWolf then
  1442.     if runMode == "debug" then
  1443.       runMode = "stop"
  1444.       operatingMode = "debug"
  1445.     end
  1446.     return
  1447.   end
  1448.   local event, data, x, y = os.pullEvent()
  1449.   if event == "timer" and data == launchTimer then
  1450.     if runMode ~= "debug" then
  1451.       drawTimer()
  1452.     end
  1453.     timerTicker = timerTicker + 0.1 --# increment the timer counter
  1454.     if currentRocket <= numRockets and timerTicker >= waitTime then --# time to launch another firework
  1455.       timerTicker = 0
  1456.       if runMode == "debug" then
  1457.         print("Timer Cycle")
  1458.       end
  1459.       launchControl()        --# initiate the build/launch process
  1460.     end
  1461.     launchTimer = os.startTimer(TIMER_GRANULARITY)     --# restart the timer
  1462.   elseif event == "timer" and data == screenTimer then --# if the show is over return to the main screen
  1463.     operatingMode = "main"   --# this tells the main controller loop (scKernel) which functions to monitor
  1464.     waitTime = 0.10
  1465.     timerTicker = 0
  1466.     mainScreen()
  1467.   elseif event == "mouse_click" and runMode ~= "debug" and y == 2 and x > termX - 10 and x < termX - 1 then --# if the user clicks 'cancel', end the show and return to the main screen
  1468.     operatingMode = "main"   --# this tells the main controller loop (scKernel) which functions to monitor
  1469.     wereDoneHere = false
  1470.     goFinale = false
  1471.     currentRocket = 1  
  1472.     mainScreen()
  1473.   elseif event == "char" and (data:lower() == "q" or data:lower() == "c" or data:lower() == "x") then --# if the user types 'q', 'c', or 'x', end the show, and return to the main screen if not in debug mode
  1474.     if runMode == "debug" then
  1475.       operatingMode = "debug"
  1476.       runMode = "stop"
  1477.     else
  1478.       operatingMode = "main" --# this tells the main controller loop (scKernel) which functions to monitor
  1479.       wereDoneHere = false
  1480.       goFinale = false
  1481.       currentRocket = 1  
  1482.       mainScreen()
  1483.     end
  1484.   end
  1485. end
  1486.  
  1487. local function startShow()
  1488.   sessionNumber = sessionNumber + 1 --# Increment the session number
  1489.   if runMode ~= "debug" then        --# Create and insert the show # log separator
  1490.     local lastPart = (sessionNumber < 10 and string.rep("-", math.ceil(termX / 2) - (5 + math.floor(#tostring(sessionNumber))))  or string.rep("-", math.ceil(termX / 2) - (4 + math.floor(#tostring(sessionNumber)))))
  1491.     local separator = string.rep("-", math.floor(termX / 2) - (3 + math.ceil(#tostring(sessionNumber) / 2))) .. " Show" .. (sessionNumber < 10 and "  " or " ") .. "#" .. tostring(sessionNumber) .. " " .. lastPart
  1492.     if sessionNumber > 99 and sessionNumber < 1000 then
  1493.       separator = "-" .. separator
  1494.     elseif sessionNumber > 999 then
  1495.       separator = separator .. "-"
  1496.     end
  1497.     errorLog[#errorLog + 1] = separator
  1498.   end
  1499.   if not checkInventory() then      --# if there isn't sufficient inventory then error out
  1500.     operatingMode = "error"
  1501.     badWolf, badInventory = true, true
  1502.     if runMode == "debug" then
  1503.       print("Abort @ start: Missing critical components")
  1504.     else
  1505.       logError("Abort @ start: Missing critical components")
  1506.     end
  1507.     return false
  1508.   end                      --# if the program didn't error out then set the necessary variables and start the show
  1509.   operatingMode = "show"   --# this tells the controller loop (scKernel) which functions to monitor
  1510.   wereDoneHere = false
  1511.   goFinale = false
  1512.   starsMade = 0
  1513.   starColors = 0
  1514.   currentRocket = 1
  1515.   abortCounter = 0
  1516.   waitTime = 0.10
  1517.   timerTicker = 0
  1518.   numStars = 1
  1519.   if runMode ~= "debug" then staticLaunchScreen() end --# show the launch info screen if not in debug mode
  1520.   launchTimer = os.startTimer(TIMER_GRANULARITY)      --# this starts the launch timer which is managed by showControl()
  1521. end
  1522.  
  1523. local function selectPage()
  1524.   term.setBackgroundColor(colors.gray)
  1525.   term.setTextColor(colors.lightGray)
  1526.   for i = 1, 3 do                             --# draw the box
  1527.     term.setCursorPos(math.ceil(termX / 2) - 3, termY - 4 + i)
  1528.     term.write(string.rep(" ", 8))
  1529.   end
  1530.   term.setCursorPos(math.ceil(termX / 2) - 2, termY-3)
  1531.   term.write("Page #")                        --# draw the 'title'
  1532.   term.setCursorPos(math.ceil(termX / 2) - 2, termY-2)
  1533.   term.setBackgroundColor(colors.lightGray)
  1534.   term.write(string.rep(" ", 6))              --# draw the input area
  1535.   term.setCursorPos(math.ceil(termX / 2) - 1, termY-2)
  1536.   term.setTextColor(colors.white)
  1537.   local newPage = tonumber(read(nil, nil, 4)) --# get the selected page from the user
  1538.   if not newPage then newPage = pageNum end   --# if no input then stay on the same page
  1539.   pageNum = math.max(1, math.min(newPage, newPages)) --# make sure page # is valid
  1540.   drawHeader()
  1541. end
  1542.  
  1543. local function logInput()
  1544.   local event, button, x, y = os.pullEvent()
  1545.   if event == "mouse_click" then
  1546.     if x > 48 and y < 4 and button == 1 then --# exit button
  1547.       operatingMode = "main"
  1548.       mainScreen()
  1549.       return
  1550.     elseif x > 20 and x < 31 and y == termY and numPages > 1 then --# 'Go To Page'
  1551.       selectPage()
  1552.       logScreen()
  1553.     else
  1554.       for _, click in pairs(guiElements.logButtons) do --# process buttons
  1555.         click.processEvent(event, button, x, y)
  1556.       end
  1557.     end
  1558.   elseif event == "mouse_scroll" then
  1559.     pageNum = button == 1 and math.min(numPages, pageNum + 1) or math.max(1, pageNum - 1)
  1560.     logScreen()
  1561.   elseif event == "key" then
  1562.     if button == keys.home or button == keys["end"] then
  1563.       pageNum = button == keys.home and 1 or numPages
  1564.       logScreen()
  1565.     elseif button == keys.pageUp or button == keys.pageDown then
  1566.       pageNum = button == keys.pageUp and math.max(1, pageNum - 1) or math.min(numPages, pageNum + 1)
  1567.       logScreen()
  1568.     end
  1569.   end
  1570. end
  1571.  
  1572. local function mainInput()
  1573.   local event, button, x, y = os.pullEvent()
  1574.   if event == "mouse_click" then
  1575.     if x > 48 and y < 4 and button == 1 and not popUp then    --# big red X
  1576.       if not gettingHelp then                                 --# exit StarCaster
  1577.         runMode = "stop"
  1578.         return
  1579.       elseif gettingHelp then                                 --# exit help
  1580.         gettingHelp = false
  1581.         mainScreen()
  1582.         return
  1583.       end
  1584.     elseif x < 4 and y < 4  and button == 1 and not gettingHelp and not popUp then --# open logs
  1585.       operatingMode = "logs"
  1586.       drawHeader()
  1587.       logScreen()
  1588.       return
  1589.     elseif y == 14 and button == 2 and not gettingHelp and not popUp then
  1590.       if x > 19 and x < 24 then                               --# shapeChance popup
  1591.         drawPopUp("shape")
  1592.       elseif x > 40 and x < 45 then                           --# effectChance popup
  1593.         drawPopUp("effect")
  1594.       end
  1595.     elseif y == 16 and x > 19 and x < 24 and not gettingHelp and not popUp then --# showPacing (launch timing)
  1596.       if button == 1 then
  1597.         showPacing = not showPacing
  1598.         drawSwitch(20, 16, showPacing)
  1599.       elseif button == 2 then
  1600.         drawPopUp("pacing")
  1601.       end
  1602.     elseif y == 18 and x > 19 and x < 24 and not gettingHelp and not popUp then --# finaleMode
  1603.       if button == 1 then
  1604.         finaleMode = not finaleMode
  1605.         drawSwitch(20, 18, finaleMode)
  1606.       elseif button == 2 then
  1607.         drawPopUp("finale")
  1608.       end
  1609.     elseif y > 7 and y < 15 and x == termX and not gettingHelp and not popUp then --# F1 Help
  1610.       gettingHelp = true
  1611.       helpScreen()
  1612.     elseif popUp then
  1613.       popUp = false
  1614.       mainScreen()
  1615.     else
  1616.       if not gettingHelp and not popUp then
  1617.         for _, click in pairs(guiElements.mainButtons) do     --# process buttons
  1618.           click.processEvent(event, button, x, y)             --# carry out action for clicked button
  1619.           if click.getType() == "picker" and operatingMode == "main" and not gettingHelp and not popUp then --# this second full sanity check is necessary since we've already processed the button event and things have changed
  1620.             updateVariable(click.getName(), click.getValue()) --# update the actual variable associated with the button
  1621.             click.render()                                    --# draw buttons
  1622.           end
  1623.         end
  1624.       end
  1625.     end
  1626.   elseif event == "key" and button == keys.f1 then
  1627.     gettingHelp = not gettingHelp
  1628.     if gettingHelp then
  1629.       helpScreen()
  1630.     else
  1631.       mainScreen()
  1632.     end
  1633.   end
  1634. end
  1635.  
  1636. local function scKernel() --# this is the main loop
  1637.   while true do
  1638.     if badWolf and runMode ~= "debug" and runMode ~= "stop" then  --# if there's a 'critical' error show the error screen
  1639.       errorScreen()
  1640.       os.pullEvent("mouse_click")
  1641.       postError()
  1642.       mainScreen()
  1643.     elseif operatingMode == "main" and runMode == "standard" then --# main UI / input
  1644.       mainInput()
  1645.     elseif operatingMode == "logs" then                           --# logs UI / input
  1646.       logInput()
  1647.     elseif operatingMode == "show" then                           --# show UI / input
  1648.       showControl()
  1649.     elseif runMode == "debug" and operatingMode == "main" then    --# auto-start show when in debug mode
  1650.       startShow()
  1651.     end
  1652.     if runMode == "stop" then                                     --# exit and terminate program
  1653.       if operatingMode ~= "debug" then
  1654.         clearScreen()
  1655.         term.setTextColor(colors.white)
  1656.         term.setCursorPos(1, 1)
  1657.       end
  1658.       return
  1659.     end
  1660.   end
  1661. end
  1662.  
  1663. local function initHardware()
  1664.   local notFind, perp = false
  1665.   launcher = peripheral.find("firework_launcher")
  1666.   if not launcher then
  1667.     notFind = true
  1668.     perp = "a \n   MoarPeripherals Fireworks Launcher"
  1669.   end
  1670.   if not term.isColor() or pocket or turtle then
  1671.     notFind = true
  1672.     perp = "an \n   Advanced Computer"
  1673.   end
  1674.   if notFind then
  1675.     clearScreen()
  1676.     term.setTextColor(colors.white)
  1677.     term.setCursorPos(2, 2)
  1678.     write("StarCaster requires " .. perp)
  1679.     term.setCursorPos(1, 5)
  1680.     return false
  1681.   end
  1682.   if not checkInventory() then
  1683.     badWolf, badInventory = true, true
  1684.     errorScreen()
  1685.     os.pullEvent("mouse_click")
  1686.     clearScreen()
  1687.     term.setCursorPos(1, 1)
  1688.     return false
  1689.   end
  1690.   return true
  1691. end
  1692.  
  1693. if not os.getComputerLabel() then os.setComputerLabel("StarCaster") end
  1694.  
  1695. if not initHardware() then return end
  1696.  
  1697. if tArgs[1] then
  1698.   if tArgs[1] == "?" or tArgs[1]:sub(1, 1) == "/" or tArgs[1]:sub(1, 1) == "-" then
  1699.     commandLineHelp()
  1700.     return
  1701.   else
  1702.     for i = 1, #tArgs do
  1703.       if string.lower(tArgs[i]) == "fancy" then
  1704.         showType = true
  1705.       elseif string.lower(tArgs[i]) == "fast" then
  1706.         showPacing = true
  1707.       elseif string.lower(tArgs[i]) == "finale" then
  1708.         finaleMode = true
  1709.       elseif string.lower(tArgs[i]) == "debug" then
  1710.         runMode = "debug"
  1711.       elseif string.lower(tArgs[i]) == "splash" then
  1712.         clearScreen()
  1713.         local splash = {
  1714.           { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32768, 32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
  1715.           { 0, 0, 0, 0, 16384, 16384, 16384, 16384, 16384, 0, 0, 16, 16, 16, 16, 16, 32768, 0, 0, 0, 32, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 32768, 0, 0, 8192, 0, 0, 2, 0, 2048, 0, 0, 0, 0, },
  1716.           { 0, 0, 0, 16384, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 32768, 32, 0, 32, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1024, 0, 32768, 0, 0, 1024, 0, 0, 0, 0, 1, 0, 0, },
  1717.           { 0, 0, 0, 0, 16384, 16384, 16384, 16384, 0, 0, 0, 0, 0, 16, 0, 0, 0, 32768, 32, 0, 0, 0, 32, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, 0, 0, 8192, 0, 32768, 0, 0, 8192, },
  1718.           { 0, 0, 0, 0, 0, 0, 0, 32768, 16384, 0, 0, 0, 0, 16, 0, 0, 0, 32768, 32, 32, 32, 32, 32, 32768, 0, 4, 0, 32768, 32768, 4, 0, 0, 0, 0, 0, 8192, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 512, 0, 0, },
  1719.           { 0, 0, 0, 16384, 16384, 16384, 16384, 16384, 0, 0, 0, 0, 0, 16, 0, 0, 32768, 32768, 32, 0, 0, 0, 32, 0, 0, 4, 0, 0, 32768, 4, 0, 0, 0, 32768, 0, 0, 0, 32768, 32768, 1024, 0, 0, 512, 0, 2, 0, 0, 16, 0, },
  1720.           { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 8192, 0, 0, 0, 0, 1, 0, 32768, 0, },
  1721.           { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2048, 0, 0, 32768, 1024, 32768, },
  1722.           { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32768, 32768, 32768, 32768, 32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024, 0, 2, 0, 32768, 8192, 0, 0, 0, },
  1723.           { 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 32768, 0, 8, 0, 0, 0, 0, 8, 8, 8, 8, 32768, 8, 8, 8, 8, 8, 32768, 8, 8, 8, 8, 32768, 32768, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 32768, 0, 32768, 0, },
  1724.           { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 32768, 8, 0, 8, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 32768, 0, 0, 8, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 32768, 0, },
  1725.           { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 8, 32768, 0, 0, 8, 0, 0, 8, 8, 8, 32768, 0, 0, 0, 8, 32768, 0, 0, 8, 8, 8, 0, 0, 0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
  1726.           { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 8, 32768, 0, 0, 8, 0, 0, 0, 8, 32768, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
  1727.           { 0, 0, 0, 32768, 8, 8, 8, 8, 8, 0, 8, 32768, 0, 0, 8, 0, 8, 8, 8, 8, 0, 0, 0, 32768, 8, 0, 0, 0, 8, 8, 8, 8, 8, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
  1728.           { 0, 0, 0, 0, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 0, 0, 0, 32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
  1729.         }
  1730.         paintutils.drawImage(splash, 1, 1)
  1731.         term.setCursorPos(15, termY - 1)
  1732.         term.setTextColor(colors.gray)
  1733.         term.write("By: Dog and TheOriginalBIT")
  1734.         sleep(2)
  1735.       end
  1736.     end
  1737.   end
  1738. end
  1739.  
  1740. if not showType then maxStars, maxColors = 3, 3 end
  1741.  
  1742. --# x pos, y pos, width, height, text, background color, text color, function, name, button to activate (optional)
  1743. guiElements = {
  1744.   mainButtons = {
  1745.     newButton(26, 5, 2, 1, "..", colors.gray, colors.white, function() drawPopUp("type") end, "showPop", 2);                      --# showType popup
  1746.     newButton(26, 5, 2, 1, "..", colors.gray, colors.white, function() switchShowType() end, "showType", 1);                      --# showType
  1747.     newButton(41, 5, 4, 1, "25", colors.lightGray, colors.white, function() drawPopUp("rockets") end, "rocketPop", 2);            --# numRockets popup
  1748.     newButton(41, 5, 4, 1, "25", colors.lightGray, colors.white, function() getNumRockets() end, "numRockets", 1);                --# numRockets
  1749.     newButton(20, 14, 3, 1, "30", colors.lightGray, colors.white, function() getChance("shape", 20, 14) end, "shapeChance", 1);   --# Shape percent chance
  1750.     newButton(41, 14, 3, 1, "30", colors.lightGray, colors.white, function() getChance("effect", 41, 14) end, "effectChance", 1); --# Effect percent chance
  1751.     newButton(26, 16, 19, 3, "Start  Show", colors.green, colors.white, function() startShow() end, "startShow", 1);              --# startShow
  1752.     --# add new buttons and pickers here, not at the end
  1753.     newNumberPicker(20, 8, 1, 1, 3, "minHeight", false, pickerChanged);          --# rocketHeight min
  1754.     newNumberPicker(34, 8, 3, 1, 3, "maxHeight", false, pickerChanged);          --# rocketHeight max
  1755.     newNumberPicker(20, 10, 1, 1, 7, "minStars", false, pickerChanged);          --# minStars
  1756.     newNumberPicker(34, 10, maxStars, 1, 7, "maxStars", false, pickerChanged);   --# maxStars
  1757.     newNumberPicker(20, 12, 1, 1, 8, "minColors", false, pickerChanged);         --# minColors
  1758.     newNumberPicker(34, 12, maxColors, 1, 8, "maxColors", false, pickerChanged); --# maxColors
  1759.   },
  1760.  
  1761.   logButtons = {
  1762.     newButton(17, termY, 2, 1, "<<", colors.lightGray, colors.gray, function() pageNum = 1 logScreen() end, "home", 1);
  1763.     newButton(20, termY, 1, 1, "<", colors.lightGray, colors.gray, function() pageNum = math.max(1, pageNum - 1) logScreen() end, "pageMinus", 1);
  1764.     newButton(33, termY, 1, 1, ">", colors.lightGray, colors.gray, function() pageNum = math.min(numPages, pageNum + 1) logScreen() end, "pagePlus", 1);
  1765.     newButton(35, termY, 2, 1, ">>", colors.lightGray, colors.gray, function() pageNum = numPages logScreen() end, "end", 1);
  1766.   },
  1767. }
  1768.  
  1769. if runMode == "standard" then
  1770.   mainScreen()
  1771. elseif runMode == "debug" then
  1772.   clearScreen()
  1773.   term.setCursorPos(1, 1)
  1774. end
  1775.  
  1776. scKernel() --# start the program
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement