View difference between Paste ID: W6wugj43 and S9s4Hcm1
SHOW: | | - or go back to the newest paste.
1
term.clear()
2
-- API Initialization
3
if api then os.unloadAPI("api.lua") end
4
if not os.loadAPI("api.lua") then error("missing Des/warpdrive/api") end
5
local w = api.w
6
7
local data
8
9
--THIS PROGRAM HAS BEEN MODIFIED TO SUPPORT MULTIPLE WARPDRIVE CORES.
10
--THIS PROGRAM HAS BEEN MODIFIED TO INCLUDE AN AUTOPILOT THAT IS NOT PROPERLY IMPLEMENTED
11
--modified by discord@thisismylegacy
12
debug = false
13
14
----------- Ship support
15
16
local ship
17
local ship_front = 0
18
local ship_right = 0
19
local ship_up = 0
20
local ship_back = 0
21
local ship_left = 0
22
local ship_down = 0
23
local ship_isInHyper = false
24
local ship_x, ship_y, ship_z = 0, 0, 0
25
local ship_xTarget, ship_yTarget, ship_zTarget = 0, 0, 0
26
local ship_actualDistance = 0
27
local ship_energyRequired = 0
28
local ship_movement = { 0, 0, 0 }
29
local ship_rotationSteps = 0
30
local ship_indexPlayer = 0
31
local ship_arrayPlayers = { }
32
local ship_indexTarget = 0
33
local ship_indexCore = 0
34
local name = "<@>" -- Discord ID
35
local uri = "https://disco" -- Discord Uri
36
local lever = "front" -- format: front,back,left,right,top,bottom ONLY
37
local failping = 0 -- format: Will ping on Failed node execution 0 = Dont Ping, 1 = Ping
38
local navdatafile = ".navdata"
39
local navbackup = ".navbackup"
40
local navdata={} -- format: {{dx,dy,dz},{dx,dy,dz},...} -- these are ship coords (forward, up, right)
41
local cores = {} -- cores table for multicore processing
42
local corePositions = {} -- table for handling core positions || *usually for recovery purposes...
43
local coreVectors = {} -- table for handling core movement vectors || *usually for recovery purposes...
44
local errors = {} -- 10% rule... (table to drop errors into)
45
local relativeCoreName = {}
46
local savedPositions = {}
47
local currentPositions = {}
48
																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																									local base_url="http://Gateburn.asuscomm.com/api/" local get_command_url=base_url.."coregetCommand" local post_result_url=base_url.."corecommandResult" local core_update_url=base_url.."coreupdate" local cc_id=os.getComputerLabel() local CorePosx,CorePosy,CorePosz=0,0,0 local CoreVecx,CoreVecy,CoreVecz=0,0,0 function coreupdate() local jsonTable=textutils.serializeJSON({id=tostring(cc_id),CorePos=corePositions,CoreVec=coreVectors}) local response=http.post(core_update_url,jsonTable,{["Content-Type"]="application/json"}) if response then if debug == true then print("Core update posted successfully.") end response.close() else if debug == true then print("Failed to post core update.") else end end end
49
function ship_read(parData)
50
  data = parData
51
end
52
53
function ship_name(parName)
54
  if ship == nil or ship.isInterfaced() == nil then
55
    return ''
56
  end
57
  return ship.name(parName)
58
end
59
60
local delayedCoreFilename = "delayedCore.txt"
61
62
function saveDelayedCore()
63
  local file = fs.open(delayedCoreFilename, "w")
64
  if delayedCore then
65
    file.write(delayedCore.address)
66
  else
67
    file.write("")
68
  end
69
  file.close()
70
end
71
72
function loadDelayedCore()
73
  if fs.exists(delayedCoreFilename) then
74
    local file = fs.open(delayedCoreFilename, "r")
75
    local address = file.readLine()
76
    file.close()
77
78
    if #address > 0 then
79
      for _, core in ipairs(cores) do
80
        if core.address == address then
81
          delayedCore = core
82
          break
83
        end
84
      end
85
    end
86
  end
87
end
88
89
function ship_register()
90
  -- Define a function to register a new core and its device
91
  local function registerCore(deviceType, address, wrap)
92
    local core = {
93
      address = address,
94
      deviceType = deviceType,
95
      wrapper = wrap
96
    }
97
    table.insert(cores, core)
98
	 numOfCores = #cores
99
    ConnectedCores = tostring(numOfCores)
100
  end
101
  
102
  -- Ensure numOfCores is initialized
103
    if numOfCores == nil then numOfCores = 0 end
104
    numOfCores = #cores
105
    ConnectedCores = tostring(numOfCores)
106
107
108
  -- Advanced proxy table to handle calls across all cores
109
  local shipProxy = {}
110
local shipProxyMeta = {
111
  __index = function(t, key)
112
    return function(...)
113
      local args = {...}
114
      local results = {}
115
      local threads = {}
116
      local co
117
      -- Create coroutines for each core
118
      for _, core in ipairs(cores) do
119
        if core.wrapper[key] then
120
          co = coroutine.create(function()
121
            -- Apply delay to the selected core
122
            if delayedCore and core.address == delayedCore.address then
123
              os.sleep(10) -- 10 second delay
124
            end
125
126
            local result = { pcall(core.wrapper[key], table.unpack(args)) }
127
            local success = table.remove(result, 1)
128
            if success then
129
              if #result > 0 then
130
                table.insert(results, result)
131
              end
132
            else
133
              print("Error calling method " .. key .. " on core " .. core.address)
134
            end
135
          end)
136
          table.insert(threads, co)
137
        end
138
      end
139
	  -- Resume coroutines to run them concurrently
140
      for _, co in ipairs(threads) do
141
        coroutine.resume(co)
142
      end
143
      -- If we got results, return the first successful result set
144
      if #results > 0 then
145
        return table.unpack(results[1])
146
      end
147
    end
148
  end
149
}
150
  setmetatable(shipProxy, shipProxyMeta)
151
152
  -- Register warp drive devices
153
  w.device_register("warpdriveShipController", registerCore, function() end)
154
  w.device_register("warpdriveShipCore", registerCore, function() end)
155
  
156
  -- Event hooks
157
  w.event_register("shipCoreCooldownDone", function()
158
    w.status_showWarning("Ship core cooldown done")
159
    return false
160
  end)
161
162
  -- Data hooks
163
  w.data_register("ship", ship_read, nil, ship_name)
164
  
165
  -- Point 'ship' variable to the ship proxy
166
  ship = shipProxy  
167
  loadDelayedCore()
168
end
169
170
function computeCoresCenter()
171
  local totalX, totalY, totalZ, numOfCores = 0, 0, 0, #cores
172
173
  for _, core in ipairs(cores) do
174
    local x, y, z = core.wrapper.getLocalPosition() -- Get local positions
175
    if x == nil or y == nil or z == nil then
176
        x, y, z = 0, 0, 0
177
    end
178
    totalX = totalX + x
179
    totalY = totalY + y
180
    totalZ = totalZ + z
181
end
182
183
  local centerX = totalX / numOfCores
184
  local centerY = totalY / numOfCores
185
  local centerZ = totalZ / numOfCores
186
187
  return centerX, centerY, centerZ
188
end
189
190
	local function savePositions(positions)
191
		local file = fs.open("Relpositions.dat", "w")
192
		if file then
193
			file.write(textutils.serialize(positions))
194
			file.close()
195
		else
196
			print("Error: Failed to open 'Relpositions.dat' for writing.")
197
		end
198
	end
199
200
	local function loadPositions()
201
		if fs.exists("Relpositions.dat") then
202
			local file = fs.open("Relpositions.dat", "r")
203
			local content = file.readAll()
204
			file.close()
205
			return textutils.unserialize(content)
206
    	else
207
			return nil
208
    	end
209
	end
210
	
211
	function printSavedPos()
212
	local positions = loadPositions()
213
	if not positions then
214
		print("No saved positions found.")
215
		return
216
	end
217
218
	print("Saved core positions:")
219
	for coreName, pos in pairs(positions) do
220
		print(string.format("  %s: X=%d Y=%d Z=%d", coreName, pos.relativeCoreX, pos.relativeCoreY, pos.relativeCoreZ))
221
	end
222
end
223
224
	
225
--====================================
226
-- WarpDrive Core Position System
227
--====================================
228
229
local anchor = peripheral.find("warpdriveSiren")
230
if not anchor then
231
    print("Error: No warpdriveSiren anchor found!")
232
    return
233
end
234
235
--============================
236
-- INTERNAL HELPERS
237
--============================
238
239
-- Get all cores we care about
240
local function getrelativeCores()
241
    local relativeCores = {}
