Posted 20 May 2012 - 05:03 AM
A first program from someone 3 days into lua and computer craft, so go easy. Only first version.
[media]http://www.youtube.com/watch?v=Xwf6MT6pP2Y[/media]
I use the Redworks Addon installed via floppy on my terminals, this was designed in this environment and does depend on the redworks api for 1 function.
I have many future plans for this program, and tried to leave room to add them in later, so there are some unfinished peices lingering, once I've reached a point in my game that I need to impliment these other features I'll add them in.
.7z Download
Alternatively the code follows in the spoilers.
File: /mem/redworks/apis/be
File: /mem/redworks/programs/befactory
If you use any of my code in a seperate project for fork this, credit would be nice. :P/>/>
[media]http://www.youtube.com/watch?v=Xwf6MT6pP2Y[/media]
I use the Redworks Addon installed via floppy on my terminals, this was designed in this environment and does depend on the redworks api for 1 function.
I have many future plans for this program, and tried to leave room to add them in later, so there are some unfinished peices lingering, once I've reached a point in my game that I need to impliment these other features I'll add them in.
.7z Download
Alternatively the code follows in the spoilers.
File: /mem/redworks/apis/be
Spoiler
--[[
@author Bechill
@version 1.0
@since 2012-05-19
]]
--[[name: clear
Simple clear function so no need to rewrite constantly.
@return nil
]]
clear = function()
term.clear()
term.setCursorPos(1,1)
end
home = function ()
term.setCursorPos(1, select(2, term.getCursorPos()))
end
--[[name: fif
Ternary function that can be called recursively with other functions
@param condition condition to test that resolves to a boolean
@param if_true return variable if condition true
@param if_false return variable if condition false
@return mixed
]]
fif = function(condition, if_true, if_false)
if condition then return if_true else return if_false end
end
--[[name: palignText
Only required to provide string s, all other parameters have optional defaults
Use: be.palignText{s="Test Title", a="RIGHT", sw="_/", lm=10, rm=term.getSize()-10}
@param s string required to align
@param a string alignement "LEFT" or "RIGHT" or "CENTER", defaults to "CENTER"
@param lm integer to mark the left margin
@param rm integer to mark the right margin
@param sw string to surround text with
@param sp integer to space lines with, useful for when included in a loop
@param tr bool to set if text is truncated to fit within margins
]]
palignText = function(args)
if type(args.s) ~= "string" then error("No string") end
if args.a then
if ((type(args.a) ~= "string") or not (args.a == "LEFT" or args.a == "RIGHT" or args.a == "CENTER")) then
error("alignment should be "LEFT", "RIGHT", or "CENTER"")
end
end
if args.sw then
if type(args.s) ~= "string" then
error("sw if supplied should be a string")
end
end
_palignText (args.s,
args.a or "CENTER",
args.lm or 1,
args.rm or term.getSize(),
args.sw or nil,
args.sp or 1,
args.tr or false)
end
_palignText = function(str, align, leftMargin, rightMargin, surroundWith, spacing, truncate)
local x, y = term.getCursorPos()
local strlength = string.len(str)
local space = rightMargin-leftMargin+1
if surroundWith then
local swl = string.len(surroundWith)
term.setCursorPos(leftMargin, y)
term.write(string.rep(surroundWith, math.modf(space/swl)))
local remain = math.fmod(space, swl)
if remain then
term.write(string.sub(surroundWith, 1, remain))
end
end
local doalign = {
["LEFT"] = function ()
term.setCursorPos(leftMargin, y)
end,
["RIGHT"] = function ()
term.setCursorPos(rightMargin-strlength+1, y)
end,
["CENTER"] = function ()
term.setCursorPos(leftMargin+space/2-strlength/2, y)
end
}
doalign[align]()
if truncate then
term.write(string.sub(str, 1, space))
else
term.write(str)
end
if (spacing ~= 0) then
term.setCursorPos(x, y+spacing)
end
end
--[[name: drawBox
@param x location for top left corner of box
@param y location for top left corner of box
@param h Height
@param w Width
@param fill Fill with character
@param title Title on top of box
@param titlea Title alignment "LEFT", "RIGHT", or "CENTER"
@return nil
]]--
drawBox = function (x, y, h, w, fill, title, titlea)
term.setCursorPos(x, y)
term.write("+"..string.rep("-", w-2).."+")
if title then
palignText{s=title, a=titlea, lm=x+1, rm=x+w-2, tr=true, sp=0, sw="-"}
end
for yy=y+1, y+h-2 do
term.setCursorPos(x, yy)
if fill then
term.write("|"..string.rep(fill, w-2).."|")
else
term.write("|")
term.setCursorPos(x+w-1, yy)
term.write("|")
end
end
term.setCursorPos(x,y+h-1)
term.write("+"..string.rep("-", w-2).."+")
term.setCursorPos(x,y)
end
menuBox = function (x, y, w, fill, title, titlea, tOptions, selected)
drawBox(x, y, #tOptions+2, w, fill, title, titlea)
if not selected or selected > #tOptions then selected = 1 end
term.setCursorPos(x+1,y+1)
for n=1, #tOptions do
if (string.len(tOptions[n].name) > w-2) then
opt = string.sub(tOptions[n].name, 1, w-2)
else
opt = tOptions[n].name
end
if (n == selected) then
palignText{s=">"..opt.."<", lm=x+1, rm=x+w-1}
else
palignText{s=opt, lm=x+1, rm=x+w-1}
end
end
end
-- Internal persistence library
--[[ Provides ]]
-- persistence.store(path, ...): Stores arbitrary items to the file at the given path
-- persistence.load(path): Loads files that were previously stored with store and returns them
--[[ Limitations ]]
-- Does not export userdata, threads or most function values
-- Function export is not portable
--[[ License: MIT (see bottom) ]]
-- Private methods
local write, writeIndent, writers, refCount;
persistence =
{
store = function (path, ...)
local file, e;
if type(path) == "string" then
-- Path, open a file
file, e = io.open(path, "w");
if not file then
return error(e);
end
else
-- Just treat it as file
file = path;
end
local n = select("#", ...);
-- Count references
local objRefCount = {}; -- Stores reference that will be exported
for i = 1, n do
refCount(objRefCount, (select(i,...)));
end;
-- Export Objects with more than one ref and assign name
-- First, create empty tables for each
local objRefNames = {};
local objRefIdx = 0;
file:write("-- Persistent Datan");
file:write("local multiRefObjects = {n");
for obj, count in pairs(objRefCount) do
if count > 1 then
objRefIdx = objRefIdx + 1;
objRefNames[obj] = objRefIdx;
file:write("{};"); -- table objRefIdx
end;
end;
file:write("n} -- multiRefObjectsn");
-- Then fill them (this requires all empty multiRefObjects to exist)
for obj, idx in pairs(objRefNames) do
for k, v in pairs(obj) do
file:write("multiRefObjects["..idx.."][");
write(file, k, 0, objRefNames);
file:write("] = ");
write(file, v, 0, objRefNames);
file:write(";n");
end;
end;
-- Create the remaining objects
for i = 1, n do
file:write("local ".."obj"..i.." = ");
write(file, (select(i,...)), 0, objRefNames);
file:write("n");
end
-- Return them
if n > 0 then
file:write("return obj1");
for i = 2, n do
file:write(" ,obj"..i);
end;
file:write("n");
else
file:write("returnn");
end;
file:close();
end;
load = function (path)
local f, e = loadfile(path);
if f then
return f();
else
return nil, e;
end;
end;
}
-- Private methods
-- write thing (dispatcher)
write = function (file, item, level, objRefNames)
writers[type(item)](file, item, level, objRefNames);
end;
-- write indent
writeIndent = function (file, level)
for i = 1, level do
file:write("t");
end;
end;
-- recursively count references
refCount = function (objRefCount, item)
-- only count reference types (tables)
if type(item) == "table" then
-- Increase ref count
if objRefCount[item] then
objRefCount[item] = objRefCount[item] + 1;
else
objRefCount[item] = 1;
-- If first encounter, traverse
for k, v in pairs(item) do
refCount(objRefCount, k);
refCount(objRefCount, v);
end;
end;
end;
end;
-- Format items for the purpose of restoring
writers = {
["nil"] = function (file, item)
file:write("nil");
end;
["number"] = function (file, item)
file:write(tostring(item));
end;
["string"] = function (file, item)
file:write(string.format("%q", item));
end;
["boolean"] = function (file, item)
if item then
file:write("true");
else
file:write("false");
end
end;
["table"] = function (file, item, level, objRefNames)
local refIdx = objRefNames[item];
if refIdx then
-- Table with multiple references
file:write("multiRefObjects["..refIdx.."]");
else
-- Single use table
file:write("{n");
for k, v in pairs(item) do
writeIndent(file, level+1);
file:write("[");
write(file, k, level+1, objRefNames);
file:write("] = ");
write(file, v, level+1, objRefNames);
file:write(";n");
end
writeIndent(file, level);
file:write("}");
end;
end;
["function"] = function (file, item)
-- Does only work for "normal" functions, not those
-- with upvalues or c functions
local dInfo = debug.getinfo(item, "uS");
if dInfo.nups > 0 then
file:write("nil --[[functions with upvalue not supported]]");
elseif dInfo.what ~= "Lua" then
file:write("nil --[[non-lua function not supported]]");
else
local r, s = pcall(string.dump,item);
if r then
file:write(string.format("loadstring(%q)", s));
else
file:write("nil --[[function could not be dumped]]");
end
end
end;
["thread"] = function (file, item)
file:write("nil --[[thread]]n");
end;
["userdata"] = function (file, item)
file:write("nil --[[userdata]]n");
end;
}
--[[
Copyright (c) 2010 Gerhard Roethlin
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
]]
File: /mem/redworks/programs/befactory
Spoiler
local revKeys = {["1"] = 1, ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5, ["6"] = 6,
["7"] = 7, ["8"] = 8, ["9"] = 9, ["0"] = 10,["A"] = 11,["B"] = 12,
["C"] = 13,["D"] = 14,["E"] = 15,["F"] = 16,["a"] = 11,["b"] = 12,
["c"] = 13,["d"] = 14,["e"] = 15,["f"] = 16}
local autorunpath = "/mem/autorun/z.factorystart"
local settingspath = "/factory.settings"
local pat=be.palignText
local fif=be.fif
local _menuState, _componentToggle, _updateComponents, _componentToggle
local factory = {}
local _factoryOutSelect = 0
-- Copy redstone.getSides() into local table compatible with be.menuBox()
local sideOptions = {}
for k,v in ipairs(redstone.getSides()) do
table.insert(sideOptions, k, {name = v})
end
_updateComponents = function ()
c = 0
for k in ipairs(factory.components) do
if factory.components[k].monitored and factory.components[k].on then
c = c + factory.components[k].color
end
end
rs.setBundledOutput(factory.outdirection, c)
be.persistence.store(settingspath, factory)
end
_componentSettings = function(c)
be.clear()
pat{s=" "..factory.componentname.." Settings ", a="LEFT", sw="_"}
pat{s="----"..factory.components[c].name.." Settings ", a="LEFT", sw="-"}
write("1) Name: "..factory.components[c].name.."n")
write("2) Controlled: "..fif(factory.components[c].monitored, "Truen", "Falsen"))
write("3) State: "..fif(factory.components[c].on, "Onn", "Offn"))
write("4) Status: [Inactive in this version]n")
term.setCursorPos(1, select(2, term.getSize())-1)
pat{s="", sw="-"}
term.write("[#] to change option, [D]one > ")
_menuState = "COMPONENTMENU"
end
updateFactory = function()
if factory.autostart and not fs.exists(autorunpath) then
_writeAutostart()
elseif not factory.autostart and fs.exists(autorunpath) then
fs.delete(autorunpath)
end
_updateComponents()
end
_writeAutostart = function()
file, e = io.open(autorunpath, "w");
if not file then error(e) end
file:write("shell.run("/mem/redworks/programs/befactory")")
file:close();
end
_factoryOutChange = function()
if _factoryOutSelect == 0 then
for k,v in ipairs(sideOptions) do
if factory.outdirection == v.name then
_factoryOutSelect = k
break
end
end
end
be.menuBox(term.getSize()/2-6, 5, 12, " ", "Output", nil, sideOptions, _factoryOutSelect)
end
--[[_factoryInChange = function()
end]]
displaymenuFactory = function()
be.clear()
pat{s=" "..factory.name.." Settings ", a="LEFT", sw="_"}
pat{s="", sw="-"}
write("1) Name: "..factory.name.."n")
write("2) Component Name: "..factory.componentname.."n")
write("3) Output Side: "..factory.outdirection.."n")
write("4) Input Side: "..factory.indirection.." [Inactive]n")
write("5) Autostart: "..fif(factory.autostart, "True", "False"))
term.setCursorPos(1, select(2, term.getSize())-1)
pat{s="", sw="-"}
term.write("[#] to change option, [M]enu > ")
_menuState = "FACTORYMENU"
end
displaymenuToggle = function()
be.clear()
pat{s=" "..factory.componentname.." Settings ", a="LEFT", sw="_"}
pat{s="", sw="-"}
for k in ipairs(factory.components) do
if (k < 9) then _lm=0 else _lm=math.modf(term.getSize()/2) end
pat{s=factory.components[k].key..") "..factory.components[k].name, a="LEFT", lm=_lm+1, rm=_lm+20, sp=1}
if k == 8 then term.setCursorPos(term.getSize()/2, 3) end
end
term.setCursorPos(1, select(2, term.getSize())-1)
pat{s="", sw="-"}
term.write("[#] to configure component, [M]enu > ")
_menuState = "TOGGLEMENU"
end
displaymenuStatus = function ()
be.clear()
pat{s=" "..factory.name.." ", a="CENTER", sw="_"}
pat{s=factory.componentname.." ", a="LEFT", sw="-", sp=1}
local c=0
for k in ipairs(factory.components) do
if (factory.components[k].monitored) then
c=c+1
if (c < (select(2, term.getSize())-3)) then _lm=0 else _lm=math.modf(term.getSize()/2) end
pat{s=factory.components[k].key..") ", a="LEFT", lm=_lm+1, rm=_lm+3, sp=0}
pat{s=be.fif(factory.components[k].on,"ON","OFF"), a="RIGHT", lm=_lm+5, rm=_lm+6, sp=0}
pat{s=factory.components[k].name, a="LEFT", lm=_lm+9, rm=_lm+24, sp=1, tr=true}
if c == (select(2, term.getSize())-4) then term.setCursorPos(term.getSize()/2, 3) end
end
end
term.setCursorPos(1, select(2, term.getSize())-1)
pat{s="", sw="-"}
term.write("[#] to toggle state, [M]enu > ")
_menuState = "DISPLAYSTATUS"
end
mainmenuOptions = {
[1] = { name = "Factory Settings", ret = function() displaymenuFactory() end },
[2] = { name = "Toggle Components", ret = function() displaymenuToggle() end },
[3] = { name = "Factory Status", ret = function() displaymenuStatus() end },
[4] = { name = "Exit", ret = function() _menuState = "EXIT" end },
["selected"] = 3
}
displaymenuMain = function ()
be.menuBox(10, 5, 25, " ", "Menu", "CENTER", mainmenuOptions, mainmenuOptions["selected"])
_menuState = "MAINMENU"
end
main = function()
if fs.exists(settingspath) then
factory = be.persistence.load(settingspath)
_updateComponents()
else
factory.name = "Powerplant"
factory.componentname = "Generator"
factory.outdirection = "back"
factory.indirection = "back"
factory.autostart = false
factory.components = {
[1] = {key = "1", color = colors.white, name = "Engine 1", monitored = true, on = false, status=nil},
[2] = {key = "2", color = colors.orange, name = "Engine 2", monitored = true, on = false, status=nil},
[3] = {key = "3", color = colors.magenta, name = "Engine 3", monitored = true, on = false, status=nil},
[4] = {key = "4", color = colors.lightBlue, name = "Engine 4", monitored = true, on = false, status=nil},
[5] = {key = "5", color = colors.yellow, name = "Engine 5", monitored = true, on = false, status=nil},
[6] = {key = "6", color = colors.lime, name = "Engine 6", monitored = true, on = false, status=nil},
[7] = {key = "7", color = colors.pink, name = "Engine 7", monitored = true, on = false, status=nil},
[8] = {key = "8", color = colors.gray, name = "Engine 8", monitored = true, on = false, status=nil},
[9] = {key = "9", color = colors.lightGray, name = "Engine 9", monitored = true, on = false, status=nil},
[10] = {key = "0", color = colors.cyan, name = "Engine 10", monitored = true, on = false, status=nil},
[11] = {key = "A", color = colors.purple, name = "Engine 11", monitored = true, on = false, status=nil},
[12] = {key = "B", color = colors.blue, name = "Engine 12", monitored = true, on = false, status=nil},
[13] = {key = "C", color = colors.brown, name = "Engine 13", monitored = true, on = false, status=nil},
[14] = {key = "D", color = colors.green, name = "Engine 14", monitored = true, on = false, status=nil},
[15] = {key = "E", color = colors.red, name = "Engine 15", monitored = true, on = false, status=nil},
[16] = {key = "F", color = colors.black, name = "Engine 16", monitored = true, on = false, status=nil}
}
_updateComponents()
end
displaymenuStatus()
local menuState = {
["DISPLAYSTATUS"] = function ()
if event == "char" then
if revKeys[p1] and factory.components[revKeys[p1]].monitored then
factory.components[revKeys[p1]].on = fif(factory.components[revKeys[p1]].on, false, true)
_updateComponents()
displaymenuStatus()
elseif (p1 == "m" or p1 == "M") then
displaymenuMain()
end
end
end,
["MAINMENU"] = function ()
if event == "key" then
if (p1 == 200) and (mainmenuOptions["selected"] > 1) then
mainmenuOptions["selected"] = mainmenuOptions["selected"] - 1
displaymenuMain()
elseif (p1 == 208) and (mainmenuOptions["selected"] < #mainmenuOptions) then
mainmenuOptions["selected"] = mainmenuOptions["selected"] + 1
displaymenuMain()
elseif (p1 == 28) or (p1 == 156)
then mainmenuOptions[mainmenuOptions["selected"]].ret()
end
end
end,
["FACTORYMENU"] = function ()
if event == "key" then
if _factoryOutSelect ~= 0 then
if (p1 == 200) and (_factoryOutSelect > 1) then
_factoryOutSelect = _factoryOutSelect - 1
_factoryOutChange()
elseif (p1 == 208) and _factoryOutSelect < #sideOptions then
_factoryOutSelect = _factoryOutSelect + 1
_factoryOutChange()
elseif ((p1 == 28) or (p1 == 156)) and _factoryOutSelect then
factory.outdirection = sideOptions[_factoryOutSelect].name
_factoryOutSelect = 0
updateFactory()
displaymenuFactory()
end
else
if (p1 == 2) then -- "1"
os.pullEvent("char") -- Discard char event
factory.name = redworks.textBox(19,2,20,3,true,false,factory.name, false)
updateFactory()
displaymenuFactory()
elseif (p1 == 3) then -- "2"
os.pullEvent("char") -- Discard char event
factory.componentname = redworks.textBox(19,3,20,3,true,false,factory.componentname, false)
updateFactory()
displaymenuFactory()
elseif (p1 == 4) then -- "3"
_factoryOutChange()
updateFactory()
-- elseif (p1 == 5) then -- "4"
--_factoryInChange()
--updateFactory()
elseif (p1 == 6) then -- "5"
factory.autostart = fif(factory.autostart, false, true)
updateFactory()
displaymenuFactory()
elseif (p1 == 50) then -- "m"
displaymenuMain()
end
end
end
end,
["TOGGLEMENU"] = function ()
if event == "char" then
if revKeys[p1] then
_currentComponent = revKeys[p1]
_componentSettings(_currentComponent)
elseif (p1 == "m" or p1 == "M") then
displaymenuMain()
end
end
end,
["COMPONENTMENU"] = function ()
if event == "char" then
if (p1 == "1") then
factory.components[_currentComponent].name =
redworks.textBox(15,2,20,3,true,false,factory.components[_currentComponent].name, false)
_updateComponents()
_componentSettings(_currentComponent)
elseif (p1 == "2") then
factory.components[_currentComponent].monitored =
fif(factory.components[_currentComponent].monitored, false, true)
_updateComponents()
_componentSettings(_currentComponent)
elseif (p1 == "3") then
factory.components[_currentComponent].on = fif(factory.components[_currentComponent].on, false, true)
_updateComponents()
_componentSettings(_currentComponent)
elseif (p1 == "d" or p1 == "D") then
displaymenuToggle()
end
end
end
}
while _menuState ~= "EXIT" do
event, p1 = os.pullEvent()
menuState[_menuState]()
end
term.setCursorPos(1, select(2, term.getSize()))
pat{s="Type "befactory" to restart. Goodbye!", a="LEFT", sw=" "}
print()
end
main()
If you use any of my code in a seperate project for fork this, credit would be nice. :P/>/>