Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Import necessary libraries
- print("Create Mod Big Cannons Ballistics Calculator Ultimate! Booting")
- math = require("math") -- DTB
- -- Define the side of the computer where the bundled cable is connected
- local bundledSide = "bottom" -- Adjust this as needed
- -- Define color variables in a table for dynamic access
- local chargeColors = {
- Charge1 = colors.lightBlue,
- Charge2 = colors.magenta,
- Charge3 = colors.orange,
- Charge4 = colors.white,
- Shell = colors.yellow,
- }
- local componentColors = {
- GunBreachGearShift = colors.lime,
- GunLoadingPole = colors.brown,
- GunPitchClutch = colors.cyan,
- GunYawClutch = colors.purple,
- GunYawGearShift = colors.blue,
- GunBuild = colors.gray,
- GunAlarm = colors.black,
- GunFire = colors.green,
- }
- -- Initialize variables for the chat system and the UUIDs
- local chatBox = peripheral.find("chatBox")
- if not chatBox then
- error("ChatBox peripheral not found.")
- end
- local authorizedUUIDs = {
- "uuid1",
- "76364a85-1e9c-48df-975c-11eb045d5a51",
- -- Add more authorized UUIDs here
- }
- -- Function to check if a UUID is authorized
- local function isAuthorized(uuid)
- for _, authorizedUUID in ipairs(authorizedUUIDs) do
- if uuid == authorizedUUID then
- return true
- end
- end
- return false
- end
- -- Function to power on a specific color (activate a component)
- local function PowerOn(componentColor)
- if not componentColor then
- print("Error: Invalid color provided to PowerOn.")
- return
- end
- local currentOutput = redstone.getBundledOutput(bundledSide) or 0
- redstone.setBundledOutput(bundledSide, bit.bor(currentOutput, componentColor))
- print("Powered on color: " .. tostring(componentColor))
- end
- -- Function to power off a specific color (deactivate a component)
- local function PowerOff(componentColor)
- if not componentColor then
- print("Error: Invalid color provided to PowerOff.")
- return
- end
- local currentOutput = redstone.getBundledOutput(bundledSide) or 0
- redstone.setBundledOutput(bundledSide, bit.band(currentOutput, bit.bnot(componentColor)))
- print("Powered off color: " .. tostring(componentColor))
- end
- -- Function to reset all outputs
- local function ResetOutputs()
- redstone.setBundledOutput(bundledSide, 0)
- print("All outputs reset.")
- end
- -- Variables to store cannon data and pending fire mission
- local Cannon = {}
- local pendingFireSolution = nil
- local RotationConstant = 2.6695 / 10
- -- Function to send a denial message with red brackets
- local function sendDeniedMessage(username)
- chatBox.sendMessageToPlayer("You are not authorized to perform this action.", username, "Denied", "[]", "&c")
- end
- -- Function to send success message with green brackets
- local function sendAcceptedMessage(username, message)
- chatBox.sendMessageToPlayer(message, username, "Accepted", "[]", "&a")
- end
- -- Function to send informational message with blue brackets
- local function sendInfoMessage(username, message)
- chatBox.sendMessageToPlayer(message, username, "Info", "[]", "&b")
- end
- -- Function to save cannon data to a file
- local function SaveCannonData()
- local file = fs.open("cannon_data.txt", "w")
- if file == nil then
- print("Error opening file for writing.")
- return
- end
- file.writeLine(Cannon.x)
- file.writeLine(Cannon.y)
- file.writeLine(Cannon.z)
- file.writeLine(Cannon.yaw)
- file.writeLine(Cannon.maxCharges)
- file.close()
- print("Cannon data saved.")
- end
- -- Function to load cannon data from a file
- local function LoadCannonData()
- local file = fs.open("cannon_data.txt", "r")
- if file == nil then
- print("No cannon data found.")
- return false -- Indicate that data was not loaded
- end
- Cannon.x = tonumber(file.readLine())
- Cannon.y = tonumber(file.readLine())
- Cannon.z = tonumber(file.readLine())
- Cannon.yaw = tonumber(file.readLine())
- Cannon.maxCharges = tonumber(file.readLine())
- file.close()
- print("Cannon data loaded.")
- return true -- Indicate that data was loaded successfully
- end
- -- Function to prompt the user for cannon info via console
- local function PromptForCannonInfo()
- print("No cannon data found. Please enter cannon information.")
- print("Input Barrel X: ")
- Cannon.x = tonumber(io.read())
- print("Input Barrel Y: ")
- Cannon.y = tonumber(io.read())
- print("Input Barrel Z: ")
- Cannon.z = tonumber(io.read())
- print("Facing Yaw? (-180 to 180): ")
- Cannon.yaw = tonumber(io.read())
- print("Max Charges?: ")
- Cannon.maxCharges = tonumber(io.read())
- SaveCannonData()
- print("Cannon information saved.")
- end
- -- Function to set cannon information via chat command
- local function SetCannonInfo(username, params)
- local x, y, z, yaw, maxCharges = params:match("^(%-?%d+) (%-?%d+) (%-?%d+) (%-?%d+) (%d+)$")
- if x and y and z and yaw and maxCharges then
- Cannon.x = tonumber(x)
- Cannon.y = tonumber(y)
- Cannon.z = tonumber(z)
- Cannon.yaw = tonumber(yaw)
- Cannon.maxCharges = tonumber(maxCharges)
- SaveCannonData()
- sendAcceptedMessage(username, "Cannon information updated.")
- else
- sendInfoMessage(username, "Invalid format. Use: <SetCannon> x y z yaw maxCharges")
- end
- end
- -- Define the maximum acceptable distance error
- local MaxDistanceError = 70 -- Adjust this value as needed
- -- Function to calculate the pitch angle and charge count
- local function CalculatePitch(maxCharges, Distance, TargetY)
- local overallBestCharge = nil
- local overallBestAngle = nil
- local overallBestDistanceError = math.huge
- for ChargeCount = maxCharges, 2, -1 do -- Minimum charge count is now 2
- local bestAngle = nil
- local bestDistanceError = math.huge
- for angle = 10, 60, 0.1 do
- local height = Cannon.y + 0.01
- local muzzle_velocity = 40 * ChargeCount
- local rad_angle = math.rad(angle)
- local horizontal_velocity = muzzle_velocity * math.cos(rad_angle)
- local vertical_velocity = muzzle_velocity * math.sin(rad_angle)
- local time = 0
- local range = 0
- while height > TargetY or vertical_velocity > 0 do
- time = time + 0.05
- vertical_velocity = vertical_velocity - 23 * 0.05
- range = range + horizontal_velocity * 0.05
- horizontal_velocity = horizontal_velocity * 0.99
- height = height + vertical_velocity * 0.05
- end
- local distanceError = math.abs(range - Distance)
- if distanceError < bestDistanceError then
- bestDistanceError = distanceError
- bestAngle = angle
- end
- end
- -- Check if this is the best overall solution
- if bestDistanceError < overallBestDistanceError then
- overallBestDistanceError = bestDistanceError
- overallBestAngle = bestAngle
- overallBestCharge = ChargeCount
- end
- -- If the distance error is acceptable, return immediately
- if bestDistanceError <= MaxDistanceError then
- return ChargeCount, bestAngle, bestDistanceError
- end
- end
- -- After trying all charges, return nil if no acceptable solution found
- return nil, nil, nil
- end
- local function normalizeAngle(angle)
- angle = angle % 360
- if angle > 180 then
- angle = angle - 360
- end
- return angle
- end
- -- Function to handle the fire solution calculation
- -- Function to handle the fire solution calculation
- local function FireSolution(Target)
- -- Ensure Cannon data is loaded
- if not Cannon.x or not Cannon.y or not Cannon.z then
- return false, "Cannon data not loaded. Please set cannon info using <SetCannon> command."
- end
- -- Calculate Distance
- local Distance = math.sqrt((Target.x - Cannon.x)^2 + (Target.z - Cannon.z)^2)
- -- Call CalculatePitch()
- local ChargeCount, ClosestAngle, ClosestDistance = CalculatePitch(Cannon.maxCharges, Distance, Target.y)
- if ChargeCount then
- local rawYaw = math.deg(math.atan2(Target.z - Cannon.z, Target.x - Cannon.x)) - Cannon.yaw - 90
- local Yaw = normalizeAngle(rawYaw)
- local RotationTimeAngle = ClosestAngle * RotationConstant
- local YawTime = math.abs(Yaw * RotationConstant)
- local fireSolutionMessage = string.format(
- "Fire Solution:\nAccuracy range: ±%.2f blocks\nCharge Count: %d\nYaw: %.2f degrees\nAngle: %.2f degrees\nType 'confirm' to proceed with firing.",
- ClosestDistance, ChargeCount, Yaw, ClosestAngle
- )
- -- Return success, message, and necessary data
- return true, fireSolutionMessage, {
- ChargeCount = ChargeCount,
- ClosestAngle = ClosestAngle,
- ClosestDistance = ClosestDistance,
- Yaw = Yaw,
- RotationTimeAngle = math.abs(ClosestAngle * RotationConstant),
- YawTime = math.abs(Yaw * RotationConstant),
- Target = Target,
- username = pendingFireSolution and pendingFireSolution.username or nil,
- }
- else
- return false, "No Fire Solution possible within acceptable error margin for coordinates X=" .. Target.x .. ", Y=" .. Target.y .. ", Z=" .. Target.z
- end
- end
- -- Function to load the required number of charges
- local function func_load_shells_mission(shells)
- if shells < 2 or shells > 4 then -- Minimum shells is now 2
- print("Error: Number of shells must be between 2 and 4.")
- return
- end
- -- Activate the required charges
- for i = 1, shells do
- local chargeName = "Charge" .. i
- local chargeColor = chargeColors[chargeName] -- Access from the table
- if chargeColor then
- PowerOn(chargeColor)
- else
- print("Error: " .. chargeName .. " not defined.")
- end
- end
- PowerOn(chargeColors["Shell"])
- -- Wait for 2 seconds
- sleep(2)
- -- Deactivate all charges
- for i = 1, shells do
- local chargeName = "Charge" .. i
- local chargeColor = chargeColors[chargeName]
- if chargeColor then
- PowerOff(chargeColor)
- else
- print("Error: " .. chargeName .. " not defined.")
- end
- end
- PowerOff(chargeColors["Shell"])
- sleep(13)
- end
- -- Function to execute the fire mission
- local function ExecuteFireMission(fireMissionData)
- -- Extract data
- local ChargeCount = fireMissionData.ChargeCount
- local ClosestAngle = fireMissionData.ClosestAngle
- local Yaw = fireMissionData.Yaw
- local RotationTimeAngle = fireMissionData.RotationTimeAngle
- local YawTime = fireMissionData.YawTime
- local Target = fireMissionData.Target
- local username = fireMissionData.username
- -- Reset outputs
- ResetOutputs()
- PowerOff(componentColors.GunBuild)
- -- Load the required number of charges
- print("Loading " .. ChargeCount .. " charges...")
- sendAcceptedMessage(username, "Starting Fire Mission with " .. ChargeCount .. " charges.")
- func_load_shells_mission(ChargeCount)
- sendAcceptedMessage(username, ChargeCount .. " charges loaded.")
- -- Prepare for firing
- PowerOn(componentColors.GunBuild)
- sleep(0.5)
- -- Handle Yaw Direction
- if Yaw < 0 then
- PowerOn(componentColors.GunYawGearShift) -- Set direction for negative Yaw
- else
- PowerOff(componentColors.GunYawGearShift) -- Set direction for positive Yaw
- end
- -- Start moving both Pitch and Yaw
- print("Aiming Cannon Pitch and Yaw simultaneously...")
- local pitchActive = true
- local yawActive = true
- local elapsedTime = 0
- local timeStep = 0.1 -- Time increment for the loop
- -- Turn on both motors if movement is needed
- if RotationTimeAngle > 0 then
- PowerOn(componentColors.GunPitchClutch)
- else
- pitchActive = false -- No movement needed
- end
- if YawTime > 0 then
- PowerOn(componentColors.GunYawClutch)
- else
- yawActive = false -- No movement needed
- end
- -- Timer loop
- while pitchActive or yawActive do
- sleep(timeStep)
- elapsedTime = elapsedTime + timeStep
- -- Check Pitch
- if pitchActive and elapsedTime >= RotationTimeAngle then
- PowerOff(componentColors.GunPitchClutch)
- pitchActive = false
- print("Pitch adjustment complete.")
- end
- -- Check Yaw
- if yawActive and elapsedTime >= YawTime then
- PowerOff(componentColors.GunYawClutch)
- yawActive = false
- print("Yaw adjustment complete.")
- end
- end
- -- Fire the cannon
- print("Firing Cannon...")
- PowerOn(componentColors.GunAlarm)
- sleep(1)
- PowerOn(componentColors.GunFire)
- sleep(1)
- PowerOff(componentColors.GunFire)
- PowerOff(componentColors.GunAlarm)
- PowerOff(componentColors.GunBuild)
- print("Fire mission complete.")
- sendAcceptedMessage(username, "Fire Mission Complete.")
- end
- -- Attempt to load cannon data
- local dataLoaded = LoadCannonData()
- -- If data not loaded, prompt the user for cannon info
- if not dataLoaded then
- PromptForCannonInfo()
- end
- -- Main event loop for chat handling
- while true do
- local event, username, message, uuid, isHidden = os.pullEvent("chat")
- print("Chat event from " .. username .. ": " .. message)
- -- Check if the message is a command
- if message == "<GetMyUUID>" then
- -- Respond with the user's UUID
- sendInfoMessage(username, "Your UUID is: " .. uuid)
- elseif message:match("^<[%a]+>") then
- -- This is a command, check authorization
- if isAuthorized(uuid) then
- local x, y, z = message:match("^<FireSolution> (%-?%d+) (%-?%d+) (%-?%d+)")
- if x and y and z then
- -- Handle FireSolution command
- local Target = { x = tonumber(x), y = tonumber(y), z = tonumber(z) }
- local success, fireSolutionMessage, fireMissionData = FireSolution(Target)
- if success then
- -- Send the fire solution to the user via chat
- sendInfoMessage(username, fireSolutionMessage)
- -- Set pending fire solution
- pendingFireSolution = {
- username = username,
- data = fireMissionData,
- }
- else
- -- Send failure message
- sendInfoMessage(username, fireSolutionMessage)
- pendingFireSolution = nil
- end
- else
- sendInfoMessage(username, "Unknown command or invalid format.")
- end
- else
- -- Handle unauthorized access
- sendDeniedMessage(username)
- end
- elseif pendingFireSolution and username == pendingFireSolution.username then
- if message:lower() == "confirm" then
- -- Proceed with firing
- sendAcceptedMessage(username, "Firing mission proceeding.")
- pendingFireSolution.data.username = username -- Pass the username to the execution function
- ExecuteFireMission(pendingFireSolution.data)
- pendingFireSolution = nil
- elseif message:lower() == "cancel" then
- sendInfoMessage(username, "Fire mission canceled.")
- pendingFireSolution = nil
- else
- sendInfoMessage(username, "Please type 'confirm' to proceed or 'cancel' to abort.")
- end
- else
- -- Normal chat message
- print("Normal chat message: " .. message)
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement