Difference between revisions of "User:SwissalpS"

From Pandorabox
Jump to navigation Jump to search
(Created page with "SwissalpS started as SwissaplS. It was a typo when initially just checking out Pandorabox. The test went much longer than expected.<br> SwissalpS occasiona...")
 
(add telemosaic auto updater)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
SwissalpS started as [[User:SwissaplS|SwissaplS]]. It was a typo when initially just checking out Pandorabox. The test went much longer than expected.<br>
 
SwissalpS started as [[User:SwissaplS|SwissaplS]]. It was a typo when initially just checking out Pandorabox. The test went much longer than expected.<br>
SwissalpS occasionally logs on as SwissaplS for tests and to appreciate more how far he has come. Sometimes also just to see who notices the difference at all.
+
SwissalpS occasionally logs on as SwissaplS for tests and to appreciate more how far he has come.<br>
 +
When SwissalpS is logged in as SwissaplS he might be AFK or not reading the in-game chat, so best use /mail if it is something 'important'.
 +
 
 +
= Models =
 +
== Chocolate Stag ==
 +
Dimensions: 39x 108y 71z
 +
 
 +
6'302 chocolate blocks used
 +
 
 +
Located in Fruitland
 +
[[File:Screenshot_20201206_005406_stag_night.png|200px]]
 +
[[File:Screenshot 20201206 003715 stag sunrise.png|200px]]
 +
 
 +
= Bag Contents =
 +
As of 20201124 SwissalpS carries these items in his bags:
 +
* 3 different coloured beacons
 +
* Protectors
 +
* Protector Placement Tool (hardly used but great to have)
 +
* WiFi Chests
 +
* Travelnets
 +
* Travelnet Elevators
 +
* Stackwise Injectors
 +
* Mob Net
 +
* Hammer
 +
* 2 Multitools
 +
* Node Inspection Tool
 +
* Mapping Kit
 +
* Binoculars
 +
* Teleport Tubes
 +
* 450m Ropes
 +
* Circular Saws
 +
* Anvils
 +
* Postool
 +
* Tube Tool
 +
* 2 Replacers
 +
* Sonic Screwdriver and at times also a normal Screwdriver
 +
* Autocrafters
 +
 
 +
* Space Suit Parts
 +
* Hazmat Suit
 +
* Water Can
 +
* Black Bones
 +
* Wooden Signs
 +
* Item Frames
 +
* Slingshot (is in bags so it might get some use)
 +
* Xenomorph
 +
* Horse
 +
* Saddle
 +
* Lasso
 +
* Mission Blocks
 +
* Mission Wands
 +
* Reserve Food (High in Nutrition)
 +
* Mese Blocks
 +
* Mese Crystals
 +
* Mese Shards
 +
* Mese Arrows
 +
* Diamond Crossbow
 +
* Diamond Blocks
 +
* Books
 +
*
 +
* One Way Tubes
 +
* Sorting Tubes
 +
* Pneumatic Tubes
 +
* Sand Tubes
 +
* Accelerating Tubes
 +
* NICs
 +
* Mese Tubes
 +
* Priority Tubes
 +
* Digiline Detector Tubes
 +
* Digiline Tubes
 +
* Adjustable Tubes
 +
* Digiline Wires
 +
* Mesecon Wires
 +
* Copper Chests
 +
* Fancy Vendors
 +
* Mithril Chests
 +
* Vertical Digiline Intermediate (sometimes other wires)
 +
* Digtron Axle
 +
* MV Cables
 +
* HV Cables
 +
* Global Memory (as long as supply lasts)
 +
* I/O Expanders
 +
* Lua Controllers
 +
* Digiline Injectors
 +
 
 +
* Self Contained Injectors
 +
* Node Breakers
 +
* Deployers
 +
* Mesecon Pistons
 +
* Mesecon Sticky Pistons
 +
* Constructor Mk1
 +
* Constructor Mk2/3
 +
* Buttons
 +
* Jump Drives
 +
* Lua Tool
 +
* Prospector (hardly gets used)
 +
* Spare Boots
 +
* Spare Glider
 +
* Mob Protectors
 +
* Mob Tags
 +
* Posters
 +
* Switches (mostly protected)
 +
* Compass(es)
 +
* Some kind of dig-able lights, currently Jack-O-Lanterns
 +
 
 +
= Inventory Contents =
 +
* Slot 1: Glider
 +
* Slot 2: Multitool
 +
* Slot 3: Replacer
 +
* Slot 8: Food (canned, marinated or pickled)
 +
* Slot 32: Empty Glass Bottles
 +
 
 +
= Armour Inventory =
 +
Mostly only lava boots. Sometimes also a lava shield but not so much since beacons got healing effect.
 +
When AFK in mines, when too lazy to use multi-tool for defence or when working around chernobilyte: Lava boots, diamond leggings and shirt, hazmat and lava shield.
 +
SwissalpS does not like diamond helmet, the sound it makes on damage is irritating.
 +
 
 +
= Client Side Mods =
 +
* VelocityHUD https://github.com/SwissalpS/CSMvelocityHUD Shows your speed when not attached to a digtron or similar.
 +
* silenceBeacon https://github.com/SwissalpS/CSMsilenceBeacon Supresses the beacon messages when they are placed in a tight spot.
 +
* Too Much Information (TMI) https://github.com/SwissalpS/CSMtooMuchInformationHUD Choose which info you want to have shown. Most useful: wielded item. Open formspec with .tmi command.
 +
* Breadcrumbs https://github.com/SwissalpS/CSMbreadcrumbs Adds a trail of HUD markers so you can see where you came from. Open config with .bread command.
 +
 
 +
= Server Side Mods =
 +
* postool https://github.com/SwissalpS/postool Shows HUD with positional data and provides a tool to show map-block border. Replaced the old poshud mod.
 +
* replacer https://github.com/SwissalpS/replacer Cool creative tool originally by Sokomine. SwissalpS merged features from HybridDog and Coil0 and has maintained the version used on Pandorabox.io since.
 +
* SwissalpS has contributed to various other mods used on Pandorabox.io
 +
 
 +
= In-Game Code =
 +
== Self Updating Telemosaic ==
 +
