Difference between revisions of "User:SwissalpS"
(add telemosaic auto updater) |
|||
| (7 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 | + | 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'. | 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 = | = Bragging Rights = | ||
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
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.
-- 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