2016-02-18

Ticketer - Railcraft Tickets automatisch per ComputerCraft erstellen (Quellcode)

In der heutigen Episode habe ich versprochen, den Quelltext meines Fahrkartensystems hier zu veröffentlichen. Das System verwendet Railcraft, ComputerCraft und OpenPeripherals. Wie es genau zusammengebaut wird, zeige ich über mehrere Episoden verteilt in meiner Direwolf20-Minecraft-Serie.

-- ticketer - a program to manage train destinations and print tickets
-- You can see it in action on youtube.com/foreander
--
-- Usage: Attach a train dispenser and a ticket machine to a computer,
-- set the destinations and config variables below and run it.
-- Enjoy your automatic train system.
--
-- Version 4
--
-- Config variables below

-- Label prefix for computer/ticket machine
-- fahrtziele index of location will be added
computer_label = "Fahrkartenautomat"

-- numeric index for the "secret" maintenance menu
maint_menu_nr = 99

-- table of destinations and other main menu entries
fahrtziele = {
[1] = "Spawn";
[2] = "1-Chunk-Basis";
[3] = "Trant0rs_Basis";
[4] = "Dunkler_Turm";
[5] = "Carthago";
[6] = "Baumhaus-Ruine";
[7] = "End-Portal";
[8] = "Keller_Spawn";
[9] = "Bergdorf";
[maint_menu_nr] = "Systemwartungsmenue"
} -- table fahrtziele
-- Note: Railcaft doesn't work with spaces in Destinations

-- destination number of this ticketer's location
num_here = 1

-- position of redstone connection
redstone_cable = "top"

-- time in seconds to display success messages
success_timer = 5

-- default pattern for trains is one steam locomotive and
-- one passenger cart, table keys use ["name"] field of
-- inventory slots. set this to the same as the pattern  
-- in the train dispenser
needed_carts = {
["cart.loco.steam.solid"] = 1; -- steam locomotive
["cart.loco.electric"] = 0;  -- electric locomotive
["minecart"] = 1; -- (passenger) minecart
["chest_minecart"] = 0; -- chest minecart
["cart.tank"] = 0; -- tank cart
["cart.work"] = 0; -- work cart
} -- table needed_carts

-- display names for those needed carts
-- I couldn't find a way to dynamically get them in game
-- so hard coding them was the only way to get a nice display
cart_names = {
  ["cart.loco.steam.solid"] = "Steam Locomotive";
  ["cart.loco.electric"] = "Electrical Locomotive";
  ["minecart"] = "Minecart";
  ["chest_minecart"] = "Chest Minecart";
  ["cart.tank"] = "Tank Cart";
  ["cart.work"] = "Work Cart"
} -- table cart_names

-- End of config variables

-- keeping track of which menu to display
menu_mode = "main"

-- automatically find and attach ticket printer and train dispenser
ticket_printer = peripheral.find("openperipheral_ticketmachine")
train_dispenser = peripheral.find("train_dispenser")

-- set the computer's label
os.setComputerLabel(computer_label.." ("..tostring(num_here)..")")

-- displays and handles the main menu
function main_menu()
  term.clear() 
  print(" ***************")
  print(" FeluccaRail(tm)")
  print(" ***************")
  print("")
  -- check if enough carts and engines available
  if not check_train_dispenser_inventory() then
    print("")
    print("Fahrkartenausgabe eingestellt.")
 print("")
  end
   -- check if enough ink and paper in ticket_printer
  if not check_ticket_machine_inventory() then
    print("")
    print("Fahrkartenausgabe eingestellt.")
 print("")
  end
  
  print("Bitte Fahrtziel auswaehlen:")
  print("")
  for k,v in pairs(fahrtziele) do
    -- exclude the ticketer's location and the maintenance menu
    if k ~= num_here and k ~= maint_menu_nr then 
      print(k .. " - " .. v)
 end
  end
  print("")
  io.write("Ziel: ")
  local ziel = tonumber(io.read())
  print (" * Bearbeite Anfrage...")
  if ziel == maint_menu_nr then
    maintenance_menu()
 return -- this breaks out of main menu function early
  end
  if check_destination_validity(ziel) and 
    check_train_dispenser_inventory(true) and 
    check_ticket_machine_inventory(true) then
    if print_ticket(ziel) then
   start_train()
   success_screen("Zug nach " .. fahrtziele[ziel].. " faehrt in Kuerze ab.")
 end
  else 
    error_screen("Ungueltiges Fahrtziel")
  end 