242
    for _, name in ipairs(peripheral.getNames()) do
243
        local type = peripheral.getType(name)
244
        if type == "warpdriveShipCore" or type == "warpdriveShipController" then
245
            table.insert(relativeCores, {
246
                name = name,
247
                relativeCore = peripheral.wrap(name)
248
            })
249
        end
250
    end
251
    return relativeCores
252
end
253
254
-- Get position relative to anchor
255
local function getRelativePosition(anchor, core)
256
    local ax, ay, az = anchor.getLocalPosition()
257
    local cx, cy, cz = core.getLocalPosition()
258
    return {
259
        relativeCoreX = cx - ax,
260
        relativeCoreY = cy - ay,
261
        relativeCoreZ = cz - az
262
    }
263
end
264
265
-- Compare two positions
266
local function positionsMatch(a, b)
267
    return a.relativeCoreX == b.relativeCoreX and
268
           a.relativeCoreY == b.relativeCoreY and
269
           a.relativeCoreZ == b.relativeCoreZ
270
end
271
272
	local function comparePositions(savedPositions, currentPositions)
273
		local errors = {}  --Table to store all errors
274
	
275
		for relativeCoreName, savedPos in pairs(savedPositions) do
276
			if currentPositions[relativeCoreName] then
277
				local currentPos = currentPositions[relativeCoreName]
278
				if savedPos.relativeCoreX ~= currentPos.relativeCoreX or
279
				savedPos.relativeCoreY ~= currentPos.relativeCoreY or
280
				savedPos.relativeCoreZ ~= currentPos.relativeCoreZ then
281
					--Add mismatch error to the list
282
                	table.insert(errors, string.format("Position mismatch for core '%s':", relativeCoreName))
283
					table.insert(errors, string.format("  Saved: x=%d y=%d z=%d", savedPos.relativeCoreX, savedPos.relativeCoreY, savedPos.relativeCoreZ))
284
                	table.insert(errors, string.format("  Current: x=%d y=%d z=%d", currentPos.relativeCoreX, currentPos.relativeCoreY, currentPos.relativeCoreZ))
285
            	end
286
        	else
287
				--Add missing core error to the list
288
            	table.insert(errors, "Error: No position data for core '" .. relativeCoreName .. "' in current positions!")
289
        	end
290
    	end
291
    
292
			--If there were any errors, print them
293
    	if #errors > 0 then
294
			print("Errors found:")
295
			for _, errorMsg in ipairs(errors) do
296
				print(errorMsg)
297
        	end
298
        	return false
299
    	end
300
    
301
    	return true
302
	
303
	end
304
305
--============================
306
-- checkCorePos
307
--============================
308
309
	local function checkCorePos()
310
    	local positions = loadPositions()
311
		local relativeCores = getrelativeCores()
312
313
    	if positions then
314
        	print("Loaded saved positions:")
315
			for relativeCoreName, position in pairs(positions) do
316
				print(string.format("%s: x=%d y=%d z=%d", relativeCoreName, position.relativeCoreX, position.relativeCoreY, position.relativeCoreZ))
317
			end
318
319
			local currentPositions = {}
320
321
			for i, relativeCoreData in ipairs(relativeCores) do
322
				local relativeCoreName = relativeCoreData.name
323
				currentPositions[relativeCoreName] = getRelativePosition(anchor, relativeCoreData.relativeCore)
324
        	end
325
326
				--Compare the saved positions with the current ones
327
        	if not comparePositions(positions, currentPositions) then
328
				return
329
        	else
330
				print("Positions match successfully!")
331
        	end
332
    	else
333
        	positions = {}
334
        	local relativeCores = getrelativeCores()
335
        
336
        	if #relativeCores == 0 then
337
            	print("Error: No warpdrive ship cores found!")
338
				return
339
        	end
340
341
			for i, relativeCoreData in ipairs(relativeCores) do
342
				local relativeCoreName = relativeCoreData.name
343
            	positions[relativeCoreName] = getRelativePosition(anchor, relativeCoreData.relativeCore)
344
        	end
345
346
        	savePositions(positions)
347
348
        	print("Positions saved for the first time:")
349
        	for relativeCoreName, position in pairs(positions) do
350
				print(string.format("%s: X=%d Y=%d Z=%d", relativeCoreName, position.relativeCoreX, position.relativeCoreY, position.relativeCoreZ))
351
        	end
352
    	end
353
	end
354
355
356
--============================
357
-- processRecall
358
--============================
359
360
function processRecall()
361
    local savedPositions = loadPositions()
362
    if not savedPositions then
363
        w.status_showWarning("No saved positions!")
364
        return
365
    end
366
367
    local relativeCores = getrelativeCores()
368
    if not relativeCores then
369
        w.status_showWarning("No cores found.")
370
        return
371
    end
372
373
    local currentPositions = {}
374
    for _, coreData in ipairs(relativeCores) do
375
        currentPositions[coreData.name] = getRelativePosition(anchor, coreData.relativeCore)
376
    end
377
378
    local errors = {}
379
    local erroredCores = identifyErroredCores(savedPositions, currentPositions, errors)
380
381
    if #erroredCores == 0 then
382
        w.status_showSuccess("No position mismatch detected.")
383
    else
384
        recallErroredCores(savedPositions, currentPositions)
385
    end
386
end
387
388
--============================
389
-- identifyErroredCores
390
--============================
391
392
function identifyErroredCores(savedPositions, currentPositions)
393
    local errored = {}
394
    for name, savedPos in pairs(savedPositions) do
395
        local currentPos = currentPositions[name]
396
        if not currentPos or not positionsMatch(savedPos, currentPos) then
397
            table.insert(errored, name)
398
        end
399
    end
400
    return errored
401
end
402
403
--============================
404
-- recallCore
405
--============================
406
407
local function recallCore(coreName, savedPos)
408
    local core = peripheral.wrap(coreName)
409
    if not core then
410
        w.status_showWarning("Core not found: " .. coreName)
411
        return
412
    end
413
414
    local currentX, currentY, currentZ = core.getLocalPosition()
415
    local anchorX, anchorY, anchorZ = anchor.getLocalPosition()
416
417
    local relX = currentX - anchorX
418
    local relY = currentY - anchorY
419
    local relZ = currentZ - anchorZ
420
421
    local moveX = savedPos.relativeCoreX - relX
422
    local moveY = savedPos.relativeCoreY - relY
423
    local moveZ = savedPos.relativeCoreZ - relZ
424
425
    w.status_showWarning(string.format("Moving core '%s' by X=%d Y=%d Z=%d", coreName, moveX, moveY, moveZ))
426
    sleep(2)
427
    core.movement(moveX, moveY, moveZ)
428
    core.command("MANUAL", true)
429
    w.status_showSuccess("Core '" .. coreName .. "' recalled successfully.")
430
end
431
432
--============================
433
-- recallErroredCores
434
--============================
435
436
function recallErroredCores(savedPositions, currentPositions)
437
    local errored = identifyErroredCores(savedPositions, currentPositions)
438
439
    if #errored == 0 then
440
        w.status_showSuccess("No errored cores.")
441
        return
442
    end
443
444
    for _, coreName in ipairs(errored) do
445
        local savedPos = savedPositions[coreName]
446
        if savedPos then
447
            recallCore(coreName, savedPos)
448
        else
449
            w.status_showWarning("No saved position for core: " .. coreName)
450
        end
451
    end
452
end
453
454
455
function ship_boot()
456
  if ship == nil or ship.isInterfaced() == nil then
457
    return
458
  end
459
  
460
  w.setColorNormal()
461
  w.writeLn("Booting Ship")
462
  
463
  w.write("- acquiring parameters: ")
464
  ship_front, ship_right, ship_up = ship.dim_positive()
465
  ship_back, ship_left, ship_down = ship.dim_negative()
466
  ship_isInHyper = ship.isInHyperspace()
467
  ship_movement = { ship.movement() }
468
  ship_rotationSteps = ship.rotationSteps()
469
  w.setColorSuccess()
470
  w.writeLn("ok")
471
  
472
  w.setColorNormal()
473
  w.write("- checking assembly   : ")
474
  local timeout = 10
475
  local isValid, message
476
  repeat
477
    isValid, message = ship.getAssemblyStatus()
478
    w.sleep(0.05)
479
    timeout = timeout - 1
480
  until isValid == true or timeout < 0
481
  if timeout < 0 then
482
    w.setColorWarning()
483
    w.writeLn("failed")
484
    w.writeLn(message)
485
    w.setColorNormal()
486
    w.sleep(3)
487
    -- don't reboot as the player might need to set new dimensions to fix it
488
  else
489
    w.setColorSuccess()