Requires mooncontroller. Solution to update telemosaics on a ship where each telemosaic targets another one and none is targeted by multiple others (making it impossible for [jumpdrive] to update them.
 +
<syntaxhighlight lang="lua">
 +
-- Title: Telemosaic updater
 +
-- Author: SwissalpS
 +
-- Version: 20260424_2332
 +
-- Description: For use in a mooncontroller using
 +
--        its pos global to detect when the telemosaic has moved and
 +
--        update target of telemosaic.
 +
--        This allows using non-bidirectional telemosaics in jd-fleets.
 +
--------------
 +
 
 +
local sC = 'telemosaic'
 +
 
 +
local p = print
 +
local mem = mem
 +
local d = digiline_send
 +
 
 +
local function checkCoords()
 +
  if not (mem.pos
 +
    and mem.pos.x == pos.x
 +
    and mem.pos.y == pos.y
 +
    and mem.pos.z == pos.z)
 +
  then
 +
    d(sC, 'get')
 +
  end
 +
end -- checkCoords
 +
 
 +
local sET = event.type
 +
if 'interrupt' == sET then
 +
 
 +
  interrupt(120)
 +
  checkCoords()
 +
 
 +
elseif 'digiline' == sET then
 +
 
 +
  if sC ~= event.channel then return end
 +
 
 +
  local tM = event.msg
 +
  if tM.player then return end
 +
 
 +
  if not mem.pos then
 +
    local tDelta = {
 +
      x = tM.target.x - pos.x,
 +
      y = tM.target.y - pos.y,
 +
      z = tM.target.z - pos.z,
 +
    }
 +
    mem.delta = tDelta
 +
    mem.pos = pos
 +
    return
 +
  end
 +
 
 +
  d(sC, { command = 'setdest', pos = {
 +
    x = pos.x + mem.delta.x,
 +
    y = pos.y + mem.delta.y,
 +
    z = pos.z + mem.delta.z,
 +
  } })
 +
 
 +
  mem.pos = pos
 +
  -- Report to other systems for example to restart quarries or activate beacons.
 +
  d('b', true)
 +
 
 +
elseif 'off' == sET then
 +
 
 +
  checkCoords()
 +
 
 +
elseif 'program' == sET then
 +
 
 +
  interrupt(1)
 +
 
 +
end
 +
</syntaxhighlight>
 +
 
 +
== Experiment Digibuilder & Quarry Farm I ==
 +
This setup works on multiple layers and digs single crops when ripe.
 +
WIP, not sure I will ever bother to refine it.
 +
Can be visited by going to SwissalpS' "Hub Zero" -> "Quarries" -> "misc. Quarries" -> "92 FH46 Sphere" -> It is below you, you are on the level where luaCs are.
 +
Main luaC (digibuilder):
 +
<syntaxhighlight lang="lua">
 +
-- Title: Digibuilder Gardener
 +
-- Author: SwissalpS
 +
-- Version: 20260322_1004
 +
-- Description: Uses a digibuilder to inspect crops and plant if needed.
 +
--              When a ripe crop is detected the coordinates are sent via
 +
--              digilines to (an)other luacontroller(s) that will deal with
 +
--              harvesting.
 +
--------------
 +
--do mem = {} return end
 +
 
 +
local sCbutt = 'b'
 +
local sCbuilder = 'r'
 +
local sCdig = 'c'
 +
local sCp = 'l'
 +
 
 +
local iBeat = 5
 +
 
 +
local iFirstX = -10
 +
local iFirstZ = -10
 +
local iFirstY = -15
 +
 
 +
local iLastX = 10
 +
local iLastZ = 10
 +
local iLastY = -6
 +
 
 +
local lSeeds = {
 +
  'farming:chili_pepper', --'farming:cucumber',
 +
  'farming:onion', 'farming:blueberries',
 +
}
 +
 
 +
local d = digiline_send
 +
local p = sCp and function(m) d(sCp, tostring(m)) end or pos and print
 +
  or function(m)
 +
    if not mem.p then mem.p = {} end
 +
    local i = #mem.p
 +
    if 44 > i then
 +
      i = i + 1
 +
    else
 +
      table.remove(mem.p, 1)
 +
    end
 +
    mem.p[i] = m
 +
  end
 +
 
 +
local function setMain(b)
 +
 
 +
  mem.bMain = b
 +
  d(sCbutt, 'light_o' .. (b and 'n' or 'ff'))
 +
  if b then interrupt(heat) end
 +
 
 +
end -- setMain
 +
 
 +
local function dig()
 +
 
 +
  d(sCdig, { x = mem.iX, y = mem.iY, z = mem.iZ })
 +
 
 +
end -- dig
 +
 
 +
-- helper function for isRipe()
 +
local fFind = function(sHaystack, sNeedle) return string.find(sHaystack, sNeedle, 0, true) ~= nil end
 +
 
 +
-- pass item name to check. Ex. take event.msg from
 +
-- node detector response to 'GET' message
 +
local function isRipe(s)
 +
  if 'string' ~= type(s) then return false end
 +
  local tRipe = {
 +
 
 +
    ['bakedclay:delphinium'] = 1,
 +
    ['bakedclay:lazarus'] = 1,
 +
    -- managrass will be found with 'grass' check
 +
    ['bakedclay:thistle'] = 1,
 +
    ['canned_food:canned_beetroot_plus'] = 1,
 +
    ['canned_food:canned_carrot_plus'] = 1,
 +
    ['canned_food:canned_chili_pepper_plus'] = 1,
 +
    ['canned_food:canned_cucumber_plus'] = 1,
 +
    ['canned_food:canned_garlic_cloves_plus'] = 1,
 +
    ['canned_food:canned_mushrooms_plus'] = 1,
 +
    ['canned_food:canned_onion_plus'] = 1,
 +
    ['canned_food:canned_potato_plus'] = 1,
 +
    ['canned_food:canned_tomato_plus'] = 1,
 +
    ['cherrytree:cherries'] = 1,
 +
    ['default:blueberry_bush_leaves_with_berries'] = 1,
 +
    ['default:cactus'] = 1,
 +
    -- if you want to use this, you may want to remove some others
 +
    ['default:dry_shrub'] = 1,
 +
    ['default:fern_1'] = 1,
 +
    ['default:fern_2'] = 1,
 +
    ['default:fern_3'] = 1,
 +
    ['default:papyrus'] = 1,
 +
    ['farming:artichoke_5'] = 1,
 +
    ['farming:barely_7'] = 1,
 +
    ['farming:beetroot_5'] = 1,
 +
    ['farming:blackberry_4'] = 1,
 +
    ['farming:blueberry_4'] = 1,
 +
    ['farming:cabbage_6'] = 1,
 +
    ['farming:carrot_8'] = 1,
 +
    ['farming:chili_8'] = 1,
 +
    ['farming:cocoa_4'] = 1,
 +
    ['farming:coffee_5'] = 1,
 +
    ['farming:corn_8'] = 1,
 +
    ['farming:cotton_8'] = 1,
 +
    ['farming:cucumber_4'] = 1,
 +
    ['farming:garlic_5'] = 1,
 +
    ['farming:grapes_8'] = 1,
 +
    ['farming:beanpole_5'] = 1,
 +
    ['farming:hemp_8'] = 1,
 +
    ['farming:lettuce_5'] = 1,
 +
    ['farming:melon_8'] = 1,
 +
    ['farming:mint_4'] = 1,
 +
    ['farming:oat_8'] = 1,
 +
    ['farming:onion_5'] = 1,
 +
    ['farming:parsley_3'] = 1,
 +
    ['farming:pea_5'] = 1,
 +
    -- if you want a particular colour, set the others to -1
 +
    ['farming:pepper_5'] = 1,  -- green
 +
    ['farming:pepper_6'] = 1,  -- yellow
 +
    ['farming:pepper_7'] = 1,  -- red
 +
    ['farming:pineapple_8'] = 1,
 +
    ['farming:potato_4'] = 1,
 +
    ['farming:pumpkin_8'] = 1,
 +
    ['farming:raspberry_4'] = 1,
 +
    ['farming:rhubarb_3'] = 1,
 +
    ['farming:rice_8'] = 1,
 +
    ['farming:rye_8'] = 1,
 +
    ['farming:soy_7'] = 1,
 +
    ['farming:sunflower_8'] = 1,
 +
    ['farming:tomato_8'] = 1,
 +
    ['farming:vanilla_8'] = 1,
 +
    ['farming:weed'] = 1,
 +
    ['farming:wheat_8'] = 1,
 +
    ['moretrees:coconut_3'] = 1,
 +
    ['moretrees:dates_f4'] = 1,
 +
    ['moretrees:rubber_tree_trunk'] = 1,
 +
    -- need to catch this one early or search for 'trunk' will say yes
 +
    ['moretrees:rubber_tree_trunk_empty'] = -1,
 +
    ['vines:jungle_middle'] = 1,
 +
    ['vines:root_middle'] = 1,
 +
    ['vines:side_middle'] = 1,
 +
    ['vines:vine_middle'] = 1,
 +
    ['vines:willow_middle'] = 1,
 +
    ['wine:blue_agave'] = 1,
 +
 
 +
  } -- tRipe
 +
 
 +
  -- straight up ripe?
 +
  local iV = tRipe[s]
 +
  if nil ~= iV then
 +
    -- blacklisted?
 +
    if 0 >= iV then return false end
 +
    return true
 +
  end
 +
 
 +
  -- flowers are ripe if they exists
 +
  -- this also covers mushrooms
 +
  if fFind(s, 'flowers:') then return true end
 +
 
 +
  -- bushes, not yet checked, are ripe when stem exists
 +
  if fFind(s, 'bush_stem') then return true end
 +
 
 +
  -- grass can be considered ripe too
 +
  if fFind(s, 'grass') then return true end
 +
 
 +
  -- check for trunks as these are ripe too
 +
  return fFind(s, 'trunk')
 +
 
 +
end -- isRipe
 +
 
 +
local function peek(tPos)
 +
 
 +
  mem.isPeek = true
 +
  d(sCbuilder, { command = 'getnode', pos = tPos })
 +
 
 +
end -- peek
 +
 
 +
local function poke(tPos, sN)
 +
 
 +
  mem.isPoke = true
 +
  d(sCbuilder, { command = 'setnode', pos = tPos, name = sN, down = true })
 +
 
 +
end -- poke
 +
 
 +
local function handlePeek(mEM)
 +
 
 +
  mem.isPeek = nil
 +
  local sN = mEM.name
 +
  if 'air' == sN then
 +
    poke({ x = mem.iX, y = mem.iY, z = mem.iZ },
 +
      lSeeds[math.random(1, #lSeeds)])
 +
    return
 +
  elseif isRipe(sN) then
 +
    dig()
 +
  end
 +
 
 +
  interrupt(iBeat)
 +
--p(mEM)
 +
 
 +
end -- handlePeek
 +
 
 +
local function handlePoke(mEM)
 +
 
 +
  mem.isPoke = nil
 +
  port.a = nil
 +
  port.c = nil
 +
  if mEM.error then
 +
    port.c = true
 +
    local sE = mEM.message
 +
    local sEstart = sE:sub(1, 4)
 +
    if 'Item' == sEstart then
 +
      port.a = true
 +
      p('\nNeed:' .. sE:sub(23))
 +
    end
 +
  end
 +
 
 +
  interrupt(iBeat)
 +
 
 +
end -- handlePoke
 +
 
 +
local sET = event.type
 +
if 'interrupt' == sET then
 +
 
 +
  if not mem.bMain then return end
 +
 
 +
  mem.iX = mem.iX + 1
 +
  if iLastX < mem.iX then
 +
    mem.iX = iFirstX
 +
    mem.iZ = mem.iZ + 1
 +
    p('Y: ' .. mem.iY .. ' Z: ' .. mem.iZ)
 +
    if iLastZ < mem.iZ then
 +
      mem.iZ = iFirstZ
 +
      mem.iY = mem.iY + 3
 +
      if iLastY < mem.iY then
 +
        mem.iY = iFirstY
 +
      end
 +
      p('Y: ' .. mem.iY)
 +
    end
 +
  end
 +
 
 +
  peek({ x = mem.iX, y = mem.iY, z = mem.iZ })
 +
 
 +
elseif 'digiline' == sET then
 +
 
 +
  local sEC = event.channel
 +
  if sCbutt == sEC then return setMain(not mem.bMain) end
 +
 
 +
  local mEM = event.msg
 +
  if sCbuilder == sEC then
 +
    if mem.isPeek then return handlePeek(mEM) end
 +
    if mem.isPoke then return handlePoke(mEM) end
 +
  end
 +
 +
  --p(event)
 +
 
 +
elseif 'program' == sET then
 +
 
 +
  if not mem.iX then mem.iX = iFirstX end
 +
  if not mem.iY then mem.iY = iFirstY end
 +
  if not mem.iZ then mem.iZ = iFirstZ end
 +
 
 +
  setMain(mem.bMain)
 +
 
 +
end
 +
</syntaxhighlight>
 +
Secondary luaC (quarry):
 +
<syntaxhighlight lang="lua">
 +
-- Title: Quarry Gardener
 +
-- Author: SwissalpS
 +
-- Version: 20260320_2341
 +
-- Description: Uses a quarry to harvest crops.
 +
--              It takes the coordinates from another luacontroller that
 +
--              has scanned for ripe crops.
 +
--------------
 +
 
 +
-- Channel to listen for coordinates from.
 +
local sCcom = 'c'
 +
-- Channel of quarry.
 +
local sCq = 'q'
 +
 
 +
local iBeat = 3
 +
 
 +
-- Offset to convert the received coordinates to quarry coordinates.
 +
local tD = { x = 0, y = 3, z = 0 }
 +
 
 +
--------------
 +
local d = digiline_send
 +
local p = function(m) d('l', m) end
 +
local q = function(t) d(sCq, t) end
 +
 
 +
local sET = event.type
 +
if 'interrupt' == sET then
 +
 
 +
  if mem.bDigging then
 +
    -- Check if quarry is done.
 +
    q('get')
 +
  else
 +
    -- Not digging, start next dig - if there is at least one more coordinate.
 +
    local tP = mem.lQ[1]
 +
    if tP then
 +
      mem.bDigging = true
 +
      q({ command = 'set', restart = true,
 +
        max_depth = -1 * (tP.y + tD.y + 1),
 +
        offset_x = tP.x + tD.x,
 +
        offset_y = tP.y + tD.y,
 +
        offset_z = tP.z + tD.z,
 +
      })
 +
      table.remove(mem.lQ, 1)
 +
    end
 +
    interrupt(iBeat)
 +
  end
 +
 
 +
elseif 'digiline' == sET then
 +
 
 +
  local sEC = event.channel
 +
  local mEM = event.msg
 +
  if sCq == sEC then
 +
    if mEM.finished then
 +
      mem.bDigging = nil
 +
    end
 +
    return interrupt(iBeat)
 +
  end
 +
 
 +
  if sCcom ~= sEC then return end
 +
 
 +
  -- Add coordinate to queue.
 +
  table.insert(mem.lQ, mEM)
 +
  -- Prevent overflow.
 +
  if 444 < #mem.lQ then
 +
    table.remove(mem.lQ, 1)
 +
  end
 +
 
 +
  --p(mEM)
 +
 
 +
elseif 'program' == sET then
 +
 
 +
  if not mem.lQ then mem.lQ = {} end
 +
  q({ command = 'set', radius = 0, enabled = true })
 +
  interrupt(heat)
 +
 
 +
end
 +
</syntaxhighlight>
 +
 
 +
== Experiment Digibuilder & Quarry Farm II ==
 +
This setup works on multiple layers and digs entire layers when enough crops are deemed ripe.
 +
It can safely plant on a layer that is being dug.
 +
WIP, not sure I will ever bother to refine it.
 +
Can be visited by going to SwissalpS' "Hub Zero" -> "Quarries" -> "misc. Quarries" -> "92 FH46 Sphere" -> It is above you.
 +
Main luaC (digibuilder):
 +
<syntaxhighlight lang="lua">
 +
-- Title: Digibuilder Gardener 2.0
 +
-- Author: SwissalpS
 +
-- Version: 20260324_1007
 +
-- Description: Uses a digibuilder to inspect crops and plant if needed.
 +
--              When enough ripe crops are detected on a layer, the
 +
--              y-coordinate is sent via digilines to another luacontroller
 +
--              which deals with harvesting via quarry.
 +
--------------
 +
--do mem.tToCheck = nil return end
 +
--do mem.iX = nil mem.iY = nil mem.iZ = nil return end
 +
 
 +
local sCbutt = 'b'
 +
local sCbuilder = 'r'
 +
local sCdig = 'c'
 +
local sCp = 'l'
 +
 
 +
local iBeatPeek = .1
 +
local iBeatPoke = .51
 +
 
 +
local iFirstX = -11
 +
local iFirstZ = -2
 +
local iFirstY = -15
 +
 
 +
local iLastX = 5
 +
local iLastZ = 14
 +
local iLastY = -6
 +
 
 +
local lSeeds = {
 +
  'farming:chili_pepper', --'farming:cucumber',
 +
  'farming:onion', 'farming:blueberries',
 +
}
 +
 
 +
local d = digiline_send
 +
local p = sCp and function(m) d(sCp, m) end or pos and print
 +
  or function(m)
 +
    if not mem.p then mem.p = {} end
 +
    local i = #mem.p
 +
    if 44 > i then
 +
      i = i + 1
 +
    else
 +
      table.remove(mem.p, 1)
 +
    end
 +
    mem.p[i] = m
 +
  end
 +
 
 +
local function setMain(b)
 +
 
 +
  mem.bMain = b
 +
  d(sCbutt, 'light_o' .. (b and 'n' or 'ff'))
 +
  if b then interrupt(heat) end
 +
 
 +
end -- setMain
 +
 
 +
local function dig()
 +
 
 +
  local sY = tostring(mem.iY)
 +
  if mem.tQueued[sY] then
 +
    p(sY .. ' already queued')
 +
    return
 +
  end
 +
 
 +
  mem.tQueued[sY] = true
 +
  d(sCdig, mem.iY)
 +
 
 +
end -- dig
 +
 
 +
-- helper function for isRipe()
 +
local fFind = function(sHaystack, sNeedle) return string.find(sHaystack, sNeedle, 0, true) ~= nil end
 +
 
 +
-- pass item name to check. Ex. take event.msg from
 +
-- node detector response to 'GET' message
 +
local function isRipe(s)
 +
  if 'string' ~= type(s) then return false end
 +
  local tRipe = {
 +
 
 +
    ['bakedclay:delphinium'] = 1,
 +
    ['bakedclay:lazarus'] = 1,
 +
    -- managrass will be found with 'grass' check
 +
    ['bakedclay:thistle'] = 1,
 +
    ['canned_food:canned_beetroot_plus'] = 1,
 +
    ['canned_food:canned_carrot_plus'] = 1,
 +
    ['canned_food:canned_chili_pepper_plus'] = 1,
 +
    ['canned_food:canned_cucumber_plus'] = 1,
 +
    ['canned_food:canned_garlic_cloves_plus'] = 1,
 +
    ['canned_food:canned_mushrooms_plus'] = 1,
 +
    ['canned_food:canned_onion_plus'] = 1,
 +
    ['canned_food:canned_potato_plus'] = 1,
 +
    ['canned_food:canned_tomato_plus'] = 1,
 +
    ['cherrytree:cherries'] = 1,
 +
    ['default:blueberry_bush_leaves_with_berries'] = 1,
 +
    ['default:cactus'] = 1,
 +
    -- if you want to use this, you may want to remove some others
 +
    ['default:dry_shrub'] = 1,
 +
    ['default:fern_1'] = 1,
 +
    ['default:fern_2'] = 1,
 +
    ['default:fern_3'] = 1,
 +
    ['default:papyrus'] = 1,
 +
    ['farming:artichoke_5'] = 1,
 +
    ['farming:barely_7'] = 1,
 +
    ['farming:beetroot_5'] = 1,
 +
    ['farming:blackberry_4'] = 1,
 +
    ['farming:blueberry_4'] = 1,
 +
    ['farming:cabbage_6'] = 1,
 +
    ['farming:carrot_8'] = 1,
 +
    ['farming:chili_8'] = 1,
 +
    ['farming:cocoa_4'] = 1,
 +
    ['farming:coffee_5'] = 1,
 +
    ['farming:corn_8'] = 1,
 +
    ['farming:cotton_8'] = 1,
 +
    ['farming:cucumber_4'] = 1,
 +
    ['farming:garlic_5'] = 1,
 +
    ['farming:grapes_8'] = 1,
 +
    ['farming:beanpole_5'] = 1,
 +
    ['farming:hemp_8'] = 1,
 +
    ['farming:lettuce_5'] = 1,
 +
    ['farming:melon_8'] = 1,
 +
    ['farming:mint_4'] = 1,
 +
    ['farming:oat_8'] = 1,
 +
    ['farming:onion_5'] = 1,
 +
    ['farming:parsley_3'] = 1,
 +
    ['farming:pea_5'] = 1,
 +
    -- if you want a particular colour, set the others to -1
 +
    ['farming:pepper_5'] = 1,  -- green
 +
    ['farming:pepper_6'] = 1,  -- yellow
 +
    ['farming:pepper_7'] = 1,  -- red
 +
    ['farming:pineapple_8'] = 1,
 +
    ['farming:potato_4'] = 1,
 +
    ['farming:pumpkin_8'] = 1,
 +
    ['farming:raspberry_4'] = 1,
 +
    ['farming:rhubarb_3'] = 1,
 +
    ['farming:rice_8'] = 1,
 +
    ['farming:rye_8'] = 1,
 +
    ['farming:soy_7'] = 1,
 +
    ['farming:sunflower_8'] = 1,
 +
    ['farming:tomato_8'] = 1,
 +
    ['farming:vanilla_8'] = 1,
 +
    ['farming:weed'] = 1,
 +
    ['farming:wheat_8'] = 1,
 +
    ['moretrees:coconut_3'] = 1,
 +
    ['moretrees:dates_f4'] = 1,
 +
    ['moretrees:rubber_tree_trunk'] = 1,
 +
    -- need to catch this one early or search for 'trunk' will say yes
 +
    ['moretrees:rubber_tree_trunk_empty'] = -1,
 +
    ['vines:jungle_middle'] = 1,
 +
    ['vines:root_middle'] = 1,
 +
    ['vines:side_middle'] = 1,
 +
    ['vines:vine_middle'] = 1,
 +
    ['vines:willow_middle'] = 1,
 +
    ['wine:blue_agave'] = 1,
 +
 
 +
  } -- tRipe
 +
 
 +
  -- straight up ripe?
 +
  local iV = tRipe[s]
 +
  if nil ~= iV then
 +
    -- blacklisted?
 +
    if 0 >= iV then return false end
 +
    return true
 +
  end
 +
 
 +
  -- flowers are ripe if they exists
 +
  -- this also covers mushrooms
 +
  if fFind(s, 'flowers:') then return true end
 +
 
 +
  -- bushes, not yet checked, are ripe when stem exists
 +
  if fFind(s, 'bush_stem') then return true end
 +
 
 +
  -- grass can be considered ripe too
 +
  if fFind(s, 'grass') then return true end
 +
 
 +
  -- check for trunks as these are ripe too
 +
  return fFind(s, 'trunk')
 +
 
 +
end -- isRipe
 +
 
 +
local function ignoreString()
 +
  return 'x' .. mem.iX .. 'z' .. mem.iZ --.. 'y' .. mem.iY
 +
end -- ignoreString
 +
 
 +
local function isIgnore()
 +
 
 +
  -- only level -12 has water -> only level with ignore fields
 +
  if -12 ~= mem.iY then return false end
 +
  return fFind(mem.sIgnore, ignoreString())
 +
 
 +
end -- isIgnore
 +
 
 +
local function peek(tPos)
 +
 
 +
  mem.isPeek = true
 +
  d(sCbuilder, { command = 'getnode', pos = tPos })
 +
 
 +
end -- peek
 +
 
 +
local function poke(tPos, sN)
 +
 
 +
  mem.isPoke = true
 +
  d(sCbuilder, { command = 'setnode', pos = tPos, name = sN, down = true })
 +
 
 +
end -- poke
 +
 
 +
local function checkHash() return mem.iX .. ';' .. mem.iZ end
 +
 
 +
local function addToCheck()
 +
 
 +
  local sY = tostring(mem.iY)
 +
  if not mem.tToCheck[sY] then mem.tToCheck[sY] = {} end
 +
  table.insert(mem.tToCheck[sY], { mem.iX, mem.iZ })
 +
 
 +
end -- addToCheck
 +
 
 +
local function handlePeek(mEM)
 +
 
 +
  mem.isPeek = nil
 +
  local sN = mEM.name
 +
  if 'air' == sN then
 +
    poke({ x = mem.iX, y = mem.iY, z = mem.iZ },
 +
      lSeeds[math.random(1, #lSeeds)])
 +
    return
 +
  elseif not isRipe(sN) then
 +
    addToCheck()
 +
  end
 +
 
 +
  interrupt(iBeatPeek)
 +
--p(mEM)
 +
 
 +
end -- handlePeek
 +
 
 +
local function handlePoke(mEM)
 +
 
 +
  mem.isPoke = nil
 +
  port.a = nil
 +
  port.c = nil
 +
  if mEM.error then
 +
    port.c = true
 +
    local sE = mEM.message
 +
    local sEstart = sE:sub(1, 4)
 +
    if 'Item' == sEstart then
 +
      -- "Item not in inventory: ..."
 +
      port.a = true
 +
      p('\nNeed:' .. sE:sub(23))
 +
      addToCheck()
 +
    elseif 'item' == sEstart then
 +
      -- "item placement failed: ..."
 +
      -- most probably because water under this location -> ignore
 +
      mem.sIgnore = mem.sIgnore .. ignoreString()
 +
    end
 +
  else
 +
    addToCheck()
 +
  end
 +
 
 +
  interrupt(iBeatPoke)
 +
 
 +
end -- handlePoke
 +
 
 +
local function advanceCheck()
 +
 
 +
  if mem.bLastCheck then
 +
    mem.bLastCheck = nil
 +
    mem.iX = iLastX
 +
    mem.iZ = iLastZ
 +
    return false
 +
  end
 +
 
 +
  if not mem.tCheck then return false end
 +
 
 +
  local iC = #mem.tCheck
 +
  local t = mem.tCheck[iC]
 +
  table.remove(mem.tCheck)
 +
  mem.iX = t[1]
 +
  mem.iZ = t[2]
 +
  -- Last coordinate?
 +
  if 1 == iC then
 +
    mem.tCheck = nil
 +
    mem.bLastCheck = true
 +
  end
 +
  p('X: ' .. mem.iX .. ' Z: ' .. mem.iZ)
 +
  return true
 +
 
 +
end -- advanceCheck
 +
 
 +
local function advancePos()
 +
 
 +
  if advanceCheck() then return end
 +
 
 +
  local sY, iC
 +
 
 +
  mem.iX = mem.iX + 1
 +
  if iLastX < mem.iX then
 +
    mem.iX = iFirstX
 +
    mem.iZ = mem.iZ + 1
 +
    p('Y: ' .. mem.iY .. ' Z: ' .. mem.iZ)
 +
    if iLastZ < mem.iZ then
 +
      mem.iZ = iFirstZ
 +
      sY = tostring(mem.iY)
 +
      iC = mem.tToCheck[sY] and #mem.tToCheck[sY] or -1
 +
      if 0 >= iC then
 +
        dig()
 +
      end
 +
      mem.iY = mem.iY + 3
 +
      if iLastY < mem.iY then
 +
        mem.iY = iFirstY
 +
      end
 +
 
 +
      p('Y: ' .. mem.iY)
 +
 
 +
      sY = tostring(mem.iY)
 +
      iC = mem.tToCheck[sY] and #mem.tToCheck[sY] or -1
 +
      if 0 < iC then
 +
        mem.tCheck = mem.tToCheck[sY]
 +
        mem.tToCheck[sY] = {}
 +
        advanceCheck()
 +
      end
 +
    end
 +
  end
 +
 
 +
end -- advancePos()
 +
 
 +
local sET = event.type
 +
if 'interrupt' == sET then
 +
 
 +
  if not mem.bMain then return end
 +
 
 +
  advancePos()
 +
  if isIgnore() then advancePos() end
 +
 
 +
  peek({ x = mem.iX, y = mem.iY, z = mem.iZ })
 +
 
 +
elseif 'digiline' == sET then
 +
 
 +
  local sEC = event.channel
 +
  if sCbutt == sEC then return setMain(not mem.bMain) end
 +
 
 +
  local mEM = event.msg
 +
  if sCbuilder == sEC then
 +
    if mem.isPeek then return handlePeek(mEM) end
 +
    if mem.isPoke then return handlePoke(mEM) end
 +
  elseif sCdig == sEC then
 +
    mem.tQueued[tostring(mEM)] = nil
 +
  end
 +
 +
  p(event)
 +
 
 +
elseif 'program' == sET then
 +
 
 +
  if not mem.tQueued then mem.tQueued = {} end
 +
  if not mem.tToCheck then mem.tToCheck = {} end
 +
  if not mem.sIgnore then mem.sIgnore = '' end
 +
  if not mem.iX then mem.iX = iFirstX end
 +
  if not mem.iY then mem.iY = iFirstY end
 +
  if not mem.iZ then mem.iZ = iFirstZ end
 +
 
 +
  setMain(mem.bMain)
 +
 
 +
end
 +
</syntaxhighlight>
 +
Secondary luaC (quarry):
 +
<syntaxhighlight lang="lua">
 +
-- Title: Quarry Gardener 2.0
 +
-- Author: SwissalpS
 +
-- Version: 20260323_1446
 +
-- Description: Uses a quarry to harvest crops.
 +
--              It takes the y-coordinate from another luacontroller that
 +
--              has scanned for ripe crops.
 +
--------------
 +
--do mem.lQ = nil return end
 +
--do mem.bDigging = nil return end
 +
 
 +
-- Channel to listen for coordinates from.
 +
local sCcom = 'c'
 +
-- Channel of quarry.
 +
local sCq = 'q'
 +
 
 +
local iBeat = 3
 +
 
 +
-- Offset to convert the received coordinates to quarry coordinates.
 +
local iDy = 3
 +
 
 +
--------------
 +
local d = digiline_send
 +
local p = function(m) d('l', m) end
 +
local q = function(m) d(sCq, m) end
 +
 
 +
local sET = event.type
 +
if 'interrupt' == sET then
 +
 
 +
  if mem.bDigging then
 +
    -- Check if quarry is done.
 +
    q('get')
 +
  else
 +
    -- Not digging, start next dig - if there is at least one more coordinate.
 +
    local iY = mem.lQ[1]
 +
    if nil == iY then return end
 +
 
 +
    mem.bDigging = true
 +
    q({ command = 'set', restart = true,
 +
      max_depth = -1 * (iY + iDy + 1),
 +
      offset_y = iY + iDy,
 +
    })
 +
    mem.iY = iY
 +
    table.remove(mem.lQ, 1)
 +
    interrupt(iBeat)
 +
  end
 +
 
 +
elseif 'digiline' == sET then
 +
 
 +
  local sEC = event.channel
 +
  local mEM = event.msg
 +
  if sCq == sEC then
 +
    if mEM.finished then
 +
      d(sCcom, mem.iY)
 +
      mem.iY = nil
 +
      mem.bDigging = nil
 +
    end
 +
    return interrupt(iBeat)
 +
  end
 +
 
 +
  if sCcom ~= sEC then return end
 +
 
 +
  -- Add coordinate to queue.
 +
  table.insert(mem.lQ, mEM)
 +
  -- Prevent overflow.
 +
  if 77 < #mem.lQ then
 +
    table.remove(mem.lQ, 1)
 +
  end
 +
  interrupt(iBeat)
 +
 
 +
  --p(mEM)
 +
 
 +
elseif 'program' == sET then
 +
 
 +
  if not mem.lQ then mem.lQ = {} end
 +
  q({ command = 'set', radius = 8, enabled = true,
 +
    offset_x = -3,
 +
    offset_z = 6,
 +
  })
 +
 
 +
  interrupt(heat)
 +
 
 +
end
 +
</syntaxhighlight>
 +
 
 +
= Bragging Rights =
 +
* Spawn 3.0 builder
 +
** Initiated and executed rainbow-promenade, with a lot of design help from [[User:Chi|chi]].
 +
** Brought up the idea of decorating earth lower half as globe.
 +
** Main designer of trumpet and flame (aka reactor) under Spawn Island.
 +
** Idea of platform on water which then was quickly implemented as Coast Guard Boat by others.
 +
** Copied PandoraBox text and spaceship from Spawn 2.0
 +
** Coordinated decisions across time-zones.
 +
** Assisted [[User:Coil|coil]] when removing ores from Space Island and painting dome.
 +
** Filled in the gaps and assisted in many parts.
 +
** Main Island: Tutorials
 +
** Favorite Island: Space

Latest revision as of 16:19, 3 May 2026

SwissalpS started as SwissaplS. It was a typo when initially just checking out Pandorabox. The test went much longer than expected.
SwissalpS occasionally logs on as SwissaplS for tests and to appreciate more how far he has come.
When SwissalpS is logged in as SwissaplS he might be AFK or not reading the in-game chat, so best use /mail if it is something 'important'.

Models

Chocolate Stag

Dimensions: 39x 108y 71z

6'302 chocolate blocks used

Located in Fruitland Screenshot 20201206 005406 stag night.png Screenshot 20201206 003715 stag sunrise.png

Bag Contents

As of 20201124 SwissalpS carries these items in his bags:

  • 3 different coloured beacons
  • Protectors
  • Protector Placement Tool (hardly used but great to have)
  • WiFi Chests
  • Travelnets
  • Travelnet Elevators
  • Stackwise Injectors
  • Mob Net
  • Hammer
  • 2 Multitools
  • Node Inspection Tool
  • Mapping Kit
  • Binoculars
  • Teleport Tubes
  • 450m Ropes
  • Circular Saws
  • Anvils
  • Postool
  • Tube Tool
  • 2 Replacers
  • Sonic Screwdriver and at times also a normal Screwdriver
  • Autocrafters
  • Space Suit Parts
  • Hazmat Suit
  • Water Can
  • Black Bones
  • Wooden Signs
  • Item Frames
  • Slingshot (is in bags so it might get some use)
  • Xenomorph
  • Horse
  • Saddle
  • Lasso
  • Mission Blocks
  • Mission Wands
  • Reserve Food (High in Nutrition)
  • Mese Blocks
  • Mese Crystals
  • Mese Shards
  • Mese Arrows
  • Diamond Crossbow
  • Diamond Blocks
  • Books
  • One Way Tubes
  • Sorting Tubes
  • Pneumatic Tubes
  • Sand Tubes
  • Accelerating Tubes
  • NICs
  • Mese Tubes
  • Priority Tubes
  • Digiline Detector Tubes
  • Digiline Tubes
  • Adjustable Tubes
  • Digiline Wires
  • Mesecon Wires
  • Copper Chests
  • Fancy Vendors
  • Mithril Chests
  • Vertical Digiline Intermediate (sometimes other wires)
  • Digtron Axle
  • MV Cables
  • HV Cables
  • Global Memory (as long as supply lasts)
  • I/O Expanders
  • Lua Controllers
  • Digiline Injectors
  • Self Contained Injectors
  • Node Breakers
  • Deployers
  • Mesecon Pistons
  • Mesecon Sticky Pistons
  • Constructor Mk1
  • Constructor Mk2/3
  • Buttons
  • Jump Drives
  • Lua Tool
  • Prospector (hardly gets used)
  • Spare Boots
  • Spare Glider
  • Mob Protectors
  • Mob Tags
  • Posters
  • Switches (mostly protected)
  • Compass(es)
  • Some kind of dig-able lights, currently Jack-O-Lanterns

Inventory Contents

  • Slot 1: Glider
  • Slot 2: Multitool
  • Slot 3: Replacer
  • Slot 8: Food (canned, marinated or pickled)
  • Slot 32: Empty Glass Bottles

Armour Inventory

Mostly only lava boots. Sometimes also a lava shield but not so much since beacons got healing effect. When AFK in mines, when too lazy to use multi-tool for defence or when working around chernobilyte: Lava boots, diamond leggings and shirt, hazmat and lava shield. SwissalpS does not like diamond helmet, the sound it makes on damage is irritating.

Client Side Mods

Server Side Mods

  • postool https://github.com/SwissalpS/postool Shows HUD with positional data and provides a tool to show map-block border. Replaced the old poshud mod.
  • replacer https://github.com/SwissalpS/replacer Cool creative tool originally by Sokomine. SwissalpS merged features from HybridDog and Coil0 and has maintained the version used on Pandorabox.io since.
  • SwissalpS has contributed to various other mods used on Pandorabox.io

In-Game Code

Self Updating Telemosaic

Requires mooncontroller. Solution to update telemosaics on a ship where each telemosaic targets another one and none is targeted by multiple others (making it impossible for [jumpdrive] to update them.

-- Title: Telemosaic updater
-- Author: SwissalpS
-- Version: 20260424_2332
-- Description: For use in a mooncontroller using
--        its pos global to detect when the telemosaic has moved and
--        update target of telemosaic.
--        This allows using non-bidirectional telemosaics in jd-fleets.
--------------

local sC = 'telemosaic'

local p = print
local mem = mem
local d = digiline_send

local function checkCoords()
  if not (mem.pos
    and mem.pos.x == pos.x
    and mem.pos.y == pos.y
    and mem.pos.z == pos.z)
  then
    d(sC, 'get')
  end
end -- checkCoords

local sET = event.type
if 'interrupt' == sET then

  interrupt(120)
  checkCoords()

elseif 'digiline' == sET then

  if sC ~= event.channel then return end

  local tM = event.msg
  if tM.player then return end

  if not mem.pos then
    local tDelta = {
      x = tM.target.x - pos.x,
      y = tM.target.y - pos.y,
      z = tM.target.z - pos.z,
    }
    mem.delta = tDelta
    mem.pos = pos
    return
  end

  d(sC, { command = 'setdest', pos = {
    x = pos.x + mem.delta.x,
    y = pos.y + mem.delta.y,
    z = pos.z + mem.delta.z,
  } })

  mem.pos = pos
  -- Report to other systems for example to restart quarries or activate beacons.
  d('b', true)

elseif 'off' == sET then

  checkCoords()

elseif 'program' == sET then

  interrupt(1)

end

Experiment Digibuilder & Quarry Farm I

This setup works on multiple layers and digs single crops when ripe. WIP, not sure I will ever bother to refine it. Can be visited by going to SwissalpS' "Hub Zero" -> "Quarries" -> "misc. Quarries" -> "92 FH46 Sphere" -> It is below you, you are on the level where luaCs are. Main luaC (digibuilder):

-- Title: Digibuilder Gardener
-- Author: SwissalpS
-- Version: 20260322_1004
-- Description: Uses a digibuilder to inspect crops and plant if needed.
--              When a ripe crop is detected the coordinates are sent via
--              digilines to (an)other luacontroller(s) that will deal with
--              harvesting.
--------------
--do mem = {} return end

local sCbutt = 'b'
local sCbuilder = 'r'
local sCdig = 'c'
local sCp = 'l'

local iBeat = 5

local iFirstX = -10
local iFirstZ = -10
local iFirstY = -15

local iLastX = 10
local iLastZ = 10
local iLastY = -6

local lSeeds = {
  'farming:chili_pepper', --'farming:cucumber',
  'farming:onion', 'farming:blueberries',
}

local d = digiline_send
local p = sCp and function(m) d(sCp, tostring(m)) end or pos and print
  or function(m)
    if not mem.p then mem.p = {} end
    local i = #mem.p
    if 44 > i then
      i = i + 1
    else
      table.remove(mem.p, 1)
    end
    mem.p[i] = m
  end

local function setMain(b)

  mem.bMain = b
  d(sCbutt, 'light_o' .. (b and 'n' or 'ff'))
  if b then interrupt(heat) end

end -- setMain

local function dig()

  d(sCdig, { x = mem.iX, y = mem.iY, z = mem.iZ })

end -- dig

-- helper function for isRipe()
local fFind = function(sHaystack, sNeedle) return string.find(sHaystack, sNeedle, 0, true) ~= nil end

-- pass item name to check. Ex. take event.msg from
-- node detector response to 'GET' message
local function isRipe(s)
  if 'string' ~= type(s) then return false end
  local tRipe = {

    ['bakedclay:delphinium'] = 1,
    ['bakedclay:lazarus'] = 1,
    -- managrass will be found with 'grass' check
    ['bakedclay:thistle'] = 1,
    ['canned_food:canned_beetroot_plus'] = 1,
    ['canned_food:canned_carrot_plus'] = 1,
    ['canned_food:canned_chili_pepper_plus'] = 1,
    ['canned_food:canned_cucumber_plus'] = 1,
    ['canned_food:canned_garlic_cloves_plus'] = 1,
    ['canned_food:canned_mushrooms_plus'] = 1,
    ['canned_food:canned_onion_plus'] = 1,
    ['canned_food:canned_potato_plus'] = 1,
    ['canned_food:canned_tomato_plus'] = 1,
    ['cherrytree:cherries'] = 1,
    ['default:blueberry_bush_leaves_with_berries'] = 1,
    ['default:cactus'] = 1,
    -- if you want to use this, you may want to remove some others
    ['default:dry_shrub'] = 1,
    ['default:fern_1'] = 1,
    ['default:fern_2'] = 1,
    ['default:fern_3'] = 1,
    ['default:papyrus'] = 1,
    ['farming:artichoke_5'] = 1,
    ['farming:barely_7'] = 1,
    ['farming:beetroot_5'] = 1,
    ['farming:blackberry_4'] = 1,
    ['farming:blueberry_4'] = 1,
    ['farming:cabbage_6'] = 1,
    ['farming:carrot_8'] = 1,
    ['farming:chili_8'] = 1,
    ['farming:cocoa_4'] = 1,
    ['farming:coffee_5'] = 1,
    ['farming:corn_8'] = 1,
    ['farming:cotton_8'] = 1,
    ['farming:cucumber_4'] = 1,
    ['farming:garlic_5'] = 1,
    ['farming:grapes_8'] = 1,
    ['farming:beanpole_5'] = 1,
    ['farming:hemp_8'] = 1,
    ['farming:lettuce_5'] = 1,
    ['farming:melon_8'] = 1,
    ['farming:mint_4'] = 1,
    ['farming:oat_8'] = 1,
    ['farming:onion_5'] = 1,
    ['farming:parsley_3'] = 1,
    ['farming:pea_5'] = 1,
    -- if you want a particular colour, set the others to -1
    ['farming:pepper_5'] = 1,  -- green
    ['farming:pepper_6'] = 1,  -- yellow
    ['farming:pepper_7'] = 1,  -- red
    ['farming:pineapple_8'] = 1,
    ['farming:potato_4'] = 1,
    ['farming:pumpkin_8'] = 1,
    ['farming:raspberry_4'] = 1,
    ['farming:rhubarb_3'] = 1,
    ['farming:rice_8'] = 1,
    ['farming:rye_8'] = 1,
    ['farming:soy_7'] = 1,
    ['farming:sunflower_8'] = 1,
    ['farming:tomato_8'] = 1,
    ['farming:vanilla_8'] = 1,
    ['farming:weed'] = 1,
    ['farming:wheat_8'] = 1,
    ['moretrees:coconut_3'] = 1,
    ['moretrees:dates_f4'] = 1,
    ['moretrees:rubber_tree_trunk'] = 1,
    -- need to catch this one early or search for 'trunk' will say yes
    ['moretrees:rubber_tree_trunk_empty'] = -1,
    ['vines:jungle_middle'] = 1,
    ['vines:root_middle'] = 1,
    ['vines:side_middle'] = 1,
    ['vines:vine_middle'] = 1,
    ['vines:willow_middle'] = 1,
    ['wine:blue_agave'] = 1,

  } -- tRipe

  -- straight up ripe?
  local iV = tRipe[s]
  if nil ~= iV then
    -- blacklisted?
    if 0 >= iV then return false end
    return true
  end

  -- flowers are ripe if they exists
  -- this also covers mushrooms
  if fFind(s, 'flowers:') then return true end

  -- bushes, not yet checked, are ripe when stem exists
  if fFind(s, 'bush_stem') then return true end

  -- grass can be considered ripe too
  if fFind(s, 'grass') then return true end

  -- check for trunks as these are ripe too
  return fFind(s, 'trunk')

end -- isRipe

local function peek(tPos)

  mem.isPeek = true
  d(sCbuilder, { command = 'getnode', pos = tPos })

end -- peek

local function poke(tPos, sN)

  mem.isPoke = true
  d(sCbuilder, { command = 'setnode', pos = tPos, name = sN, down = true })

end -- poke

local function handlePeek(mEM)

  mem.isPeek = nil
  local sN = mEM.name
  if 'air' == sN then
    poke({ x = mem.iX, y = mem.iY, z = mem.iZ },
      lSeeds[math.random(1, #lSeeds)])
    return
  elseif isRipe(sN) then
    dig()
  end

  interrupt(iBeat)
--p(mEM)

end -- handlePeek

local function handlePoke(mEM)

  mem.isPoke = nil
  port.a = nil
  port.c = nil
  if mEM.error then
    port.c = true
    local sE = mEM.message
    local sEstart = sE:sub(1, 4)
    if 'Item' == sEstart then
      port.a = true
      p('\nNeed:' .. sE:sub(23))
    end
  end

  interrupt(iBeat)

end -- handlePoke

local sET = event.type
if 'interrupt' == sET then

  if not mem.bMain then return end

  mem.iX = mem.iX + 1
  if iLastX < mem.iX then
    mem.iX = iFirstX
    mem.iZ = mem.iZ + 1
    p('Y: ' .. mem.iY .. ' Z: ' .. mem.iZ)
    if iLastZ < mem.iZ then
      mem.iZ = iFirstZ
      mem.iY = mem.iY + 3
      if iLastY < mem.iY then
        mem.iY = iFirstY
      end
      p('Y: ' .. mem.iY)
    end
  end

  peek({ x = mem.iX, y = mem.iY, z = mem.iZ })

elseif 'digiline' == sET then

  local sEC = event.channel
  if sCbutt == sEC then return setMain(not mem.bMain) end

  local mEM = event.msg
  if sCbuilder == sEC then
    if mem.isPeek then return handlePeek(mEM) end
    if mem.isPoke then return handlePoke(mEM) end
  end
 
  --p(event)

elseif 'program' == sET then

  if not mem.iX then mem.iX = iFirstX end
  if not mem.iY then mem.iY = iFirstY end
  if not mem.iZ then mem.iZ = iFirstZ end

  setMain(mem.bMain)

end

Secondary luaC (quarry):

-- Title: Quarry Gardener
-- Author: SwissalpS
-- Version: 20260320_2341
-- Description: Uses a quarry to harvest crops.
--              It takes the coordinates from another luacontroller that
--              has scanned for ripe crops.
--------------

-- Channel to listen for coordinates from.
local sCcom = 'c'
-- Channel of quarry.
local sCq = 'q'

local iBeat = 3

-- Offset to convert the received coordinates to quarry coordinates.
local tD = { x = 0, y = 3, z = 0 }

--------------
local d = digiline_send
local p = function(m) d('l', m) end
local q = function(t) d(sCq, t) end

local sET = event.type
if 'interrupt' == sET then

  if mem.bDigging then
    -- Check if quarry is done.
    q('get')
  else
    -- Not digging, start next dig - if there is at least one more coordinate.
    local tP = mem.lQ[1]
    if tP then
      mem.bDigging = true
      q({ command = 'set', restart = true,
        max_depth = -1 * (tP.y + tD.y + 1),
        offset_x = tP.x + tD.x,
        offset_y = tP.y + tD.y,
        offset_z = tP.z + tD.z,
      })
      table.remove(mem.lQ, 1)
    end
    interrupt(iBeat)
  end

elseif 'digiline' == sET then

  local sEC = event.channel
  local mEM = event.msg
  if sCq == sEC then
    if mEM.finished then
      mem.bDigging = nil
    end
    return interrupt(iBeat)
  end

  if sCcom ~= sEC then return end

  -- Add coordinate to queue.
  table.insert(mem.lQ, mEM)
  -- Prevent overflow.
  if 444 < #mem.lQ then
    table.remove(mem.lQ, 1)
  end

  --p(mEM)

elseif 'program' == sET then

  if not mem.lQ then mem.lQ = {} end
  q({ command = 'set', radius = 0, enabled = true })
  interrupt(heat)

end

Experiment Digibuilder & Quarry Farm II

This setup works on multiple layers and digs entire layers when enough crops are deemed ripe. It can safely plant on a layer that is being dug. WIP, not sure I will ever bother to refine it. Can be visited by going to SwissalpS' "Hub Zero" -> "Quarries" -> "misc. Quarries" -> "92 FH46 Sphere" -> It is above you. Main luaC (digibuilder):

-- Title: Digibuilder Gardener 2.0
-- Author: SwissalpS
-- Version: 20260324_1007
-- Description: Uses a digibuilder to inspect crops and plant if needed.
--              When enough ripe crops are detected on a layer, the
--              y-coordinate is sent via digilines to another luacontroller
--              which deals with harvesting via quarry.
--------------
--do mem.tToCheck = nil return end
--do mem.iX = nil mem.iY = nil mem.iZ = nil return end

local sCbutt = 'b'
local sCbuilder = 'r'
local sCdig = 'c'
local sCp = 'l'

local iBeatPeek = .1
local iBeatPoke = .51

local iFirstX = -11
local iFirstZ = -2
local iFirstY = -15

local iLastX = 5
local iLastZ = 14
local iLastY = -6

local lSeeds = {
  'farming:chili_pepper', --'farming:cucumber',
  'farming:onion', 'farming:blueberries',
}

local d = digiline_send
local p = sCp and function(m) d(sCp, m) end or pos and print
  or function(m)
    if not mem.p then mem.p = {} end
    local i = #mem.p
    if 44 > i then
      i = i + 1
    else
      table.remove(mem.p, 1)
    end
    mem.p[i] = m
  end

local function setMain(b)

  mem.bMain = b
  d(sCbutt, 'light_o' .. (b and 'n' or 'ff'))
  if b then interrupt(heat) end

end -- setMain

local function dig()

  local sY = tostring(mem.iY)
  if mem.tQueued[sY] then
    p(sY .. ' already queued')
    return
  end

  mem.tQueued[sY] = true
  d(sCdig, mem.iY)

end -- dig

-- helper function for isRipe()
local fFind = function(sHaystack, sNeedle) return string.find(sHaystack, sNeedle, 0, true) ~= nil end

-- pass item name to check. Ex. take event.msg from
-- node detector response to 'GET' message
local function isRipe(s)
  if 'string' ~= type(s) then return false end
  local tRipe = {

    ['bakedclay:delphinium'] = 1,
    ['bakedclay:lazarus'] = 1,
    -- managrass will be found with 'grass' check
    ['bakedclay:thistle'] = 1,
    ['canned_food:canned_beetroot_plus'] = 1,
    ['canned_food:canned_carrot_plus'] = 1,
    ['canned_food:canned_chili_pepper_plus'] = 1,
    ['canned_food:canned_cucumber_plus'] = 1,
    ['canned_food:canned_garlic_cloves_plus'] = 1,
    ['canned_food:canned_mushrooms_plus'] = 1,
    ['canned_food:canned_onion_plus'] = 1,
    ['canned_food:canned_potato_plus'] = 1,
    ['canned_food:canned_tomato_plus'] = 1,
    ['cherrytree:cherries'] = 1,
    ['default:blueberry_bush_leaves_with_berries'] = 1,
    ['default:cactus'] = 1,
    -- if you want to use this, you may want to remove some others
    ['default:dry_shrub'] = 1,
    ['default:fern_1'] = 1,
    ['default:fern_2'] = 1,
    ['default:fern_3'] = 1,
    ['default:papyrus'] = 1,
    ['farming:artichoke_5'] = 1,
    ['farming:barely_7'] = 1,
    ['farming:beetroot_5'] = 1,
    ['farming:blackberry_4'] = 1,
    ['farming:blueberry_4'] = 1,
    ['farming:cabbage_6'] = 1,
    ['farming:carrot_8'] = 1,
    ['farming:chili_8'] = 1,
    ['farming:cocoa_4'] = 1,
    ['farming:coffee_5'] = 1,
    ['farming:corn_8'] = 1,
    ['farming:cotton_8'] = 1,
    ['farming:cucumber_4'] = 1,
    ['farming:garlic_5'] = 1,
    ['farming:grapes_8'] = 1,
    ['farming:beanpole_5'] = 1,
    ['farming:hemp_8'] = 1,
    ['farming:lettuce_5'] = 1,
    ['farming:melon_8'] = 1,
    ['farming:mint_4'] = 1,
    ['farming:oat_8'] = 1,
    ['farming:onion_5'] = 1,
    ['farming:parsley_3'] = 1,
    ['farming:pea_5'] = 1,
    -- if you want a particular colour, set the others to -1
    ['farming:pepper_5'] = 1,  -- green
    ['farming:pepper_6'] = 1,  -- yellow
    ['farming:pepper_7'] = 1,  -- red
    ['farming:pineapple_8'] = 1,
    ['farming:potato_4'] = 1,
    ['farming:pumpkin_8'] = 1,
    ['farming:raspberry_4'] = 1,
    ['farming:rhubarb_3'] = 1,
    ['farming:rice_8'] = 1,
    ['farming:rye_8'] = 1,
    ['farming:soy_7'] = 1,
    ['farming:sunflower_8'] = 1,
    ['farming:tomato_8'] = 1,
    ['farming:vanilla_8'] = 1,
    ['farming:weed'] = 1,
    ['farming:wheat_8'] = 1,
    ['moretrees:coconut_3'] = 1,
    ['moretrees:dates_f4'] = 1,
    ['moretrees:rubber_tree_trunk'] = 1,
    -- need to catch this one early or search for 'trunk' will say yes
    ['moretrees:rubber_tree_trunk_empty'] = -1,
    ['vines:jungle_middle'] = 1,
    ['vines:root_middle'] = 1,
    ['vines:side_middle'] = 1,
    ['vines:vine_middle'] = 1,
    ['vines:willow_middle'] = 1,
    ['wine:blue_agave'] = 1,

  } -- tRipe

  -- straight up ripe?
  local iV = tRipe[s]
  if nil ~= iV then
    -- blacklisted?
    if 0 >= iV then return false end
    return true
  end

  -- flowers are ripe if they exists
  -- this also covers mushrooms
  if fFind(s, 'flowers:') then return true end

  -- bushes, not yet checked, are ripe when stem exists
  if fFind(s, 'bush_stem') then return true end

  -- grass can be considered ripe too
  if fFind(s, 'grass') then return true end

  -- check for trunks as these are ripe too
  return fFind(s, 'trunk')

end -- isRipe

local function ignoreString()
  return 'x' .. mem.iX .. 'z' .. mem.iZ --.. 'y' .. mem.iY
end -- ignoreString

local function isIgnore()

  -- only level -12 has water -> only level with ignore fields
  if -12 ~= mem.iY then return false end
  return fFind(mem.sIgnore, ignoreString())

end -- isIgnore

local function peek(tPos)

  mem.isPeek = true
  d(sCbuilder, { command = 'getnode', pos = tPos })

end -- peek

local function poke(tPos, sN)

  mem.isPoke = true
  d(sCbuilder, { command = 'setnode', pos = tPos, name = sN, down = true })

end -- poke

local function checkHash() return mem.iX .. ';' .. mem.iZ end

local function addToCheck()

  local sY = tostring(mem.iY)
  if not mem.tToCheck[sY] then mem.tToCheck[sY] = {} end
  table.insert(mem.tToCheck[sY], { mem.iX, mem.iZ })

end -- addToCheck

local function handlePeek(mEM)

  mem.isPeek = nil
  local sN = mEM.name
  if 'air' == sN then
    poke({ x = mem.iX, y = mem.iY, z = mem.iZ },
      lSeeds[math.random(1, #lSeeds)])
    return
  elseif not isRipe(sN) then
    addToCheck()
  end

  interrupt(iBeatPeek)
--p(mEM)

end -- handlePeek

local function handlePoke(mEM)

  mem.isPoke = nil
  port.a = nil
  port.c = nil
  if mEM.error then
    port.c = true
    local sE = mEM.message
    local sEstart = sE:sub(1, 4)
    if 'Item' == sEstart then
      -- "Item not in inventory: ..."
      port.a = true
      p('\nNeed:' .. sE:sub(23))
      addToCheck()
    elseif 'item' == sEstart then
      -- "item placement failed: ..."
      -- most probably because water under this location -> ignore
      mem.sIgnore = mem.sIgnore .. ignoreString()
    end
  else
    addToCheck()
  end

  interrupt(iBeatPoke)

end -- handlePoke

local function advanceCheck()

  if mem.bLastCheck then
    mem.bLastCheck = nil
    mem.iX = iLastX
    mem.iZ = iLastZ
    return false
  end

  if not mem.tCheck then return false end

  local iC = #mem.tCheck
  local t = mem.tCheck[iC]
  table.remove(mem.tCheck)
  mem.iX = t[1]
  mem.iZ = t[2]
  -- Last coordinate?
  if 1 == iC then
    mem.tCheck = nil
    mem.bLastCheck = true
  end
  p('X: ' .. mem.iX .. ' Z: ' .. mem.iZ)
  return true

end -- advanceCheck

local function advancePos()

  if advanceCheck() then return end

  local sY, iC

  mem.iX = mem.iX + 1
  if iLastX < mem.iX then
    mem.iX = iFirstX
    mem.iZ = mem.iZ + 1
    p('Y: ' .. mem.iY .. ' Z: ' .. mem.iZ)
    if iLastZ < mem.iZ then
      mem.iZ = iFirstZ
      sY = tostring(mem.iY)
      iC = mem.tToCheck[sY] and #mem.tToCheck[sY] or -1
      if 0 >= iC then
        dig()
      end
      mem.iY = mem.iY + 3
      if iLastY < mem.iY then
        mem.iY = iFirstY
      end

      p('Y: ' .. mem.iY)

      sY = tostring(mem.iY)
      iC = mem.tToCheck[sY] and #mem.tToCheck[sY] or -1
      if 0 < iC then
        mem.tCheck = mem.tToCheck[sY]
        mem.tToCheck[sY] = {}
        advanceCheck()
      end
    end
  end

end -- advancePos()

local sET = event.type
if 'interrupt' == sET then

  if not mem.bMain then return end

  advancePos()
  if isIgnore() then advancePos() end

  peek({ x = mem.iX, y = mem.iY, z = mem.iZ })

elseif 'digiline' == sET then

  local sEC = event.channel
  if sCbutt == sEC then return setMain(not mem.bMain) end

  local mEM = event.msg
  if sCbuilder == sEC then
    if mem.isPeek then return handlePeek(mEM) end
    if mem.isPoke then return handlePoke(mEM) end
  elseif sCdig == sEC then
    mem.tQueued[tostring(mEM)] = nil
  end
 
  p(event)

elseif 'program' == sET then

  if not mem.tQueued then mem.tQueued = {} end
  if not mem.tToCheck then mem.tToCheck = {} end
  if not mem.sIgnore then mem.sIgnore = '' end
  if not mem.iX then mem.iX = iFirstX end
  if not mem.iY then mem.iY = iFirstY end
  if not mem.iZ then mem.iZ = iFirstZ end

  setMain(mem.bMain)

end

Secondary luaC (quarry):

-- Title: Quarry Gardener 2.0
-- Author: SwissalpS
-- Version: 20260323_1446
-- Description: Uses a quarry to harvest crops.
--              It takes the y-coordinate from another luacontroller that
--              has scanned for ripe crops.
--------------
--do mem.lQ = nil return end
--do mem.bDigging = nil return end

-- Channel to listen for coordinates from.
local sCcom = 'c'
-- Channel of quarry.
local sCq = 'q'

local iBeat = 3

-- Offset to convert the received coordinates to quarry coordinates.
local iDy = 3

--------------
local d = digiline_send
local p = function(m) d('l', m) end
local q = function(m) d(sCq, m) end

local sET = event.type
if 'interrupt' == sET then

  if mem.bDigging then
    -- Check if quarry is done.
    q('get')
  else
    -- Not digging, start next dig - if there is at least one more coordinate.
    local iY = mem.lQ[1]
    if nil == iY then return end

    mem.bDigging = true
    q({ command = 'set', restart = true,
      max_depth = -1 * (iY + iDy + 1),
      offset_y = iY + iDy,
    })
    mem.iY = iY
    table.remove(mem.lQ, 1)
    interrupt(iBeat)
  end

elseif 'digiline' == sET then

  local sEC = event.channel
  local mEM = event.msg
  if sCq == sEC then
    if mEM.finished then
      d(sCcom, mem.iY)
      mem.iY = nil
      mem.bDigging = nil
    end
    return interrupt(iBeat)
  end

  if sCcom ~= sEC then return end

  -- Add coordinate to queue.
  table.insert(mem.lQ, mEM)
  -- Prevent overflow.
  if 77 < #mem.lQ then
    table.remove(mem.lQ, 1)
  end
  interrupt(iBeat)

  --p(mEM)

elseif 'program' == sET then

  if not mem.lQ then mem.lQ = {} end
  q({ command = 'set', radius = 8, enabled = true,
    offset_x = -3,
    offset_z = 6,
  })

  interrupt(heat)

end

Bragging Rights

  • Spawn 3.0 builder
    • Initiated and executed rainbow-promenade, with a lot of design help from chi.
    • Brought up the idea of decorating earth lower half as globe.
    • Main designer of trumpet and flame (aka reactor) under Spawn Island.
    • Idea of platform on water which then was quickly implemented as Coast Guard Boat by others.
    • Copied PandoraBox text and spaceship from Spawn 2.0
    • Coordinated decisions across time-zones.
    • Assisted coil when removing ores from Space Island and painting dome.
    • Filled in the gaps and assisted in many parts.
    • Main Island: Tutorials
    • Favorite Island: Space