428 lines
14 KiB
Lua
428 lines
14 KiB
Lua
--[[
|
|
Stargate Control System - Main Entry Point
|
|
For Stargate Journey Mod - AllTheMods 9
|
|
|
|
Comprehensive control program featuring:
|
|
- Full dialing system with touch screen interface
|
|
- Automatic iris control with security features
|
|
- Incoming/outgoing wormhole management
|
|
- Address book with categories
|
|
- Entity tracking and logging
|
|
- Whitelist/blacklist security
|
|
]]
|
|
|
|
---------------------------------------------
|
|
-- LOAD MODULES
|
|
---------------------------------------------
|
|
|
|
local config = require("config")
|
|
local addresses = require("addresses")
|
|
local utils = require("utils")
|
|
local display = require("display")
|
|
|
|
---------------------------------------------
|
|
-- INITIALIZATION
|
|
---------------------------------------------
|
|
|
|
-- Find and initialize peripherals
|
|
local mon = peripheral.find("monitor")
|
|
local gate = peripheral.find("advanced_crystal_interface")
|
|
or peripheral.find("crystal_interface")
|
|
or peripheral.find("basic_interface")
|
|
|
|
if gate == nil then
|
|
error("Stargate interface not found! Please connect an interface.")
|
|
end
|
|
|
|
if mon == nil then
|
|
error("Monitor not found! Please connect a monitor.")
|
|
end
|
|
|
|
-- Check iris availability
|
|
if gate.getIris() == nil then
|
|
print("Config has Iris enabled, but there is no iris! Disabling Iris")
|
|
config.irisEnabled = false
|
|
end
|
|
|
|
-- Initialize modules
|
|
utils.init(config, gate)
|
|
display.init(mon, config, addresses, utils)
|
|
|
|
-- Ensure gate starts disconnected
|
|
gate.disconnectStargate()
|
|
|
|
-- Ensure iris starts open
|
|
if config.irisEnabled then
|
|
gate.openIris()
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- GLOBAL STATE
|
|
---------------------------------------------
|
|
|
|
local dialing = false
|
|
local totalstate = nil
|
|
local disconnect = false
|
|
local incomingAddress = {}
|
|
local incomingEntityType = ""
|
|
local incomingEntityName = ""
|
|
local destAddress = {}
|
|
local destAddressname = ""
|
|
local selx, sely = 0, 0
|
|
local y = 0
|
|
|
|
---------------------------------------------
|
|
-- EVENT HANDLERS
|
|
---------------------------------------------
|
|
|
|
local function GetClick()
|
|
mon.setTextScale(1)
|
|
local event, _, xPos, yPos = os.pullEvent("monitor_touch")
|
|
return xPos, yPos
|
|
end
|
|
|
|
local function GetActivation()
|
|
_, _, incomingAddress = os.pullEvent("stargate_incoming_wormhole")
|
|
utils.log("Incoming wormhole: " .. gate.addressToString(incomingAddress))
|
|
return 1
|
|
end
|
|
|
|
local function ParaDisconnect()
|
|
local dx, dy = 0, 0
|
|
_, _, dx, dy = os.pullEvent("monitor_touch")
|
|
|
|
if (dx ~= 0) and (dy ~= 0) then
|
|
gate.disconnectStargate()
|
|
redstone.setOutput("top", false)
|
|
utils.log("Manual disconnect triggered")
|
|
end
|
|
return 1
|
|
end
|
|
|
|
local function EntityRead()
|
|
sleep(0.1)
|
|
_, _, incomingEntityType, incomingEntityName, _ = os.pullEvent("stargate_reconstructing_entity")
|
|
utils.log("Entity reconstructed: " .. incomingEntityName .. " (" .. incomingEntityType .. ")")
|
|
return 1
|
|
end
|
|
|
|
local function DisconnectCheck()
|
|
local _, _, disCode = os.pullEvent("stargate_disconnected")
|
|
redstone.setOutput("top", false)
|
|
utils.log("Stargate disconnected (code: " .. tostring(disCode) .. ")")
|
|
|
|
if config.autoOpenIrisAfterDisconnect then
|
|
utils.openIris()
|
|
end
|
|
|
|
return 2
|
|
end
|
|
|
|
local function Paratimeout()
|
|
sleep(300)
|
|
return 2
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- INCOMING WORMHOLE HANDLER
|
|
---------------------------------------------
|
|
|
|
local function handleIncomingWormhole()
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.red)
|
|
mon.setTextScale(1)
|
|
mon.setCursorPos(9, 4)
|
|
mon.write("INCOMING")
|
|
|
|
incomingAddress = utils.addressToTable(incomingAddress)
|
|
|
|
-- Check security
|
|
local allowed, reason = utils.isAddressAllowed(incomingAddress)
|
|
local addressString = gate.addressToString(incomingAddress) or "Unknown"
|
|
|
|
utils.log("Incoming wormhole from: " .. addressString .. " " .. reason)
|
|
|
|
-- Show incoming connection status
|
|
display.showIncoming(addressString, allowed, reason)
|
|
|
|
-- Handle iris
|
|
if config.autoCloseIrisOnIncoming then
|
|
sleep(config.irisCloseDelay)
|
|
if allowed then
|
|
utils.openIris()
|
|
else
|
|
utils.closeIris()
|
|
end
|
|
end
|
|
|
|
-- Monitor for entities
|
|
disconnect = false
|
|
while (disconnect == false) do
|
|
-- parallel.waitForAny runs multiple functions at the same time and returns which one finished first
|
|
-- Here we're watching for 3 things simultaneously:
|
|
-- 1 = EntityRead (someone/something came through the gate)
|
|
-- 2 = DisconnectCheck (gate disconnected on its own)
|
|
-- ParaDisconnect (user clicked screen to manually disconnect)
|
|
-- Whichever happens first, that function returns and we handle it
|
|
local incomingcheck = parallel.waitForAny(EntityRead, DisconnectCheck, ParaDisconnect)
|
|
if (incomingcheck == 1) then
|
|
display.showEntity(incomingEntityType, incomingEntityName, allowed)
|
|
incomingEntityType = ""
|
|
incomingEntityName = ""
|
|
else
|
|
disconnect = true
|
|
end
|
|
end
|
|
disconnect = false
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- DIALING FUNCTIONS
|
|
---------------------------------------------
|
|
|
|
local function dialGate(address)
|
|
utils.log("Dialing: " .. gate.addressToString(address))
|
|
|
|
local gateType = gate.getStargateType()
|
|
|
|
-- Manual Milky Way dialing with ring rotation
|
|
if gateType == "sgjourney:milky_way_stargate" and config.manualDial == true then
|
|
local addressLength = #address
|
|
|
|
if addressLength == 8 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 6, 7, 8, 5 })
|
|
elseif addressLength == 9 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 5, 6, 7, 8 })
|
|
end
|
|
|
|
local start = gate.getChevronsEngaged() + 1
|
|
|
|
for chevron = start, addressLength, 1 do
|
|
local symbol = address[chevron]
|
|
|
|
if chevron % 2 == 0 then
|
|
gate.rotateClockwise(symbol)
|
|
else
|
|
gate.rotateAntiClockwise(symbol)
|
|
end
|
|
|
|
while (not gate.isCurrentSymbol(symbol)) do
|
|
sleep(0)
|
|
end
|
|
|
|
gate.openChevron()
|
|
sleep(1)
|
|
gate.closeChevron()
|
|
sleep(0.5)
|
|
|
|
display.showDialing(chevron, symbol, gateType)
|
|
end
|
|
else
|
|
-- Automatic dialing for other gate types
|
|
if gateType ~= "sgjourney:universe_stargate" then
|
|
local addressLength = #address
|
|
if addressLength == 8 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 6, 7, 8, 5 })
|
|
elseif addressLength == 9 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 5, 6, 7, 8 })
|
|
end
|
|
end
|
|
|
|
local start = gate.getChevronsEngaged() + 1
|
|
|
|
for chevron = start, #address, 1 do
|
|
local symbol = address[chevron]
|
|
|
|
gate.engageSymbol(symbol)
|
|
sleep(config.gatespeed)
|
|
|
|
display.showDialing(chevron, symbol, gateType)
|
|
|
|
if (symbol) ~= 0 then
|
|
if (gateType == "sgjourney:universe_stargate") or (gateType == "sgjourney:pegasus_stargate") then
|
|
os.pullEvent("stargate_chevron_engaged")
|
|
end
|
|
else
|
|
if gateType == "sgjourney:universe_stargate" then
|
|
os.pullEvent("stargate_chevron_engaged")
|
|
redstone.setOutput("top", true)
|
|
elseif (gateType == "sgjourney:pegasus_stargate") then
|
|
os.pullEvent("stargate_chevron_engaged")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function selectGateFromList()
|
|
local selecting = true
|
|
while dialing == false and selecting == true do
|
|
selx, sely = GetClick()
|
|
local buttonXY, computerAddresses, computerNames = display.getButtonData()
|
|
|
|
for i = 1, #buttonXY do
|
|
if (sely == buttonXY[i][3]) and ((selx >= buttonXY[i][1]) and (selx <= buttonXY[i][2])) then
|
|
dialGate(computerAddresses[i])
|
|
destAddressname = computerNames[i]
|
|
destAddress = computerAddresses[i]
|
|
dialing = true
|
|
sely = 0
|
|
selx = 0
|
|
elseif sely >= 17 and selx >= 23 then
|
|
selecting = false
|
|
sely = 0
|
|
selx = 0
|
|
end
|
|
end
|
|
end
|
|
|
|
display.clearButtonData()
|
|
return dialing
|
|
end
|
|
|
|
local function selectCategory()
|
|
local state = true
|
|
|
|
while state == true do
|
|
display.selectionTabs()
|
|
|
|
local tabx, taby = GetClick()
|
|
y = 2
|
|
local count = 0
|
|
|
|
if (taby >= 2) and (taby <= 6) and ((tabx >= 2) and (tabx <= 13)) then
|
|
if #addresses.MainGates ~= 0 then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.purple)
|
|
count, y = display.screenWrite(addresses.MainGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
|
|
elseif (taby >= 2) and (taby <= 6) and ((tabx >= 16) and (tabx <= 27)) then
|
|
if #addresses.playerGates ~= 0 then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.green)
|
|
count, y = display.screenWrite(addresses.playerGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
|
|
elseif (((taby >= 8) and (taby <= 12)) and ((tabx >= 2) and (tabx <= 13))) and (config.canAccessHazardGates == true) then
|
|
if (#addresses.hazardGates ~= 0) and (config.canAccessHazardGates == true) then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.red)
|
|
count, y = display.screenWrite(addresses.hazardGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
|
|
elseif (taby >= 17) and (tabx >= 23) then
|
|
state = false
|
|
totalstate = false
|
|
end
|
|
end
|
|
return 1
|
|
end
|
|
|
|
local function handleOutgoingDial()
|
|
totalstate = true
|
|
local PDO = 0
|
|
|
|
-- parallel.waitForAny runs functions simultaneously and returns which finished first
|
|
-- Here we're waiting for either:
|
|
-- 1 = selectCategory (user selected a gate to dial)
|
|
-- 2 = Paratimeout (5 minutes passed with no selection)
|
|
-- This prevents the gate from staying on the selection screen forever
|
|
PDO = parallel.waitForAny(selectCategory, Paratimeout)
|
|
|
|
if (PDO == 1) and totalstate == true then
|
|
sleep(1)
|
|
|
|
os.pullEvent("stargate_outgoing_wormhole")
|
|
|
|
display.showConnected(destAddressname, destAddress)
|
|
|
|
if (gate.isStargateConnected() == true) then
|
|
-- parallel.waitForAny runs functions at the same time, returns which finished first
|
|
-- While the wormhole is open, we wait for:
|
|
-- DisconnectCheck = gate disconnects naturally (timeout or remote disconnect)
|
|
-- ParaDisconnect = user manually clicks screen to disconnect
|
|
-- Paratimeout = safety timeout (5 minutes)
|
|
-- Whichever happens first ends the connection
|
|
PDO = parallel.waitForAny(DisconnectCheck, ParaDisconnect, Paratimeout)
|
|
dialing = false
|
|
end
|
|
end
|
|
|
|
display.clearButtonData()
|
|
destAddress = {}
|
|
destAddressname = ""
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- MAIN MENU
|
|
---------------------------------------------
|
|
|
|
local function mainMenu()
|
|
while true do
|
|
display.showMainMenu()
|
|
|
|
-- parallel.waitForAny runs multiple functions simultaneously, returns which completes first
|
|
-- The main menu waits for any of these events:
|
|
-- 1 = GetClick (user touched the monitor anywhere - assumes they want to dial out)
|
|
-- 2 = GetActivation (incoming wormhole detected with address)
|
|
-- Note: We don't wait for CheveronActivation here because it fires at the same time
|
|
-- as the incoming wormhole event, causing a race condition where we might miss the address
|
|
local answer = parallel.waitForAny(GetClick, GetActivation)
|
|
|
|
if (answer == 1) then
|
|
handleOutgoingDial()
|
|
else
|
|
handleIncomingWormhole()
|
|
end
|
|
end
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- STARTUP
|
|
---------------------------------------------
|
|
|
|
utils.log("=== Stargate Control System Starting ===")
|
|
utils.log("Gate Type: " .. gate.getStargateType())
|
|
utils.log("Iris Available: " .. tostring(config.irisEnabled))
|
|
|
|
-- Start the main menu
|
|
mainMenu()
|