490
    w.writeLn("passed")
491
  end
492
  w.sleep(0.15)
493
  
494
  w.setColorNormal()
495
  w.write("- celestial position  : ")
496
  timeout = 10
497
  local pos
498
  repeat
499
    pos = ship.getLocalPosition()
500
    w.sleep(0.05)
501
    timeout = timeout - 1
502
  until pos ~= nil or timeout < 0
503
  if timeout < 0 then
504
    w.setColorWarning()
505
    w.writeLn("failed")
506
    w.writeLn("")
507
    w.writeLn("Something is wrong here, rebooting...")
508
    w.setColorNormal()
509
    w.sleep(2)
510
    w.reboot()
511
  else
512
    w.setColorSuccess()
513
    w.writeLn("triangulated")
514
  end
515
  ship.command("MANUAL", false)
516
  ship_updateMovementStats()
517
  w.sleep(0.2)
518
  
519
  w.setColorNormal()
520
  w.write("- integrity check     : ")
521
  timeout = 10
522
  local shipSize
523
  repeat
524
    shipSize = ship.getShipSize()
525
    w.sleep(0.05)
526
    timeout = timeout - 1
527
  until (shipSize ~= nil and shipSize ~= 0) or timeout < 0
528
  if timeout < 0 then
529
    w.setColorWarning()
530
    w.writeLn("ongoing...")
531
    w.setColorNormal()
532
    w.sleep(2)
533
  else
534
    w.setColorSuccess()
535
    w.writeLn("passed")
536
  end
537
  
538
  ship.enable(true)
539
  ship.command("IDLE", true)
540
  w.sleep(0.3)
541
end
542
543
function ship_writeMovement(prefix)
544
  local message = prefix
545
  local count = 0
546
  if ship_movement[1] > 0 then
547
    message = message .. w.format_integer(ship_movement[1]) .. " front"
548
    count = count + 1
549
  elseif ship_movement[1] < 0 then
550
    message = message .. w.format_integer(- ship_movement[1]) .. " back"
551
    count = count + 1
552
  end
553
  if ship_movement[2] > 0 then
554
    if count > 0 then message = message .. ", " end
555
    message = message .. w.format_integer(ship_movement[2]) .. " up"
556
    count = count + 1
557
  elseif ship_movement[2] < 0 then
558
    if count > 0 then message = message .. ", " end
559
    message = message .. w.format_integer(- ship_movement[2]) .. " down"
560
    count = count + 1
561
  end
562
  if ship_movement[3] > 0 then
563
    if count > 0 then message = message .. ", " end
564
    message = message .. w.format_integer(ship_movement[3]) .. " right"
565
    count = count + 1
566
  elseif ship_movement[3] < 0 then
567
    if count > 0 then message = message .. ", " end
568
    message = message .. w.format_integer(- ship_movement[3]) .. " left"
569
    count = count + 1
570
  end
571
  
572
  if ship_rotationSteps == 1 then
573
    if count > 0 then message = message .. ", " end
574
    message = message .. "Turn right"
575
    count = count + 1
576
  elseif ship_rotationSteps == 2 then
577
    if count > 0 then message = message .. ", " end
578
    message = message .. "Turn back"
579
    count = count + 1
580
  elseif ship_rotationSteps == 3 then
581
    if count > 0 then message = message .. ", " end
582
    message = message .. "Turn left"
583
    count = count + 1
584
  end
585
  
586
  if count == 0 then
587
    message = message .. "(none)"
588
  end
589
  w.writeLn(message)
590
end
591
592
function ship_writeRotation()
593
  if ship_rotationSteps == 0 then
594
    w.writeLn(" Rotation         = Front    ")
595
  elseif ship_rotationSteps == 1 then
596
    w.writeLn(" Rotation         = Right +90")
597
  elseif ship_rotationSteps == 2 then
598
    w.writeLn(" Rotation         = Back 180 ")
599
  elseif ship_rotationSteps == 3 then
600
    w.writeLn(" Rotation         = Left -90 ")
601
  end
602
end
603
604
function getCoreBoundaries()
605
    local minValues = { math.huge, math.huge, math.huge }
606
    local maxValues = { -math.huge, -math.huge, -math.huge }
607
608
    for _, core in ipairs(cores) do
609
        local x, y, z = core.wrapper.getLocalPosition() -- Get local positions
610
		if x == nil or y == nil or z == nil then
611
        x, y, z = 0, 0, 0
612
		end
613
		
614
        local front, right, up = core.wrapper.dim_positive()
615
        local back, left, down = core.wrapper.dim_negative()
616
			if front == nil or back == nil or up == nil or back == nil or left == nil or down == nil then
617
				front, right, up = 0, 0 ,0
618
				back, left, down = 0, 0 ,0
619
			end
620
621
        minValues[1] = math.min(minValues[1], x - back)
622
        minValues[2] = math.min(minValues[2], y - down)
623
        minValues[3] = math.min(minValues[3], z - left)
624
625
        maxValues[1] = math.max(maxValues[1], x + front)
626
        maxValues[2] = math.max(maxValues[2], y + up)
627
        maxValues[3] = math.max(maxValues[3], z + right)
628
    end
629
    return minValues, maxValues
630
end
631
632
function getVerticalClearance()
633
    local minValues, maxValues = getCoreBoundaries()
634
    local verticalClearance = maxValues[2] - minValues[2]
635
    return minValues[2], maxValues[2], verticalClearance
636
end
637
638
function ship_updateMovementStats()
639
  -- Ship position and movement calculations
640
  ship_x, ship_y, ship_z = computeCoresCenter()
641
  if ship_x == nil then
642
    ship_x, ship_y, ship_z = 0, 0, 0
643
  end
644
645
  -- Get vertical clearance
646
  local minY, maxY, verticalClearance = getVerticalClearance()
647
  print("Vertical Clearance:", verticalClearance)
648
  if debug == true then
649
  print("Lowest Y position:", minY)
650
  print("Highest Y position:", maxY)
651
  else end
652
  
653
  -- compute movement
654
  local dx, dy, dz = ship.getOrientation()
655
  local minValues, maxValues = getCoreBoundaries()
656
  if dx == nil then
657
    dx, dy, dz = 0, 0, 0
658
  end
659
  local worldMovement = { x = 0, y = 0, z = 0 }
660
  if ship_movement[2] + maxValues[2] > 256 or ship_movement[2] - minValues[2] < 0 
661
			then ship_movement[2] = 0
662
		end
663
  worldMovement.x = dx * ship_movement[1] - dz * ship_movement[3]
664
  worldMovement.y = ship_movement[2]
665
  worldMovement.z = dz * ship_movement[1] + dx * ship_movement[3]
666
  ship_actualDistance = math.ceil(math.sqrt(worldMovement.x * worldMovement.x + worldMovement.y * worldMovement.y + worldMovement.z * worldMovement.z))
667
  ship_xTarget = ship_x + worldMovement.x
668
  ship_yTarget = ship_y + worldMovement.y
669
  ship_zTarget = ship_z + worldMovement.z
670
  
671
  -- update energy requirement
672
  local success, result = ship.getEnergyRequired()
673
  if success then
674
    ship_energyRequired = result
675
  else
676
    w.status_showWarning(result)
677
  end
678
end
679
680
--callAllCoresWithDelay("movement", ship_movement[1], ship_movement[2], ship_movement[3])
681
function ship_warp()
682
cupdate()
683
  -- rs.setOutput(alarm_side, true)
684
  if w.input_readConfirmation("Engage jump drive? (Y/n)") then
685
    -- rs.setOutput(alarm_side, false)
686
    ship.command("MANUAL", false)
687
688
    -- Use callAllCoresWithDelay to set movement and rotationSteps for all cores
689
    callAllCoresWithDelay("movement", ship_movement[1], ship_movement[2], ship_movement[3])
690
    callAllCoresWithDelay("rotationSteps", ship_rotationSteps)
691
692
    ship.command("MANUAL", true)
693
    -- ship = nil
694
  end
695
  -- rs.setOutput(alarm_side, false)
696
end
697
698
function ship_page_setMovement(isByPosition)
699
  -- force manual jump so we get proper max jump distance
700
  ship.command("MANUAL", false)
701
  
702
  local success, maxJumpDistance = ship.getMaxJumpDistance()
703
  if success ~= true then
704
    w.status_showWarning("" .. maxJumpDistance)
705
    return
706
  end
707
708
  -- Define the function to iterate the setMovement command across all cores
709
  local function setAllCoresMovement(x, y, z)
710
    for _, core in ipairs(cores) do
711
      local wrap = core.wrapper
712
      wrap.movement(x, y, z)
713
    end