end -- function main_menu()


-- displays and handles the maintenance menu
function maintenance_menu()
  -- stay in maintenance menu until return to main is chosen
  menu_mode = "maint" 
  term.clear() 
  print(" *************")
  print(" Systemwartung")
  print(" *************")
  print("")
  print("Bitte Funktion auswaehlen:")
  print("")
  print("1 - Angeschlossene Peripherals")
  print("2 - Einstellungen anzeigen")
  if (ticket_printer) then
    print("3 - Inventar Ticket Printer")
  end
  if (train_dispenser) then
    print("4 - Inventar Train Dispenser (Display Names)")
 print("5 - Inventar Train Dispenser (Names)")
  end
  print("99 - Zurueck ins Hauptmenue")
  print("")
  io.write("Auswahl: ")
  local choice = tonumber(io.read())
  if choice == 99 then
    menu_mode = "main" -- set menu back to main menu
    return
  elseif choice == 1 then
    list_peripherals()
  elseif choice == 2 then
    list_settings()
  elseif choice == 3 and (ticket_printer) then
    show_ticket_printer_inventory(true)
  elseif choice == 4 and (train_dispenser) then
    show_train_dispenser_inventory(true)
  elseif choice == 5 and (train_dispenser) then
    show_train_dispenser_inventory(false)
  end
end -- function maintenance_menu

-- list all connected peripherals
function list_peripherals()
  term.clear() 
  print("Angeschlossene Peripherals")
  print("")
  if ticket_printer then
    print("Aktiver Ticket Printer: "..ticket_printer.getInventoryName())
  end
  if train_dispenser then
    print("Aktiver Train Dispenser: "..train_dispenser.getInventoryName())
  end
  print("")
  local peris = peripheral.getNames()
  if not peris then
    print("Keine Peripherals vorhanden.")
  else
    print("Alle verfuegbaren Peripherals")
    print("Anschluss -> Geraet")
    for i = 1, #peris do
   print(peris[i].." -> "..peripheral.getType(peris[i]))
 end
  end
  wait_for_key()
end -- function list_peripherals

-- list all configurable settings
function list_settings()
  term.clear()
  print("Einstellungen:")
  print("Standort: ("..num_here..") "..fahrtziele[num_here])
  -- print("Anschluss Ticket Printer: "..ticket_printer_pos)
  print("Anschluss Redstone: "..redstone_cable)
  print("Anzeigedauer Erfolgsmeldung (s): "..tostring(success_timer))
  wait_for_key()
end -- function list_settings


-- shows the ticket printers inventory
-- use_display_names: boolean, if true show display names 
--  instead of internal names
function show_ticket_printer_inventory(use_display_names)
  term.clear()
  print("Ticket Printer Inventar:")
  print("")
  print_inventory(ticket_printer.getAllStacks(), use_display_names)
  wait_for_key()
end -- function show_ticket_printer_inventory


-- shows the train dispensers inventory
-- use_display_names: boolean, if true show display names 
--  instead of internal names
function show_train_dispenser_inventory(use_display_names)
  term.clear()
  print("Train Dispenser Inventar:")
  print("")
  print_inventory(train_dispenser.getAllStacks(),use_display_names)
  wait_for_key()
end -- function show_train_dispenser_inventory


-- helper function to print a list of an inventory
-- stacks: table, as returned from inventory.getAllStacks()
-- use_display_names: boolean, if true show display names 
--  instead of internal names
function print_inventory(stacks, use_display_names)
  for k,v in pairs(stacks) do
    io.write("Slot "..k.." -> ")
 local item = stacks[k].basic()
 if use_display_names then
   print(tostring(item["qty"]).."x "..item["display_name"])
 else 
   print(tostring(item["qty"]).."x "..item["name"])
 end
  end
