robathome icon

Untitled

robathome | PRO | 10/24/25 01:45:14 AM UTC | 0 ⭐ | 339 👁️ | Never ⏰ | []
text |

5.43 KB

|

None

|

0 👍

/

0 👎

-- startup.lua  — Hypertube catcher (CC:Tweaked + Advanced Peripherals)
-- Stack (bottom→top, adjacent to hypertube exit): Player Detector, Computer, Redstone Integrator, Piston
 -- ===== CONFIG =====
local CONFIG = {
  pistonY = 72,            -- absolute Y of the piston face level
  side = "up",             -- Redstone Integrator side wired to the piston (e.g., "up","north","east"...)
  range = 12,              -- detector radius to watch below/around the exit
  holdSec = 0.30,          -- how long to keep piston extended
  pistonExtendSec = 0.10,  -- piston actuation latency (~2 ticks ≈ 0.10 s)
  sampleDt = 0.05,         -- sampling interval (seconds). 1 MC tick = 0.05 s
  minUpVel = 0.20,         -- require at least this upward m/s before arming
  fireOncePerPass = true,  -- avoid multiple triggers until the player clears the zone
  targetPlayer = nil,      -- nil=any player; or set a specific player name
}
-- ===================
 -- Peripherals
local det = peripheral.find("playerDetector") or error("No playerDetector")
local ri  = peripheral.find("redstoneIntegrator") or error("No redstoneIntegrator")
 -- Redstone helpers (boolean API works on AP integrator)
local function piston(on) ri.setOutput(CONFIG.side, on and true or false) end
 -- Simple per-player tracker
local tracks = {}  -- name -> {hist = { {t=.., y=.., v=..} ... max 3}, armed=true/false, cool=false}
 local function clamp(n,a,b) if n<a then return a elseif n>b then return b else return n end end
 -- Quadratic arrival solve: y(t) = y0 + v0 t + 0.5 a t^2  →  find t where y(t)=Ypiston
local function arrivalTime(y0, v0, a, yTarget)
  local dy = yTarget - y0
  if math.abs(a) < 1e-5 then
    if v0 <= 0 then return nil end
    return dy / v0
  end
  -- 0.5 a t^2 + v0 t - dy = 0
  local A = 0.5 * a
  local B = v0
  local C = -dy
  local disc = B*B - 4*A*C
  if disc < 0 then return nil end
  local sqrtD = math.sqrt(disc)
  local t1 = (-B + sqrtD) / (2*A)
  local t2 = (-B - sqrtD) / (2*A)
  local t = math.huge
  if t1 and t1 > 0 then t = math.min(t, t1) end
  if t2 and t2 > 0 then t = math.min(t, t2) end
  if t == math.huge then return nil end
  return t
end
 -- Update one player's kinematics
local function updateTrack(name, yNow, tNow)
  local tr = tracks[name]
  if not tr then tr = { hist = {}, armed = true, cool = false } tracks[name] = tr end
   local h = tr.hist
  -- push sample
  local vNow = nil
  if #h >= 1 then
    local dt = math.max(1e-3, tNow - h[#h].t)
    vNow = (yNow - h[#h].y) / dt
  end
  table.insert(h, { t = tNow, y = yNow, v = vNow })
  while #h > 3 do table.remove(h, 1) end
   -- compute a from last two velocities if available
  local a = 0
  if #h >= 3 and h[#h-1].v and h[#h].v then
    local dv = h[#h].v - h[#h-1].v
    local dt = math.max(1e-3, h[#h].t - h[#h-1].t)
    a = dv / dt
  elseif #h >= 2 and h[#h].v then
    a = 0 -- fallback linear
  end
   return tr, h[#h].v or 0, a
end
 -- Fire control with simple pass-based cooldown
local function shouldArm(tr, y, v)
  if CONFIG.fireOncePerPass then
    if tr.cool then
      -- re-arm after player leaves the band around piston
      if y > CONFIG.pistonY + 2 or y < CONFIG.pistonY - 6 then
        tr.cool = false; tr.armed = true
      end
    end
    return tr.armed
  end
  return true
end
 local function markFired(tr)
  if CONFIG.fireOncePerPass then
    tr.armed = false
    tr.cool  = true
  end
end
 -- Main loop
print("Hypertube catcher armed. Watching for players...")
while true do
  local t0 = os.epoch("utc") / 1000.0
   -- enumerate players in range (prefer exact name if set)
  local names = {}
  if CONFIG.targetPlayer then
    if det.isPlayerInRange and det.isPlayerInRange(CONFIG.targetPlayer, CONFIG.range) then
      names = { CONFIG.targetPlayer }
    end
  else
    if det.getPlayersInRange then
      names = det.getPlayersInRange(CONFIG.range) or {}
    elseif det.getOnlinePlayers then
      -- fallback: filter by distance manually if only positions are available
      local online = det.getOnlinePlayers() or {}
      for _,n in ipairs(online) do
        local p = det.getPlayerPos(n); if p then
          local dx = 0 - (p.x or 0) -- unknown computer pos; range check only on Y/Z is fine for a vertical column
          local dz = 0 - (p.z or 0)
          if math.sqrt(dx*dx + dz*dz) <= CONFIG.range then table.insert(names, n) end
        end
      end
    end
  end
   for _,name in ipairs(names) do
    local pos = det.getPlayerPos and det.getPlayerPos(name)
    if pos and pos.y then
      local tr, v, a = updateTrack(name, pos.y, t0)
      -- Only consider players rising toward the piston from below
      if pos.y <= CONFIG.pistonY + 1 and v and v > CONFIG.minUpVel and shouldArm(tr, pos.y, v) then
        local tArr = arrivalTime(pos.y, v, a, CONFIG.pistonY)
        if tArr then
          local fireIn = tArr - CONFIG.pistonExtendSec
          if fireIn <= 0 then
            -- immediate fire
            piston(true)
            sleep(CONFIG.holdSec)
            piston(false)
            markFired(tr)
          else
            -- schedule within this loop tick (bounded by our sampleDt)
            local wait = clamp(fireIn, 0, CONFIG.sampleDt)
            sleep(wait)
            piston(true)
            sleep(CONFIG.holdSec)
            piston(false)
            markFired(tr)
          end
        end
      end
    end
  end
   sleep(CONFIG.sampleDt)
end
 

Comments