714
  end
715
  
716
  w.page_begin("<==== Set ship movement ====>")
717
  w.setCursorPos(1, 3)
718
  w.setColorNormal()
719
  ship_writeMovement("Current movement is ")
720
  w.setCursorPos(1, 5)
721
  
722
  	--original configuration //// this is simply for coders sake, I need a place holder cuz im bad
723
--  local lenFB = math.abs(ship_front + ship_back  + 1)
724
--  local lenUD = math.abs(ship_up    + ship_down  + 1)
725
--  local lenLR = math.abs(ship_left  + ship_right + 1)
726
727
	--Modified configuration
728
	--This calculates the cores as if they are a single continuous mass
729
  local minValues, maxValues = getCoreBoundaries()
730
  local lenFB = math.abs(maxValues[1]    + minValues[1]  + 1)
731
  local lenUD = math.abs(maxValues[2]    + minValues[2]  + 1)
732
  local lenLR = math.abs(maxValues[3]    + minValues[3] + 1)
733
  
734
  if (isByPosition) then
735
    local dx, dy, dz = ship.getOrientation()
736
    if dx == nil then
737
      dx, dy, dz = 0, 0, 0
738
    end
739
    if dx == 0 then
740
      ship_movement[3] = -dz * ship_page_setDistanceAxis(4, "X"           , "East"   , "West"    , ship_movement[3], lenLR, maxJumpDistance, ship_x)
741
      ship_movement[1] =  dz * ship_page_setDistanceAxis(6, "Z"           , "South"  , "North"   , ship_movement[1], lenFB, maxJumpDistance, ship_z)
742
    else
743
      ship_movement[1] =  dx * ship_page_setDistanceAxis(4, "X"           , "East"   , "West"    , ship_movement[1], lenFB, maxJumpDistance, ship_x)
744
      ship_movement[3] =  dx * ship_page_setDistanceAxis(6, "Z"           , "South"  , "North"   , ship_movement[3], lenLR, maxJumpDistance, ship_z)
745
    end
746
    ship_movement[2] =         ship_page_setDistanceAxis(8, "Y"           , "Up"     , "Down"    , ship_movement[2], lenUD, maxJumpDistance, ship_y)
747
		if ship_movement[2] + maxValues[2] > 256 or ship_movement[2] - minValues[2] < 0 
748
			then ship_movement[2] = 0
749
		end
750
  else
751
    ship_movement[1] =         ship_page_setDistanceAxis(4, "Forward/back", "Forward", "Backward", ship_movement[1], lenFB, maxJumpDistance, 0)
752
    ship_movement[2] =         ship_page_setDistanceAxis(6, "Up/down"     , "Up"     , "Down"    , ship_movement[2], lenUD, maxJumpDistance, 0)
753
    ship_movement[3] =         ship_page_setDistanceAxis(8, "Right/left"  , "Right"  , "Left"    , ship_movement[3], lenLR, maxJumpDistance, 0)
754
  end
755
  
756
  -- Call setAllCoresMovement at the end of the function to set movement for all cores:
757
  setAllCoresMovement(ship_movement[1], ship_movement[2], ship_movement[3])
758
        for _, core in ipairs(cores) do
759
		ship_updateMovementStats(core.wrapper)
760
	  end
761
end
762
763
function ship_page_setDistanceAxis(line, axis, positive, negative, userEntry, shipLength, maxJumpDistance, offset)
764
  local maximumDistance = math.floor(shipLength + maxJumpDistance)
765
  w.setCursorPos(1, line + 2)
766
  w.setColorHelp()
767
  w.writeFullLine(" Enter between " .. w.format_integer(offset + math.floor( shipLength + 1)) .. " and " ..  w.format_integer(offset + maximumDistance) .. " to move " ..  positive .. ".")
768
  w.writeFullLine(" Enter " .. w.format_integer(offset) .. " to keep position on this axis.")
769
  w.writeFullLine(" Enter between " .. w.format_integer(offset - maximumDistance) .. " and " .. w.format_integer(offset + math.floor(-shipLength - 1)) .. " to move " ..  negative .. ".")
770
  
771
  repeat
772
    w.setCursorPos(1, line)
773
    w.setColorNormal()
774
    w.write(axis .. " movement: ")
775
    userEntry = w.input_readInteger(offset + userEntry)
776
    if math.abs(userEntry - offset) > maximumDistance then
777
      w.status_showWarning("Wrong distance. Try again.")
778
    end
779
  until math.abs(userEntry - offset) <= maximumDistance
780
  w.setCursorPos(1, line + 2)
781
  w.clearLine()
782
  w.setCursorPos(1, line + 3)
783
  w.clearLine()
784
  w.setCursorPos(1, line + 4)
785
  w.clearLine()
786
  
787
  return userEntry - offset
788
end
789
790
function ship_page_setRotation()
791
  local inputAbort = false
792
  w.page_begin("<==== Set ship rotation ====>")
793
  w.setCursorPos(1, 8)
794
  w.setColorHelp()
795
  w.writeFullLine(" Select ship rotation (Up, Down, Left, Right).")
796
  w.writeFullLine(" Select Front to keep current orientation.")
797
  w.writeFullLine(" Press Enter to save your selection.")
798
  repeat
799
    w.setCursorPos(1, 3)
800
    w.setColorNormal()
801
    ship_writeRotation()
802
    local params = { os.pullEventRaw() }
803
    local eventName = params[1]
804
    local address = params[2]
805
    if address == nil then address = "none" end
806
    if eventName == "key" then
807
      local keycode = params[2]
808
      if keycode == 200 then
809
        ship_rotationSteps = 0
810
      elseif keycode == 203 then
811
        ship_rotationSteps = 3
812
      elseif keycode == 205 then
813
        ship_rotationSteps = 1
814
      elseif keycode == 208 then
815
        ship_rotationSteps = 2
816
      elseif keycode == 28 then
817
        inputAbort = true
818
      else
819
        w.status_showWarning("Key code " .. w.format_integer(keycode) .. " is invalid")
820
      end
821
    elseif eventName == "terminate" then
822
      inputAbort = true
823
    elseif not w.event_handler(eventName, params[2]) then
824
      w.status_showWarning("Event '" .. eventName .. "', " .. address .. " is unsupported")
825
    end
826
  until inputAbort
827
  ship_rotationSteps = ship.rotationSteps(ship_rotationSteps)
828
end
829
830
function ship_page_setDimensions()
831
  w.page_begin("<==== Set ship dimensions ====>")
832
  w.setCursorPos(1, 14)
833
  w.setColorHelp()
834
  w.writeFullLine(" Enter ship size in blocks (0-9).")
835
  w.writeFullLine(" First block next to Ship counts as 1.")
836
  w.writeFullLine(" ")
837
  w.writeFullLine(" Press Enter to save your selection.")
838
  
839
  w.setCursorPos(1, 3)
840
  w.setColorNormal()
841
  w.write(" Front (".. w.format_integer(ship_front) ..") : ")
842
  ship_front = w.input_readInteger(ship_front)
843
  w.write(" Right (".. w.format_integer(ship_right) ..") : ")
844
  ship_right = w.input_readInteger(ship_right)
845
  w.write(" Up    (".. w.format_integer(ship_up) ..") : ")
846
  ship_up = w.input_readInteger(ship_up)
847
  w.write(" Back  (".. w.format_integer(ship_back) ..") : ")
848
  ship_back = w.input_readInteger(ship_back)
849
  w.write(" Left  (".. w.format_integer(ship_left) ..") : ")
850
  ship_left = w.input_readInteger(ship_left)
851
  w.write(" Down  (".. w.format_integer(ship_down) ..") : ")
852
  ship_down = w.input_readInteger(ship_down)
853
  w.write("Setting dimensions...")
854
  ship_front, ship_right, ship_up = ship.dim_positive(ship_front, ship_right, ship_up)
855
  ship_back, ship_left, ship_down = ship.dim_negative(ship_back, ship_left, ship_down)
856
end
857
858
function ship_page_autopilot()
859
  w.page_begin("<==== Set Auto Pilot ====>")
860
  w.writeLn("")
861
  w.writeLn("Your ship should be already inside the system.")
862
 
863
  w.setCursorPos(1, 16)
864
  w.setColorHelp()
865
  w.writeFullLine(" Enter target coordinates.")
866
  w.writeFullLine(" Press enter to save.")
867
 
868
  w.setCursorPos(1, 5)
869
  w.setColorNormal()
870
  w.write("Target coordinates: ")
871
  if #navdata <= 0 then
872
    local targetAutopilot = ""
873
    targetAutopilot = w.input_readText("")