end -- function print_inventory

function success_screen(msg) -- prints a success message
  term.clear()
  print("")
  print(" ************")
  print(" Erfolgreich:")
  print(" ************")
  print("")
  print(msg)
  print("")
  os.sleep(success_timer)
end -- function success_screen(msg)


function error_screen(msg) -- prints an error message
  term.clear()
  print("")
  print(" *******")
  print(" Fehler:")
  print(" *******")
  print("")
  print(msg)
  wait_for_key()
end -- function error_screen(msg)

-- halts execution until user presses any key
function wait_for_key()
 print("")
 print("Beliebige Taste druecken")
  while true do
 local evt = os.pullEvent("key")
 if evt == "key" then
   break
 end
  end
end -- function wait_for_key


-- checks if a given destination is valid
function check_destination_validity(dest) 
  if fahrtziele[dest] and dest ~= num_here then
    return true;
  else
    return false;
  end
end -- function check_destination_validity(dest)


function print_ticket(dest) -- print a ticket
  -- that's what error handling in lua looks like
  local status,err = pcall(ticket_printer.createTicket, fahrtziele[dest], 1)
  if not status then
    local error_msg = "Fehler beim Erzeugen des Tickets\n\n"
 if type(err) == "string" then
   -- cut out the unneccessary pcal prefix
   err = string.gsub(err,"pcall: ","")
   error_msg = error_msg .. err
 else
          -- api should alwas return string, just in case it doesn't
   error_msg = error_msg .. "Unbekannter Fehler" 
 end
    error_screen(error_msg)
 return false
  else
    return true
  end
end -- function print_ticket(dest)


function start_train() -- put out redstone signal to assemble the train
  redstone.setOutput(redstone_cable,true)
  os.sleep(1)
  redstone.setOutput(redstone_cable,false)
end -- functon start_train() 


-- check if train dispenser has enough carts and locomotives
-- suppress_msg: boolean, if true no message will be printed
function check_train_dispenser_inventory(suppress_msg)
  stacks = train_dispenser.getAllStacks()
  if not stacks then -- empty inventory
    if not suppress_msg then 
     print ("Keine Carts oder Lokomotiven im Train Dispenser")
   end
    return false
  end
  -- get list of all available carts
  -- i.e. iterate over complete inventory and add up
  local available_carts = {}
  for k in pairs(stacks) do
    stack = stacks[k].basic()
 if available_carts[stack["name"]] then
   available_carts[stack["name"]] = available_carts[stack["name"]] + stack["qty"]
 else
   available_carts[stack["name"]] = stack["qty"]
 end
  end
  
  -- check if at least minimum of each needed cart is available
  local enough_carts = true
  for j,w in pairs(needed_carts) do
    if w > 0 and (not available_carts[j] or available_carts[j] < w) then
   enough_carts = false
   if not suppress_msg then 
     print ("Nicht genuegend "..cart_names[j].." im Train Dispenser")
   end
 end
  end
  return enough_carts
end -- function check_train_dispenser_inventory

-- check if ticket machine has enough paper and ink
-- suppress_msg: boolean, if true no message will be printed
function check_ticket_machine_inventory(suppress_msg)
  stacks = ticket_printer.getAllStacks()
  local all_is_well = true
  if not stacks then -- empty inventory
    if not suppress_msg then 
     print ("Weder Tinte noch Papier im Ticket Printer")
   end
    all_is_well = false
  end
  -- ticket printer has exactly two slots, one for paper (slot 1),
  -- one for ink (slot 2)
  if not stacks[1] then -- missing paper
   if not suppress_msg then 
     print ("Kein Papier im Ticket Printer")
   end
    all_is_well = false
  end
  if not stacks[2] then -- missing ink
   if not suppress_msg then 
     print ("Keine Tinte im Ticket Printer")
   end
    all_is_well = false
  end
  return all_is_well
end -- function check_ticket_machine_inventory

-- main loop
while true do
  -- menu_mode determines which screen to display
  if menu_mode == "main" then 
    main_menu()
  elseif menu_mode == "maint" then
    maintenance_menu()
  end
end



Keine Kommentare:

Kommentar veröffentlichen