nuclearempty

Untitled

Apr 26th, 2026 (edited)
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.14 KB | None | 0 0
  1. -- ============================================================================
  2. -- PROJECT ONYX: V38.0 (PRECISION FIRE CONTROL SYSTEM)
  3. -- ============================================================================
  4.  
  5. local reader = peripheral.wrap("block_reader_5")
  6. local speedYaw = peripheral.wrap("Create_RotationSpeedController_4")
  7. local speedPitch = peripheral.wrap("Create_RotationSpeedController_3")
  8. local relay1 = peripheral.wrap("redstone_relay_1")
  9.  
  10. local PIN_ASSEMBLE = "front"
  11. local PIN_FIRE = "back"
  12.  
  13. -- ----------------------------------------------------------------------------
  14. -- CONFIG
  15. -- ----------------------------------------------------------------------------
  16. local C_X, C_Y, C_Z = -5, -40, -2
  17. local V0 = 160.0
  18. local G = 20.0
  19. local MU = 1 - 0.99^20
  20. local L = 11
  21.  
  22. local YAW_OFFSET = 0
  23. local YAW_INVERT = -1
  24.  
  25. local FIXED_RPM = 1
  26. local REAL_DEG_PER_TICK = 0.002344
  27.  
  28. local MAX_PITCH, MIN_PITCH = 60, -30
  29.  
  30. local SETTINGS = {
  31. shots = 1,
  32. reload = 3.2,
  33. predictive = true,
  34. }
  35.  
  36. local bookmarks = {
  37. spawn = {x=0,y=64,z=0},
  38. }
  39.  
  40. -- ----------------------------------------------------------------------------
  41. -- UTILS
  42. -- ----------------------------------------------------------------------------
  43. local function log(cat, msg, color)
  44. if term.isColor() then term.setTextColor(color or colors.white) end
  45. print(string.format("[%s] %-8s | %s", os.date("%H:%M:%S"), cat, msg))
  46. term.setTextColor(colors.white)
  47. end
  48.  
  49. local function getPos()
  50. local d = reader.getBlockData()
  51. if not d then return nil, nil end
  52. local yaw = ((d.CannonYaw or 0) * YAW_INVERT) % 360
  53. if yaw < 0 then yaw = yaw + 360 end
  54. return yaw, (d.CannonPitch or 0)
  55. end
  56.  
  57. -- ----------------------------------------------------------------------------
  58. -- SOLVER (NEWTON + BARREL OFFSET)
  59. -- ----------------------------------------------------------------------------
  60. local function solve(tx, ty, tz)
  61. local dx, dz = tx - C_X, tz - C_Z
  62. local yaw = math.atan2(dx, dz)
  63.  
  64. -- barrel offset
  65. local ox = math.sin(yaw) * L
  66. local oz = math.cos(yaw) * L
  67.  
  68. local rx = tx - (C_X + ox)
  69. local rz = tz - (C_Z + oz)
  70.  
  71. local dist = math.sqrt(rx*rx + rz*rz)
  72. local height = ty - C_Y
  73.  
  74. local phi = (math.deg(yaw) + YAW_OFFSET) % 360
  75.  
  76. -- smart initial guess
  77. local theta = math.deg(math.atan2(height, dist))
  78.  
  79. for i=1,50 do
  80. local rad = math.rad(theta)
  81. local v0x = V0 * math.cos(rad)
  82. local v0y = V0 * math.sin(rad)
  83.  
  84. if math.abs(v0x) < 1e-6 then
  85. theta = theta + 1
  86. goto continue
  87. end
  88.  
  89. local max_x = v0x / MU
  90. if dist >= max_x * 0.999 then
  91. theta = theta + 1
  92. goto continue
  93. end
  94.  
  95. local inside = 1 - (dist * MU / v0x)
  96. if inside <= 0 then
  97. theta = theta + 1
  98. goto continue
  99. end
  100.  
  101. local t = -math.log(inside) / MU
  102. local exp = math.exp(-MU * t)
  103.  
  104. local y = (1/MU)*(v0y + G/MU)*(1-exp) - (G*t/MU)
  105. local err = height - y
  106.  
  107. if math.abs(err) < 0.01 then break end
  108.  
  109. -- slope
  110. local eps = 0.05
  111. local rad2 = math.rad(theta + eps)
  112. local v0x2 = V0 * math.cos(rad2)
  113. local v0y2 = V0 * math.sin(rad2)
  114.  
  115. local inside2 = 1 - (dist * MU / v0x2)
  116.  
  117. if inside2 > 0 and math.abs(v0x2) > 1e-6 then
  118. local t2 = -math.log(inside2) / MU
  119. local exp2 = math.exp(-MU * t2)
  120. local y2 = (1/MU)*(v0y2 + G/MU)*(1-exp2) - (G*t2/MU)
  121.  
  122. local slope = (y2 - y) / eps
  123.  
  124. if math.abs(slope) > 1e-6 then
  125. theta = theta + err / slope
  126. else
  127. theta = theta + err * 0.1
  128. end
  129. else
  130. theta = theta + 1
  131. end
  132.  
  133. -- clamp
  134. if theta > MAX_PITCH then theta = MAX_PITCH end
  135. if theta < MIN_PITCH then theta = MIN_PITCH end
  136.  
  137. ::continue::
  138. end
  139.  
  140. if theta >= MIN_PITCH and theta <= MAX_PITCH then
  141. return {p = theta, y = phi}
  142. end
  143.  
  144. return nil
  145. end
  146.  
  147. -- ----------------------------------------------------------------------------
  148. -- ALIGNMENT
  149. -- ----------------------------------------------------------------------------
  150. local function align(sol)
  151. while true do
  152. local cy, cp = getPos()
  153.  
  154. local dy = sol.y - cy
  155. while dy > 180 do dy = dy - 360 end
  156. while dy < -180 do dy = dy + 360 end
  157.  
  158. local dp = sol.p - cp
  159.  
  160. if math.abs(dy) < 1 and math.abs(dp) < 1 then break end
  161.  
  162. speedYaw.setTargetSpeed((math.abs(dy)>1) and 128*(dy>0 and 1 or -1) or 0)
  163. speedPitch.setTargetSpeed((math.abs(dp)>1) and 128*(dp>0 and 1 or -1) or 0)
  164.  
  165. sleep(0.05)
  166. end
  167.  
  168. speedYaw.setTargetSpeed(0)
  169. speedPitch.setTargetSpeed(0)
  170. end
  171.  
  172. -- ----------------------------------------------------------------------------
  173. -- PREDICTIVE FIRE
  174. -- ----------------------------------------------------------------------------
  175. local function fire(sol, shots)
  176. shots = shots or SETTINGS.shots
  177.  
  178. for s=1,shots do
  179. local lastY, lastP = getPos()
  180.  
  181. while true do
  182. local cy, cp = getPos()
  183.  
  184. local dy = sol.y - cy
  185. while dy > 180 do dy = dy - 360 end
  186. while dy < -180 do dy = dy + 360 end
  187.  
  188. local dp = sol.p - cp
  189.  
  190. local vY = cy - lastY
  191. local vP = cp - lastP
  192.  
  193. local eY = math.abs(dy)
  194. local eP = math.abs(dp)
  195.  
  196. -- ------------------------------------------------------------
  197. -- ✧ CASE 1: Perfect stop → FORCE MICRO MOVEMENT (key fix)
  198. -- ------------------------------------------------------------
  199. if math.abs(vY) < 1e-6 and math.abs(vP) < 1e-6 then
  200. if eY > 0.002 then
  201. speedYaw.setTargetSpeed(FIXED_RPM * (dy > 0 and 1 or -1))
  202. else
  203. speedYaw.setTargetSpeed(0)
  204. end
  205.  
  206. if eP > 0.002 then
  207. speedPitch.setTargetSpeed(FIXED_RPM * (dp > 0 and 1 or -1))
  208. else
  209. speedPitch.setTargetSpeed(0)
  210. end
  211. end
  212.  
  213. -- ------------------------------------------------------------
  214. -- ✧ CASE 2: Predictive firing (when moving)
  215. -- ------------------------------------------------------------
  216. local nextY = cy + vY
  217. local nextP = cp + vP
  218.  
  219. local eNow = math.abs(sol.y - cy) + math.abs(sol.p - cp)
  220. local eNext = math.abs(sol.y - nextY) + math.abs(sol.p - nextP)
  221.  
  222. if SETTINGS.predictive and (math.abs(vY) > 1e-6 or math.abs(vP) > 1e-6) then
  223. if eNext > eNow then
  224. break
  225. end
  226. end
  227.  
  228. -- ------------------------------------------------------------
  229. -- ✧ CASE 3: Hard fallback (like your old code)
  230. -- ------------------------------------------------------------
  231. if eY <= 0.01 and eP <= 0.01 then
  232. break
  233. end
  234.  
  235. lastY, lastP = cy, cp
  236. sleep(0.05)
  237. end
  238.  
  239. -- stop motion before firing
  240. speedYaw.setTargetSpeed(0)
  241. speedPitch.setTargetSpeed(0)
  242.  
  243. log("FIRE", "Shot "..s.."/"..shots, colors.red)
  244.  
  245. relay1.setOutput(PIN_FIRE, true)
  246. sleep(0.6)
  247. relay1.setOutput(PIN_FIRE, false)
  248.  
  249. if s < shots then
  250. sleep(SETTINGS.reload)
  251. end
  252. end
  253. end
  254.  
  255.  
  256. -- ----------------------------------------------------------------------------
  257. -- COMMAND SYSTEM
  258. -- ----------------------------------------------------------------------------
  259. local function help()
  260. print("\nCommands:")
  261. print(" shoot x y z [n]")
  262. print(" set shots n")
  263. print(" set reload t")
  264. print(" toggle predictive")
  265. print(" bookmark name x y z")
  266. print(" goto name [n]")
  267. print(" exit\n")
  268. end
  269.  
  270. local function main()
  271. term.clear()
  272. term.setCursorPos(1,1)
  273.  
  274. log("SYSTEM","ONYX V38 ONLINE",colors.cyan)
  275. relay1.setOutput(PIN_ASSEMBLE,true)
  276.  
  277. help()
  278.  
  279. while true do
  280. term.setTextColor(colors.lime)
  281. write("\n> ")
  282. term.setTextColor(colors.white)
  283.  
  284. local input = read()
  285. if input == "exit" then break end
  286.  
  287. local args = {}
  288. for w in input:gmatch("%S+") do table.insert(args,w) end
  289.  
  290. local cmd = args[1]
  291.  
  292. if cmd == "shoot" and #args >= 4 then
  293. local x,y,z = tonumber(args[2]), tonumber(args[3]), tonumber(args[4])
  294. local n = tonumber(args[5]) or SETTINGS.shots
  295.  
  296. local sol = solve(x,y,z)
  297. if sol then
  298. log("AIM", string.format("Y:%.2f P:%.2f",sol.y,sol.p), colors.blue)
  299. align(sol)
  300. fire(sol,n)
  301. else
  302. log("ERROR","Unreachable",colors.red)
  303. end
  304.  
  305. elseif cmd == "set" then
  306. if args[2]=="shots" then SETTINGS.shots = tonumber(args[3]) end
  307. if args[2]=="reload" then SETTINGS.reload = tonumber(args[3]) end
  308.  
  309. elseif cmd == "toggle" and args[2]=="predictive" then
  310. SETTINGS.predictive = not SETTINGS.predictive
  311. log("CONFIG","Predictive: "..tostring(SETTINGS.predictive),colors.orange)
  312.  
  313. elseif cmd == "bookmark" and #args>=5 then
  314. bookmarks[args[2]] = {
  315. x=tonumber(args[3]),
  316. y=tonumber(args[4]),
  317. z=tonumber(args[5])
  318. }
  319. log("SAVE","Bookmark "..args[2],colors.green)
  320.  
  321. elseif cmd == "goto" and bookmarks[args[2]] then
  322. local t = bookmarks[args[2]]
  323. local n = tonumber(args[3]) or SETTINGS.shots
  324.  
  325. local sol = solve(t.x,t.y,t.z)
  326. if sol then
  327. align(sol)
  328. fire(sol,n)
  329. end
  330.  
  331. else
  332. help()
  333. end
  334. end
  335. end
  336.  
  337. local ok, err = pcall(main)
  338. speedYaw.setTargetSpeed(0)
  339. speedPitch.setTargetSpeed(0)
  340.  
  341. if not ok then print("CRASH: "..err) end
  342.  
Advertisement
Add Comment
Please, Sign In to add comment