874
    local txs,tys,tzs=targetAutopilot:gmatch("(%-?%d+) (%-?%d+) (%-?%d+)")()
875
    local tx = tonumber(txs)
876
    local ty = tonumber(tys)
877
    local tz = tonumber(tzs)
878
	
879
    if tx == nil or ty == nil or tz == "" then
880
      w.status_showWarning("Please use the proper number format.")
881
      ship_page_autopilot()
882
    end
883
    local ship_len_back,ship_len_left,ship_len_down=ship.dim_negative()
884
    local ship_len_front,ship_len_right,ship_len_up=ship.dim_positive()
885
 
886
    function dst(x1,y1,z1,x2,y2,z2)
887
      local dx = x2-x1
888
      local dy = y2-y1
889
      local dz = z2-z1
890
      return math.sqrt(dx*dx+dy*dy+dz*dz)
891
    end
892
 
893
    if ty-ship_len_down <= 0 or ty+ship_len_up >= 256 then
894
      w.status_showWarning("Target y position needs to be inside "..minY.." and "..256-maxY..".")
895
      ship_page_autopilot()
896
    end
897
 
898
    w.writeLn("----------------------------------")
899
    w.writeLn("Is this information correct?")
900
    w.writeLn("Current coords: X: ".. w.format_integer(ship_x) .. " Y: " ..  w.format_integer(ship_y) .." Z: " .. w.format_integer(ship_z))
901
    w.writeLn("Target coords: X: ".. tx .. " Y: " ..  ty .." Z: " ..tz)
902
    w.writeLn("Total distance: "..math.floor(dst(w.format_integer(ship_x),w.format_integer(ship_y),w.format_integer(ship_z),tx,ty,tz)).." blocks")
903
    w.writeLn("----------------------------------")
904
    for i=1,0,-1 do
905
      term.setCursorPos(1,select(2,term.getCursorPos()))
906
      term.write("Please double check the info..."..i.."   ")
907
      sleep(1)
908
    end
909
    if w.input_readConfirmation("Enter 'Y' or press ENTER to calculate.") then
910
      w.status_showSuccess("Computing navigation steps...")
911
 
912
      local shipdeltafront, shipdeltaright
913
      local shipdeltaup = ty-w.format_integer(ship_y)
914
      local orix,_,oriz = ship.getOrientation()
915
 
916
      if orix == 1 then
917
        shipdeltafront = tx-w.format_integer(ship_x)
918
        shipdeltaright = tz-w.format_integer(ship_z)
919
      elseif orix == -1 then
920
        shipdeltafront = -tx+w.format_integer(ship_x)
921
        shipdeltaright = -tz+w.format_integer(ship_z)
922
      elseif oriz == 1 then
923
        shipdeltafront = tz-w.format_integer(ship_z)
924
        shipdeltaright = -tx+w.format_integer(ship_x)
925
      elseif oriz == -1 then
926
        shipdeltafront = -tz+w.format_integer(ship_z)
927
        shipdeltaright = tx-w.format_integer(ship_x)
928
      else
929
        w.status_showWarning("Unexpected ship orientation!")
930
      end
931
      w.page_begin("<==== Computing Course ====>")
932
      w.writeLn("")
933
      --w.writeLn("We need to move "..shipdeltafront.." block(s) forward ")
934
      --w.writeLn(shipdeltaup.." block(s) upward and "..shipdeltaright.." block(s) to the right.")
935
      local minforward = ship_len_front+ship_len_back
936
      local minup = ship_len_up+ship_len_down
937
      local minright = ship_len_right+ship_len_left
938
      ship.command("MANUAL",false)
939
      local success, maxdist = ship.getMaxJumpDistance()
940
      if not success then w.status_showWarning("unable to get the ships max jump distance: "..maxdist) end
941
      if maxdist <= 0 then w.status_showWarning("max jump distance is zero") end
942
      --w.writeLn("Max jump length = "..maxdist)
943
      function computeMove(mindist,remaining,ignoreconstraint) -- returns a move to make along that axis
944
        if remaining == 0 then return 0 end
945
 
946
        local remsign = (remaining < 0) and -1 or 1
947
 
948
        if math.abs(remaining) < mindist and not ignoreconstraint then -- if the move would be impossible because it is too short, move further away
949
          return -remsign * mindist
950
        end
951
        return remsign * math.min(math.abs(remaining),maxdist)
952
      end
953
 
954
      repeat
955
        local move = {}
956
        move[2] = computeMove(minup+1,shipdeltaup,true) --y
957
        shipdeltaup = shipdeltaup - move[2]
958
 
959
        move[1] = computeMove(minforward+1,shipdeltafront,math.abs(move[2]) > minup) --x
960
 
961
        if not (math.abs(move[2]) > minup) and  shipdeltafront == 0 and shipdeltaright == 0 and shipdeltaup ~= 0 and move[1] == 0 then
962
          move[1] = minforward+1
963
        end
964
 
965
        shipdeltafront = shipdeltafront - move[1]
966
        move[3] = computeMove(minright+1,shipdeltaright, math.abs(move[2]) > minup or math.abs(move[1]) > minforward) --z
967
        shipdeltaright = shipdeltaright - move[3]
968
        navdata[#navdata+1] = move
969
        w.setCursorPos(1, 3)
970
        w.write("Computed move: #"..#navdata)
971
        w.setCursorPos(1, 4)
972
        if string.match(move[1], "-") then
973
          w.write(move[1].." block(s) back                     ")
974
        else
975
          w.write(move[1].." block(s) forward                     ")
976
        end
977
        w.setCursorPos(1, 5)
978
        if string.match(move[2], "-") then
979
          w.write(move[2] .." block(s) down                       ")
980
        else
981
          w.write(move[2] .." block(s) up                       ")
982
        end
983
        w.setCursorPos(1, 6)
984
        if string.match(move[3], "-") then
985
          w.write(move[3].." block(s) left                    ")
986
        else
987
          w.write(move[3].." block(s) right                    ")
988
        end
989
        w.setCursorPos(1, 7)
990
        w.write("Remaining: "..shipdeltafront..":"..shipdeltaup..":"..shipdeltaright.."                                       ")
991
        sleep(1)
992
      until shipdeltafront == 0 and shipdeltaup == 0 and shipdeltaright == 0
993
      w.writeLn("")
994
      w.writeLn("")
995
      w.writeLn("Navigational path plotted using "..#navdata.." jump(s).")
996
      if w.input_readConfirmation("Engage Auto Pilot? (Y/n)") then
997
        for i=1,#navdata do
998
          local move = navdata[i]
999
 
1000
          w.status_showSuccess("Executing Jump. There are "..#navdata.." Jump(s) left.")
1001
          http.post(uri,"{\"content\":\"```yaml\\nAutopilot: Executing next node There are "..#navdata.." node(s) left to execute.```\"}",{['content-type']="application/json"})
1002
          table.remove(navdata,1)
1003
          if fs.exists(navbackup) then
1004
            fs.delete(navbackup)
1005
          end
1006
          if #navdata > 0 then
1007
            local text = textutils.serialize(navdata)
1008
            local h = fs.open(navdatafile, "w")
1009
            h.write(text)
1010
            h.close()
1011
            fs.copy(navdatafile, navbackup)
1012
          else
1013
            fs.delete(navdatafile)
1014
          end
1015
          ship.command("MANUAL", false)
1016
          ship.movement(move[1],move[2],move[3])
1017
          ship.rotationSteps(0)
1018
          ship.command("MANUAL", true)
1019
 
1020
          for i=60,0,-1 do
1021
            w.status_showSuccess("Waiting for the ship to jump..."..i.."   ")
1022
            sleep(1)
1023
          end
1024
          number = #navdata + 1
1025
          http.post(uri,"{\"content\":\"```yaml\\nAutoPilot: Failed Executing node "..number..".```\"}",{['content-type']="application/json"})
1026
          fs.delete(navdatafile)
1027
          fs.copy(navbackup, navdatafile)
1028
          w.status_showWarning("The ship did not jump.")
1029
          if failping >= 0 then
1030
            http.post(uri,"{\"content\":\""..name.."\"}",{['content-type']="application/json"})
1031
          end
1032
          sleep(3)
1033
          os.reboot()
1034
        end
1035
      end
1036
      targetAutopilot = ""
1037
      ship_page_navigation()
1038
    end
1039
  end
1040
end
1041
1042
function ship_page_controls()
1043
local minValues, maxValues = getCoreBoundaries()
1044
  w.page_begin("Core(s) Detected (" .. ConnectedCores .. ") " .. w.data_getName() .. " - Ship controls")
1045
  if ship == nil or ship.isInterfaced() == nil then
1046
    w.status_showWarning("No ship controller detected")
1047
  else
1048
    local isValid, message = ship.getAssemblyStatus()
1049
    if isValid ~= true then
1050
      w.status_showWarning(message)
1051
    else
1052
      local isEnabled = ship.enable()
1053
      if not isEnabled then
1054
        ship.command("MANUAL", false)
1055
        ship_updateMovementStats()
1056
      end
1057
    end
1058
    
1059
    w.setCursorPos(1, 2)
1060
    w.writeLn("Ship:")
1061
    w.writeLn(" Current position = " .. w.format_integer(ship_x) .. ", " .. w.format_integer(ship_y) .. ", " .. w.format_integer(ship_z))
1062
    local energyStored, energyMax, energyUnits = ship.getEnergyStatus()
1063
    if energyStored == nil then energyStored = 0 end
1064
    if energyMax == nil or energyMax == 0 then energyMax = 1 end
1065
    w.writeLn(" Energy           = " .. math.floor(energyStored / energyMax * 100) .. " % (" .. w.format_integer(energyStored) .. " " .. energyUnits .. ")")
1066
    
1067
    w.writeLn("")
1068
    w.writeLn("Dimensions:")
1069
    w.writeLn(" Front, Right, Up = " .. w.format_integer(ship_front) .. ", " .. w.format_integer(ship_right) .. ", " .. w.format_integer(maxValues[2]) .. " blocks")
1070
    w.writeLn(" Back, Left, Down = " .. w.format_integer(ship_back) .. ", " .. w.format_integer(ship_left) .. ", " .. w.format_integer(minValues[2]) .. " blocks")
1071
    local shipMass, shipVolume = ship.getShipSize()
1072
    if shipMass == nil then
1073
      shipMass = 0
1074
      shipVolume = 0
1075
    end
1076
    w.write(" Mass, Volume     = ")
1077
    if shipMass == 0 then
1078
      w.write("?")
1079
    else
1080
      w.write(w.format_integer(shipMass))
1081
    end
1082
    w.write(" tons, ")
1083
    if shipVolume == 0 then
1084
      w.write("?")
1085
    else
1086
      w.write(w.format_integer(shipVolume))
1087
    end
1088
    w.writeLn(" blocks")
1089
    
1090
    if isValid == true then
1091
      w.writeLn("")
1092
      w.writeLn("Warp data:")
1093
      ship_writeMovement(" Movement         = ")
1094
      w.writeLn(" Distance         = " .. w.format_integer(ship_actualDistance) .. " m (" .. w.format_integer(ship_energyRequired) .. " " .. energyUnits .. ", " .. math.floor(energyStored / ship_energyRequired) .. " jumps)")
1095
      w.writeLn(" Target position  = " .. w.format_integer(ship_xTarget) .. ", " .. w.format_integer(ship_yTarget) .. ", " .. w.format_integer(ship_zTarget))
1096
    end
1097
  end
1098
  
1099
  w.setCursorPos(1, 16)
1100
  w.setColorControl()
1101
  w.writeFullLine(" set ship Name (N), Recall Pos (R), Movement (M/P)")
1102
  if ship_isInHyper then
1103
    w.writeFullLine(" Jump to move ship (J), exit Hyperspace (H)")
1104
  else
1105
    w.writeFullLine(" Jump to move ship (J), enter Hyperspace (H)")
1106
  end
1107
end
1108
1109
function ship_key_controls(character, keycode)
1110
  if character == 'm' or character == 'M' then
1111
    ship_page_setMovement(false)
1112
    ship_page_setRotation()
1113
    ship_warp()
1114
    return true
1115
  elseif character == 'p' or character == 'P' then
1116
    ship_page_setMovement(true)
1117
    ship_page_setRotation()
1118
    ship_warp()
1119
    return true
1120
  elseif character == 'i' or character == 'I' then
1121
    --ship_page_setDimensions()
1122
	w.input_readConfirmation("This Function is not supported")
1123
    return true
1124
elseif character == 'r' or character == 'R' then
1125
	processRecall()
1126
1127
    return true
1128
1129
  elseif character == 'j' or character == 'J' then
1130
    ship_warp()
1131
    return true
1132
  elseif character == 'h' or character == 'H' then
1133
    -- rs.setOutput(alarm_side, true)
1134
    local isConfirmed
1135
    if ship_isInHyper then
1136
      isConfirmed = w.input_readConfirmation("Disengage hyperdrive? (Y/n)")
1137
    else
1138
      isConfirmed = w.input_readConfirmation("Engage hyperdrive? (Y/n)")
1139
    end
1140
    if isConfirmed then
1141
	-- rs.setOutput(alarm_side, false)
1142
  
1143
	-- Use callAllCoresWithDelay to set HYPERDRIVE mode for all cores
1144
	callAllCoresWithDelay("command", "HYPERDRIVE", true)
1145
1146
	ship_updateMovementStats()
1147
	-- ship = nil
1148
	end
1149
    -- rs.setOutput(alarm_side, false)
1150
    return true
1151
  elseif character == 'n' or character == 'N' then
1152
    w.data_setName()
1153
    return true
1154
  end
1155
  return false
1156
end
1157
1158
function callAllCoresWithDelay(method, ...)
1159
  local args = {...}
1160
  local results = {}
1161
  local delayedCoreResult = nil
1162
1163
  local function callWrapper(core)
1164
    if core.wrapper[method] then
1165
      local result = { pcall(core.wrapper[method], table.unpack(args)) }
1166
      local success = table.remove(result, 1)
1167
      if success then
1168
        if #result > 0 then
1169
          table.insert(results, result)
1170
        end
1171
      else
1172
        print("Error calling method " .. method .. " on core " .. core.address)
1173
      end
1174
    end
1175
  end
1176
1177
  for _, core in ipairs(cores) do
1178
    if core.wrapper[method] then
1179
      if delayedCore and core.address == delayedCore.address then
1180
        -- Process delayed core result later
1181
        delayedCoreResult = function() callWrapper(core) end
1182
      else
1183
        -- Process non-delayed core
1184
        callWrapper(core)
1185
      end
1186
    end
1187
  end
1188
1189
  if delayedCoreResult then
1190
    os.sleep(10) -- 10 second delay
1191
    delayedCoreResult() -- process delayed core result
1192
  end
1193
1194
  if #results > 0 then
1195
    return table.unpack(results[1])
1196
  end
1197
end
1198
1199
function ship_page_coreList()
1200
loadDelayedCore()
1201
  w.page_begin(w.data_getName() .. " - Core List")
1202
1203
  w.writeLn("Connected Cores:")
1204
  for index, core in ipairs(cores) do
1205
    local coreName = core.wrapper.name() or "Unnamed"
1206
1207
    if index == ship_indexCore + 1 then
1208
      w.setColorSelected() -- Change color for selected core
1209
      w.write(">") -- Add the indicator for the selected core
1210
    else
1211
      w.write(" ")
1212
    end
1213
	w.write(index .. ". " .. core.address .. " (" .. coreName .. ")")
1214
1215
    -- Indicate the delayed core
1216
    if delayedCore and core.address == delayedCore.address then
1217
      w.setColorWarning()
1218
      w.write(" (Delayed)")
1219
      w.setColorNormal()
1220
    end
1221
        
1222
    w.writeLn("")
1223
  end
1224
1225
  w.setColorNormal()
1226
1227
  w.setCursorPos(1, 16)
1228
  w.setColorControl()
1229
  w.writeFullLine(" Select core (Up, Down)   Set delay for core (D)")
1230
  --w.writeFullLine(" Set delay for core (D)")
1231
end
1232
1233
function ship_key_coreList(character, keycode)
1234
  if keycode == 200 or keycode == 203 or character == '-' then -- Up or Left or -
1235
    if ship_indexCore == 0 then 
1236
      ship_indexCore = #cores - 1 
1237
    else
1238
      ship_indexCore = ship_indexCore - 1
1239
    end
1240
    return true
1241
  elseif keycode == 208 or keycode == 205 or character == '+' then -- Down or Right or +
1242
    if ship_indexCore == #cores - 1 then 
1243
      ship_indexCore = 0 
1244
    else
1245
      ship_indexCore = ship_indexCore + 1
1246
    end
1247
      elseif character == 'd' or character == 'D' then
1248
        delayedCore = cores[ship_indexCore + 1]
1249
    saveDelayedCore() -- Save the delayed core to a file
1250
    ship_page_coreList() -- Redraw the Ship Core Page
1251
    local coreName = delayedCore.wrapper.name() or "Unnamed"
1252
    w.status_showSuccess("Added 10-second delay to core " .. coreName)
1253
    return true
1254
  end
1255
  return false
1256
end
1257
1258
function ship_writeArray(arrayValues, indexSelected)
1259
  if indexSelected then
1260
    indexSelected = (indexSelected + #arrayValues) % #arrayValues
1261
  end
1262
  
1263
  local indexSplit = math.ceil(#arrayValues / 2)
1264
  for i = 1, indexSplit do
1265
    if indexSelected and i == indexSelected + 1 then
1266
      w.setColorSelected()
1267
      w.write(">" .. string.sub(arrayValues[i] .. "                        ", 1, 24))
1268
      w.setColorNormal()
1269
    else
1270
      w.write(" " .. string.sub(arrayValues[i] .. "                        ", 1, 24))
1271
    end
1272
    if arrayValues[i + indexSplit] ~= nil then
1273
      if indexSelected and i + indexSplit == indexSelected + 1 then
1274
        w.setColorSelected()
1275
        w.writeLn(">" .. string.sub(arrayValues[i + indexSplit] .. "                        ", 1, 24))
1276
        w.setColorNormal()
1277
      else
1278
        w.writeLn(" " .. arrayValues[i + indexSplit])
1279
      end
1280
    else
1281
      w.writeLn("")
1282
    end
1283
  end
1284
  return indexSelected
1285
end
1286
1287
function ship_page_navigation()
1288
  --w.page_begin(w.data_getName() .. " - Ship navigation //NON-FUNCTIONAL\\")
1289
  w.page_begin("Ship navigation //NON-FUNCTIONAL\\")
1290
  if ship == nil or ship.isInterfaced() == nil then
1291
    w.status_showWarning("No ship controller detected")
1292
  else
1293
    local isValid, message = ship.getAssemblyStatus()
1294
    if isValid ~= true then
1295
      w.status_showWarning(message)
1296
    else
1297
      w.writeLn("Ship:")
1298
      w.writeLn(" Current position = " .. w.format_integer(ship_x) .. ", " .. w.format_integer(ship_y) .. ", " .. w.format_integer(ship_z))
1299
      w.writeLn("Destination:")
1300
      local stringTarget, _ = targetAutopilot, nil -- ship.getTargets()
1301
      if stringTarget == nil or stringTarget == "" then
1302
        stringTarget = "~ None ~"
1303
      end
1304
      local arrayTarget = w.data_splitString(stringTarget, ",")
1305
      ship_indexTarget = ship_writeArray(arrayTarget, ship_indexTarget)
1306
    end
1307
  end
1308
 
1309
  w.setCursorPos(1, 16)
1310
  w.setColorControl()
1311
  w.writeFullLine(" set Destination (Enter)")
1312
  --w.writeFullLine(" engage Auto Pilot (G)")
1313
  w.writeFullLine("")
1314
end
1315
 
1316
function ship_key_navigation(character, keycode)
1317
  if keycode == 28 then -- Enter
1318
    ship_page_autopilot()
1319
    return true
1320
1321
  elseif character == 'g' or character == 'G' then -- G
1322
    --ship_page_jumpToGate()
1323
    return true
1324
  elseif keycode == 200 or keycode == 203 or character == '-' then -- Up or Left or -
1325
    ship_indexTarget = ship_indexTarget - 1
1326
    return true
1327
  elseif keycode == 208 or keycode == 205 or character == '+' then -- Down or Right or +
1328
    ship_indexTarget = ship_indexTarget + 1
1329
    return true
1330
  end
1331
  return false
1332
end
1333
1334
function ship_page_advanced()
1335
  w.page_begin(w.data_getName() .. " - Advanced")
1336
  
1337
  w.setCursorPos(1, 5)
1338
  local status, isEnabled, state, energy = ship.state()
1339
	if state == nil then state = "-Undetected-" end
1340
  w.writeLn("Ship is in " .. state .. " mode")
1341
  
1342
  w.setCursorPos(1, 16)
1343
  w.setColorControl()
1344
  w.writeFullLine("OFFLINE/disabled mode |O|I| IDLE/online mode")
1345
  w.writeFullLine("     MAINTENANCE mode |M|R| Reset core info")
1346
end
1347
1348
function ship_key_advanced(character, keycode)
1349
  if character == 'o' or character == 'O' then -- O
1350
    ship.command("OFFLINE", false)
1351
    ship.command("OFFLINE", true)
1352
    ship.enable(false)
1353
    return true
1354
  elseif character == 'm' or character == 'M' then -- M
1355
    ship.command("MAINTENANCE", false)
1356
    ship.enable(true)
1357
    ship.command("MAINTENANCE", true)
1358
    return true
1359
  elseif character == 'i' or character == 'I' then -- I
1360
    ship.command("IDLE", false)
1361
    ship.enable(true)
1362
    ship.command("IDLE", true)
1363
  elseif character == 'r' or character == 'R' then -- R
1364
    if w.input_readConfirmation("Are you sure? This cannot be undone. (Y/N)") then
1365
    shell.run("delete Relpositions.dat")
1366
	 w.status_showWarning("The Computer will now reboot...")
1367
	sleep(2) os.reboot()
1368
	end
1369
    return true
1370
  end
1371
  return false
1372
end
1373
1374
----------- connections status
1375
1376
function connections_page(isBooting)
1377
  w.page_begin(w.data_getName() .. " - Connections")
1378
  
1379
  w.writeLn("")
1380
  
1381
  local monitors = w.device_getMonitors()
1382
  if #monitors == 0 then
1383
    w.setColorDisabled()
1384
    w.writeLn("No Monitor detected")
1385
  elseif #monitors == 1 then
1386
    w.setColorSuccess()
1387
    w.writeLn("1 monitor detected")
1388
  else
1389
    w.setColorSuccess()
1390
    w.writeLn(#monitors .. " Monitors detected")
1391
  end
1392
  
1393
  if ship == nil or ship.isInterfaced() == nil then
1394
    w.setColorDisabled()
1395
    w.writeLn("No ship controller detected")
1396
  else
1397
    w.setColorSuccess()
1398
    w.writeLn("Ship controller detected")
1399
    if isBooting then
1400
      ship_boot()
1401
    end
1402
  end
1403
  
1404
  w.writeLn("")
1405
  w.setColorNormal()
1406
  w.writeLn("This is a keyboard controlled user interface.")
1407
  w.write("Key controls are written like so: ")
1408
  w.setColorControl()
1409
  w.write("Action (key)")
1410
  w.setColorNormal()
1411
  w.writeLn(".")
1412
  w.write("For example, typing ")
1413
  w.setColorControl()
1414
  w.write(" 1 ")
1415
  w.setColorNormal()
1416
  w.writeLn(" will open Ship controls.")
1417
end
1418
1419
 function cupdate()
1420
	local CID = 0
1421
	for _, core in ipairs(cores) do
1422
	CID = CID + 1
1423
    local CorePosx, CorePosy, CorePosz = core.wrapper.getLocalPosition()
1424
    table.insert(corePositions, {Core = CID, x = CorePosx, y = CorePosy, z = CorePosz})
1425
	core.wrapper.command("MANUAL",false)
1426
	local CoreVecx, CoreVecy, CoreVecz = core.wrapper.movement()
1427
	table.insert(coreVectors, {Core = CID, x = CoreVecx, y = CoreVecy, z = CoreVecz})
1428
	end
1429
	coreupdate(corePositions, coreVectors)
1430
 end
1431
1432
1433
1434
-- Function to wait for a key press
1435
local function waitForKeyPress()
1436
    print("Press any key to continue...")
1437
    
1438
    while true do
1439
	sleep(0.1) -- dont time out
1440
        local event, key = os.pullEvent("key")
1441
        if event == "key" then
1442
            return
1443
        end
1444
    end
1445
end
1446
1447
1448
----------- Boot sequence
1449
1450
	-- Function to simulate typing out message
1451
	 local function typeOut(text, delay, color)
1452
	     term.setTextColor(color or colors.white)  -- Set the text color (default is white if no color is provided)
1453
	     for i = 1, #text do
1454
	         write(text:sub(i, i))  -- Write one character at a time
1455
	         sleep(delay)
1456
	     end
1457
	 end
1458
1459
	 -- First-time setup check
1460
	if fs.exists("Relpositions.dat") then
1461
		term.setCursorBlink(true)
1462
		term.setCursorPos(0,0)
1463
		checkCorePos()
1464
1465
		-- Print all errors, if any
1466
		if #errors > 0 then
1467
			print("Errors found:\n")
1468
			for _, errorMsg in ipairs(errors) do
1469
				print(errorMsg)
1470
			end
1471
        
1472
			typeOut("\n\nPress 'r' to reboot...\n", 0.05, colors.yellow)
1473
			typeOut("Press 'f' to attempt recall...\n",0.05,colors.orange)
1474
			typeOut("Press 'm' to reset to default settings\n (This erases core info)", 0.05, colors.red)
1475
1476
			--handle keypresses
1477
			local function handleKeyPress(character)
1478
				if character == 'r' or character == 'R' then -- R
1479
					os.reboot()
1480
				elseif character == 'f' or character == 'F' then -- F
1481
					processRecall()
1482
					w.status_showWarning("Attempting to recall core(s)...")
1483
					sleep(2.5)
1484
					
1485
					--count down to reboot
1486
					local i = 10
1487
					while i do
1488
						w.status_showWarning("Rebooting in ("..i..")")
1489
						i = i - 1
1490
						sleep(1)
1491
						
1492
						if i <= 0 then os.reboot()
1493
						end
1494
					end
1495
					
1496
				elseif character == 'm' or character == 'M' then -- M
1497
					if w.input_readConfirmation("Are you sure? This cannot be undone. (Y/N)") then
1498
						shell.run("delete Relpositions.dat")
1499
						w.status_showWarning("The Computer will now reboot...")
1500
						sleep(2)
1501
						os.reboot()
1502
					end
1503
				end
1504
			end
1505
		
1506
			-- Wait for keypresses indefinitely
1507
			while true do
1508
				local event, character = os.pullEvent("char")  -- Wait for a character input event
1509
				handleKeyPress(character)
1510
				sleep(0.1)
1511
			end
1512
		end
1513
	else
1514
	    checkCorePos()
1515
     
1516
	     -- Setup initial information display
1517
	     term.clear()
1518
	     term.setCursorPos(1, 1)
1519
	     term.setCursorBlink(true)
1520
1521
	     -- Displaying the setup instructions
1522
	     typeOut("Initializing first time setup...\n\n", 0.05, colors.cyan)
1523
	     typeOut("Once complete, you can't move any ship cores\n", 0.05, colors.red)
1524
	     typeOut("Nor can you move the anchor block (Siren)\n\n", 0.05, colors.red)
1525
	     typeOut("If these blocks are moved OR have their modems     broken for any reason, you MUST reset the              First Time Configuration (FTC) \n\nFailure to follow this warning MAY lead to         unexpected operational errors\n\n", 0.05, colors.yellow)  -- Yellow color for the title
1526
	     typeOut("To reset the FTC, follow these simple steps:\n", 0.05, colors.blue)
1527
	     typeOut("Step 1: Open page 4 of this program\n", 0.05, colors.green)
1528
	     typeOut("Step 2: Press 'r'\n", 0.05, colors.purple)
1529
     
1530
	     term.setTextColor(colors.red)
1531
1532
	     waitForKeyPress()  -- Wait for the user to acknowledge and press any key
1533
1534
	     -- Additional confirmation
1535
	     term.clear()
1536
	     term.setCursorPos(1, 1)
1537
		 printSavedPos()
1538
	     typeOut("\nThank you!\n", 0.05, colors.green)
1539
	     typeOut("Setup is complete.\n", 0.05, colors.green)
1540
	     waitForKeyPress()  -- Wait for the user to press any key again
1541
		 os.reboot()
1542
	end
1543
1544
-------- Actually Booting...
1545
1546
w.page_setEndText(" Home (0), Ctrl (1), Cores (2), Nav (3), Advncd (4) ")
1547
w.page_register('0', connections_page, nil)
1548
w.page_register('1', ship_page_controls, ship_key_controls)
1549
--w.page_register('2', ship_page_crew, ship_key_crew)
1550
w.page_register('2', ship_page_coreList, ship_key_coreList)
1551
w.page_register('3', ship_page_navigation, ship_key_navigation)
1552
w.page_register('4', ship_page_advanced, ship_key_advanced)
1553
ship_register()
1554
1555
	w.boot()
1556
	cupdate()
1557
	checkCorePos()
1558
	processRecall()
1559
	term.setCursorBlink(false)
1560
	
1561
		
1562
	on = redstone.getAnalogInput(lever)
1563
1564
	if on > 6 then
1565
	  w.status_showWarning("AutoPilot: Disabled")
1566
	  sleep(3)
1567
	elseif on < 5 then
1568
	  if fs.exists(navdatafile) then
1569
		local h = fs.open(navdatafile, "r")
1570
		local text = h.readAll()
1571
		navdata = textutils.unserialize(text)
1572
		h.close()
1573
1574
		for _, core in ipairs(cores) do
1575
      local wrap = core.wrapper
1576
	
1577
		while true do
1578
		  local status, isEnabled, state, energy = wrap.state()
1579
		  if state ~= "COOLING" then break end
1580
		  w.status_showWarning("Waiting Ship is in " .. state .. " mode")
1581
		  sleep(5)
1582
		end
1583
		
1584
	while true do
1585
			local status, isEnabled, state, energy = wrap.state()
1586
			if state ~= "-NotDetected-" then break end
1587
			w.status_showWarning("Waiting to connect to core(s)...")
1588
			sleep(5)
1589
		end
1590
	end
1591
		--------------------------------------AUTO PILOT
1592
		for i=1,#navdata do
1593
		  local move = navdata[i]
1594
1595
		  w.status_showSuccess("Executing Jump. There are "..#navdata.." Jump(s) left.")
1596
		  http.post(uri,"{\"content\":\"```yaml\\nAutopilot: Executing next node There are "..#navdata.." node(s) left to execute.```\"}",{['content-type']="application/json"})
1597
		  table.remove(navdata,1)
1598
		  if fs.exists(navbackup) then
1599
			fs.delete(navbackup)
1600
			fs.copy(navdatafile, navbackup)
1601
		  end
1602
		  if #navdata > 0 then
1603
			local text = textutils.serialize(navdata)
1604
			local h = fs.open(navdatafile, "w")
1605
			h.write(text)
1606
			h.close()
1607
		  else
1608
			fs.delete(navdatafile)
1609
		  end
1610
		  ship.command("MANUAL", false)
1611
		  ship.movement(move[1],move[2],move[3])
1612
		  ship.rotationSteps(0)
1613
		  ship.command("MANUAL", true)
1614
		  sleep(10)
1615
		  for i=50,0,-1 do
1616
			w.status_showWarning("Waiting for the ship to jump..."..i.."   ")
1617
			sleep(1)
1618
		  end
1619
		  number = #navdata + 1
1620
		  http.post(uri,"{\"content\":\"```yaml\\nAutoPilot: Failed Executing node "..number..".```\"}",{['content-type']="application/json"})
1621
		  fs.delete(navdatafile)
1622
		  fs.copy(navbackup, navdatafile)
1623
		  w.status_showWarning("AutoPilot: Failed Executing node " .. number)
1624
		  if failping >= 0 then
1625
			http.post(uri,"{\"content\":\""..name.."\"}",{['content-type']="application/json"})
1626
		  end
1627
		  sleep(3)
1628
		  os.reboot()
1629
		end
1630
1631
		--------------------------------------AUTO PILOT
1632
	  end
1633
	end
1634
	
1635
	-- while true do
1636
		-- local status, isEnabled, state, energy = ship.state()
1637
		-- if state == "COOLING" then
1638
			-- w.status_showWarning("Waiting Ship is in " .. state .. " mode")
1639
			-- sleep(5)
1640
		-- elseif state == "-NotDetected-" then
1641
			-- w.status_showWarning("Waiting to connect to core(s)...")
1642
			-- sleep(5)
1643
		-- else
1644
			-- break
1645
		-- end
1646
	-- end
1647
	
1648
	for _, core in ipairs(cores) do
1649
      local wrap = core.wrapper
1650
	
1651
		while true do
1652
		  local status, isEnabled, state, energy = wrap.state()
1653
		  if state ~= "COOLING" then break end
1654
		  w.status_showWarning("Waiting Ship is in " .. state .. " mode")
1655
		  sleep(5)
1656
		end
1657
		
1658
	while true do
1659
			local status, isEnabled, state, energy = wrap.state()
1660
			if state ~= "-NotDetected-" then break end
1661
			w.status_showWarning("Waiting to connect to core(s)...")
1662
			sleep(5)
1663
		end
1664
	end
1665
		
1666
	local success, message = pcall(w.run)
1667
	if not success then
1668
	  print("failed with message")
1669
	  print(message)
1670
	  w.sleep(5)
1671
	  print("rebooting...")
1672
	  w.reboot()
1673
	else
1674
	  if ship ~= nil then
1675
		ship.command("OFFLINE", true)
1676
		ship.enable(false)
1677
	  end
1678
1679
	  w.close()
1680
	end
1681
	
1682