Jump to content

Kurock

Alpha Team Vanguard
  • Posts

    848
  • Joined

  • Last visited

Reputation Activity

  1. Like
    Kurock got a reaction from DecoyGoatBomb in DEVBLOG: TRA$H TO TREASURE - discussion thread   
    Love these changes.  These wrecks are how their low-browed terrestrial cousins should have been. And the salvaging is long overdue.

    My only suggestion is to extend the "salvagable" timer on aphelia owned tiles from two days to a week before full deletion.  Players will clean those up quickly.

    For every well meaning DarkHorizon there are twenty market spammers with horrendous advertising and lag inducing sales pitching constructs.  I am still against having Aphelia run markets at all (power to the players, players run markets, advertising run by players) but this is still a good step in the interim.
  2. Like
    Kurock reacted to Endstar in DEVBLOG: TRA$H TO TREASURE - discussion thread   
    Hope NQ lets us submit ships we created for consideration to be placed into the pool for salvage. That would be really cool and give the community further engagement. 
  3. Like
    Kurock got a reaction from Samoht_Yreva in DEVBLOG: TRA$H TO TREASURE - discussion thread   
    Love these changes.  These wrecks are how their low-browed terrestrial cousins should have been. And the salvaging is long overdue.

    My only suggestion is to extend the "salvagable" timer on aphelia owned tiles from two days to a week before full deletion.  Players will clean those up quickly.

    For every well meaning DarkHorizon there are twenty market spammers with horrendous advertising and lag inducing sales pitching constructs.  I am still against having Aphelia run markets at all (power to the players, players run markets, advertising run by players) but this is still a good step in the interim.
  4. Like
    Kurock got a reaction from i2eilly in DEVBLOG: TRA$H TO TREASURE - discussion thread   
    Love these changes.  These wrecks are how their low-browed terrestrial cousins should have been. And the salvaging is long overdue.

    My only suggestion is to extend the "salvagable" timer on aphelia owned tiles from two days to a week before full deletion.  Players will clean those up quickly.

    For every well meaning DarkHorizon there are twenty market spammers with horrendous advertising and lag inducing sales pitching constructs.  I am still against having Aphelia run markets at all (power to the players, players run markets, advertising run by players) but this is still a good step in the interim.
  5. Like
    Kurock got a reaction from VandelayIndustries in DEVBLOG: TRA$H TO TREASURE - discussion thread   
    Love these changes.  These wrecks are how their low-browed terrestrial cousins should have been. And the salvaging is long overdue.

    My only suggestion is to extend the "salvagable" timer on aphelia owned tiles from two days to a week before full deletion.  Players will clean those up quickly.

    For every well meaning DarkHorizon there are twenty market spammers with horrendous advertising and lag inducing sales pitching constructs.  I am still against having Aphelia run markets at all (power to the players, players run markets, advertising run by players) but this is still a good step in the interim.
  6. Like
    Kurock reacted to NQ-Wanderer in DEVBLOG: TRA$H TO TREASURE   
    Wallet feeling a little light lately? Get ready to remedy that when the Panacea update hits, bringing with it two lucrative, quanta-producing possibilities: space wrecks and inactive asset requisitioning (IAR).
     
    SPACE WRECKS
     

    You’re going about your business, flying through space from one market to another or heading out to join some friends when your radar unexpectedly reveals something. Curious, you move closer to investigate and find wreckage. There’s no indication of who left it behind or why. Finders keepers, right? It’s yours now. And you’ve got some choices to make.
     
    DECISIONS, DECISIONS
    Will you: 
    salvage the ship for materials and elements? tokenize and sell the whole ship? sell the information about where the wreck is located? create a mission to get someone else to salvage it? The decision is all up to you.
     
    RADAR AND RARITY
    No special equipment is needed to locate these space wrecks, your standard radar is enough to detect them.
    They will spawn randomly throughout Helios. There are five tiers of rarity, with lower-tier wrecks with low-value contents spawning at a higher frequency. The highest-tier wrecks will be harder to find; those who discover and harvest them will be rewarded with high-value materials and elements.
     
    INACTIVE ASSET REQUISITIONING
     
    This system is being introduced to address one of the chief complaints we’ve heard from Noveans, namely abandoned constructs cluttering up the universe, especially in public markets.
     
    The purpose of IAR is to:
    create salvaging opportunities for players around the game world. ensure cleanliness and performance around markets and other Aphelia zone hot spots. clean up older, unowned constructs.  
    In other words, IAR has the potential to create a less cluttered environment for everyone and a fatter wallet for you.
     
    ABANDONED CONSTRUCTS DEFINED
    Players with a subscription, even if they have not logged in for a while, are in no danger of having their constructs flagged as “abandoned”, with the exception of constructs parked in Aphelia territory as noted below.
     
    In effect, constructs belonging to unsubscribed users and not on their owners’ sanctuary zones will eventually lose their ownership, putting them in an abandoned state and making them vulnerable to be salvaged by other players. Likewise, constructs owned by an organization will lose their ownership when the org’s last remaining legate ends their subscription.
     
    During the unsubscribe process, players will be reminded that their constructs will be in danger of becoming abandoned. When the constructs lose ownership, a blueprint will be placed in the owner’s inventory should they choose to return to Helios at a later date.
     
    TIME’S TICKING 
    When an account becomes inactive due to a canceled subscription, a countdown will begin on all constructs belonging to that account. After a period of time, those constructs will lose their ownership and other players will be able to salvage or capture them.
     
    Then, all unowned constructs will have a countdown timer and it will count down from the moment it is unowned, regardless of why they are unowned (PvP destruction, IAR, abandonment, etc.) At the end of this timer, the construct will simply be removed from the game world.
     
    The ‘ownership’ timer for unsubscribed players’ constructs is three months (90 days/2160 hours). The timer is the same for organization-owned constructs and begins when the last active legate of the owning organization’s subscription expires.
     
    Each time a construct is unclaimed, whether due to an owner’s inactive game account or if abandoned via PvP, a one-month countdown to decay is set and is visible to everyone. If no one salvages the construct, it will be permanently removed from the game.
     
    As with most newly introduced systems, this is subject to change in the future depending on the metrics we see as time passes.
     
    SPECIAL RULES FOR MARKETS 
    A modified version of IAR will be used on Aphelia territories, including all market zones.
    Regardless of whether or not the owner is an active subscriber, all constructs on Aphelia territories are subject to IAR. As soon as a player exits their construct in Aphelia territory, a 168-hour (seven-day) abandonment countdown will begin for that construct. This is considerably shorter than the default IAR timer. Once that countdown reaches 0, the construct will lose its ownership. A new countdown timer of 48-hours (two days) before deletion will start ticking.
     
    HAPPY HUNTING
     
    Will you be among the happy scavengers scouring the skyways and byways for wrecked ships and abandoned constructs? Join the conversation about this devblog here.
  7. Like
    Kurock reacted to NQ-Ligo in DEVBLOG: PANACEA LUA CHANGES - discussion thread   
    Hi all!
     
    After reading your feedback, we have noted that the removal of log functions was having a big impact on you. So after some internal discussions to find a solution based on your suggestions that would meet your expectations but would not be too heavy to implement, we have made a decision.
     
    We plan to add with Panacea, the ability to copy to your clipboard the content of a chat channel, and clean it up.

    It is certain that this will not provide a nice solution for the problems related to the lack of data export to virtual currency systems. We have some ideas for this but it won't be for a while unfortunately.

    Hopefully this announcement will allay your concerns  

    NQ-Ligo
  8. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Sample Scripts (Control Unit Interaction)
    These scripts show some simple examples of Control Units interacting with screen units.
     
    Light Control (Screen to Programming Board) This example demonstrates clickable buttons on a screen unit, that set the screens output to a value, the programming board reads that value and then clears it and toggles lights based on that data. (See the top section for the programming board code)
    --[[---------------------------------------------------------------------------- -- Add the following section to a programming boards System.Update() -- Link the screen unit and three lights to the programming board -- Name the slots in the programming board respectively: screen, light0, light1, light2 local handlers = {} handlers['Light 0'] = function() light0.toggle() end handlers['Light 1'] = function() light1.toggle() end handlers['Light 2'] = function() light2.toggle() end local output = screen.getScriptOutput() if #output > 0 then screen.clearScriptOutput() if handlers[output] then handlers[output]() end end --]]---------------------------------------------------------------------------- -- Screen render script below -------------------------------------------------------------------------------- font = loadFont('Play-Bold', 32) rx, ry = getResolution() cx, cy = getCursor() layer = createLayer() click = getCursorPressed() -------------------------------------------------------------------------------- if not Button then local mt = {} mt.__index = mt function Button (text, x, y) return setmetatable({ text = text, x = x, y = y, }, mt) end function mt:draw () local sx, sy = self:getSize() local x0 = self.x - sx/2 local y0 = self.y - sy/2 local x1 = x0 + sx local y1 = y0 + sy local r, g, b = 0.3, 0.7, 1.0 if cx >= x0 and cx <= x1 and cy >= y0 and cy <= y1 then r, g, b = 1.0, 0.0, 0.4 if click then setOutput(self.text) end end setNextShadow(layer, 64, r, g, b, 0.3) setNextFillColor(layer, 0.1, 0.1, 0.1, 1) setNextStrokeColor(layer, r, g, b, 1) setNextStrokeWidth(layer, 2) addBoxRounded(layer, self.x - sx/2, self.y - sy/2, sx, sy, 4) setNextFillColor(layer, 1, 1, 1, 1) setNextTextAlign(layer, AlignH_Center, AlignV_Middle) addText(layer, font, self.text, self.x, self.y) end function mt:getSize () local sx, sy = getTextBounds(font, self.text) return sx + 32, sy + 16 end function mt:setPos (x, y) self.x, self.y = x, y end end function drawFree (elems) for i, v in ipairs(elems) do v:draw() end end function drawListV (elems, x, y) for i, v in ipairs(elems) do local sx, sy = v:getSize() v:setPos(x, y) v:draw() y = y + sy + 4 end end function drawUsage () local font = loadFont('FiraMono', 16) setNextTextAlign(layer, AlignH_Center, AlignV_Top) addText(layer, font, "Activate board to enable buttons!", rx/2, ry - 32) end function drawCursor () if cx < 0 then return end addLine(layer, cx - 12, cy - 12, cx + 12, cy + 12) addLine(layer, cx + 12, cy - 12, cx - 12, cy + 12) end -------------------------------------------------------------------------------- local buttons = { Button('Light 0', 128, 90), Button('Light 1', 128, 290), Button('Light 2', 128, 490), } drawFree(buttons) drawUsage() drawCursor() requestAnimationFrame(5)  

     
     
    Light Control (Screen to Programming Board) This allows you to create and use toggle buttons for boolean values, the examples flip not only their own state, but also the horizontally neighbouring toggle.
    --[[---------------------------------------------------------------------------- -- Add to programming board's unit.start() and connect it to this screen. local messages = { "Hi, I am a message!", "Yes hello this is Lua Screen.", "We love Pops the Hamster!", "WARNING: No warnings found.", "I am a screen unit.", "Are you enjoying this?", "Pending Screen Operations", "Lua Screen Units o/", "It is NOT NQ-Node\'s fault.", "Knock knock...", "Who's there?", "Ran out of Knock knock jokes.", "It is all NQ-Deckard\'s fault." } local message = messages[math.random(1, #messages)] local params = { message = message, testNumber = 1.337, testStr = 'hello I am a string', testTable = {x = 1, y = 0, k = {1, 2, 3, 4}}, } screen.setScriptInput(json.encode(params)) unit.exit() --]]---------------------------------------------------------------------------- -- Screen render script below -------------------------------------------------------------------------------- local json = require('dkjson') local params = json.decode(getInput()) or {} message = params.message or '[ no message ]' fontSize = params.fontSize or 64 color = params.color or {r=1.0,g=0,b=0.3} -------------------------------------------------------------------------------- local font = loadFont('Play-Bold', fontSize) local rx, ry = getResolution() local layer = createLayer() local cx, cy = getCursor() local sx, sy = getTextBounds(font, message) setDefaultStrokeColor(layer, Shape_Line, 1, 1, 1, 0.5) setNextShadow(layer, 64, color.r, color.g, color.b, 0.4) setNextFillColor(layer, color.r, color.g, color.b, 1.0) addBoxRounded(layer, (rx-sx-16)/2, (ry-sy-16)/2, sx+16, sy+16, 8) setNextFillColor(layer, 0, 0, 0, 1) setNextTextAlign(layer, AlignH_Center, AlignV_Middle) addText(layer, font, message, rx/2,ry/2) -------------------------------------------------------------------------------- local fontCache = {} function getFont (font, size) local k = font .. '_' .. tostring(size) if not fontCache[k] then fontCache[k] = loadFont(font, size) end return fontCache[k] end function drawUsage () local font = getFont('FiraMono', 16) setNextTextAlign(layer, AlignH_Center, AlignV_Top) addText(layer, font, "Activate for an exciting new message!", rx/2, ry - 32) end function drawCursor () if cx < 0 then return end if getCursorDown() then setDefaultShadow(layer, Shape_Line, 32, color.r, color.g, color.b, 0.5) end addLine(layer, cx - 12, cy - 12, cx + 12, cy + 12) addLine(layer, cx + 12, cy - 12, cx - 12, cy + 12) end function prettyStr (x) if type(x) == 'table' then local elems = {} for k, v in pairs(x) do table.insert(elems, string.format('%s = %s', prettyStr(k), prettyStr(v))) end return string.format('{%s}', table.concat(elems, ', ')) else return tostring(x) end end function drawParams () local font = getFont('RobotoMono', 11) setNextTextAlign(layer, AlignH_Left, AlignV_Bottom) addText(layer, font, "Script Parameters", 16, 16) local y = 32 for k, v in pairs(params) do setNextTextAlign(layer, AlignH_Left, AlignV_Bottom) addText(layer, font, k .. ' = ' .. prettyStr(v), 24, y) y = y + 12 end end -------------------------------------------------------------------------------- drawUsage() drawCursor() drawParams() requestAnimationFrame(1)  

  9. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Sample Scripts (Widget Objects)
    These widget scripts show some examples of interactive UI elements created using the new Lua Screen Units.
     
    Sliders This allows you to create and use sliders for number values, the example handles change the color properties of background square.
    ------------------------------------ -- Slider widget example -- Usage: -- Creates a slider style widget with a minimum and maximum value. -- -- Init: -- widget = Slider(x,y,width,length,minValue,maxValue,defaultValue,label) -- Return fractional value of the slider position: -- widget:getFraction() -- Force set the value of the slider: -- widget:setValue(value) -- Draw the slider: -- widget:draw(layer) -- Return the value of slider: -- widget.value ------------------------------------ if not Slider then -- Object definition: Lets start by creating a meta table with some properties local slider = {} slider.__index = slider function Slider(x, y, width, length, min, max, defaultValue, label) self = {} self.x = x or 0 self.y = y or 0 self.l = length or 100 self.w = width or 20 self.min = min or 0 self.max = max or 1 self.value = math.max(self.min, math.min(self.max, defaultValue)) or self.min self.label = label or "" self.doDrag = false return setmetatable(self, slider) end -- example helper method that returns the position of the slider between a value of 0 and 1 function slider:getFraction() return (self.value - self.min) / (self.max - self.min) end -- example set method that allows us to set the value of the slider function slider:setValue(val) self.value = math.max(self.min, math.min(self.max, val)) end -- example draw method that draws the slider on the screen using the given layer and stored properties function slider:draw(layer) -- Collect some cursor data local cx, cy = getCursor() local cPressed = getCursorPressed() local cReleased = getCursorReleased() -- Determine if the handle for this slider was just clicked or released if cPressed and cx >= self.x and cx <= (self.x + self.w) and cy >= self.y + self.l - (self:getFraction() * self.l) and cy <= self.y + (self.w / 5) + self.l - (self:getFraction() * self.l) + (2 * self.w / 5) then self.doDrag = true elseif cReleased == true or cx == -1 then self.doDrag = false end -- If we are currently dragging the handle we should update the position and value of the slider. if self.doDrag then local vy = 1 - ((cy - self.y - self.w / 5) / self.l) self.value = math.max(self.min, math.min(self.max, self.min + ((self.max - self.min) * vy))) end -- Finaly lets draw the entire slider on the screen -- Draw the background vertical bar (You could change the color below this line, or make a property for it) setNextFillColor(layer, 0.5, 0.5, 1, 1) addBoxRounded(layer, self.x + (self.w / 2.5), self.y + self.w / 5, self.w / 5, self.l, math.min(self.w / 10, self.l / 10)) -- Draw the handle (You could change the color below this line, or make a property for it) setNextFillColor(layer, 0.7, 0.7, 1, 1) addBoxRounded(layer, self.x, self.y + self.l - (self:getFraction() * self.l), self.w, self.w / 2.5, self.w / 10) -- Lets load a font for this slider, if you want lots of sliders, you may want to load the font outside this object to avoid to many fonts. local font = loadFont("Montserrat", self.w / 5) -- Fetch the dimensions of the label and value we are about to draw local lw,lh = getTextBounds(font, self.label) local vw,vh = getTextBounds(font, math.floor(self.value * 100) * 0.01) -- draw the label above the slider addText(layer, font, self.label, self.x + (self.w / 2) - (lw / 2), self.y - (lh * 0.5)) -- draw the value on the handle setNextFillColor(layer, 0.1, 0.1, 0.1, 1) addText(layer, font, math.floor(self.value * 100) * 0.01, self.x + (self.w / 2) - (vw / 2), self.y + self.l - (self:getFraction() * self.l) + (self.w / 5) + (vh / 2)) end end ------------------------------------ -- Display ------------------------------------ -- Collect the screen resolution for positioning local rx, ry = getResolution() -- Create two layers, one for the background square, the other for the sliders local backgroundLayer = createLayer() local sliderLayer = createLayer() -- Run this portion only once on screen startup, this creates the slider objects if not init then init = true sliderRed = Slider(rx/5-25, ry/8,50, ry/8*6, 0, 1, 0.2, "Red") sliderGreen = Slider(rx/5*2-25, ry/8,50, ry/8*6, 0, 1, 0.4, "Green") sliderBlue = Slider(rx/5*3-25, ry/8,50, ry/8*6, 0, 1, 0.6, "Blue") sliderAlpha = Slider(rx/5*4-25, ry/8,50, ry/8*6, 0, 1, 0.8, "Alpha") end -- every frame, draw the 4 slider sliderRed:draw(sliderLayer) sliderGreen:draw(sliderLayer) sliderBlue:draw(sliderLayer) sliderAlpha:draw(sliderLayer) -- every frame, draw the background square using the slider values as colour properties for the fill setNextFillColor(backgroundLayer, sliderRed.value, sliderGreen.value, sliderBlue.value, sliderAlpha.value) addBoxRounded(backgroundLayer,25,25,rx-50,ry-50,20) -- render cost profiler if true then local layer = createLayer() local font = loadFont('Play-Bold', 14) setNextFillColor(layer, 1, 1, 1, 1) addText(layer, font, string.format('render cost : %d / %d', getRenderCost(), getRenderCostMax()), 8, 16) end -- refresh the screen at a high rate to keep the slider elements responsive requestAnimationFrame(1)  

     
     
    Toggle buttons This allows you to create and use toggle buttons for boolean values, the examples flip not only their own state, but also the horizontally neighbouring toggle.
    ------------------------------------ -- Toggle widget example -- Usage: -- Creates a toggle button style widget with a true or false state. -- -- Init: -- widget = Toggle(x, y, width, height, defaultState, label, font, onClick) -- onClick can be a function that is executed when the button is clicked. -- Draw the slider: -- widget:draw(layer) -- Return the value of slider: -- widget:getState() -- or -- widget.state -- Set the state of the toggle: -- widget:setState(boolean) -- Switch the state of the toggle: -- widget:switch() -- Also returns the new state of the widget ------------------------------------ if not Toggle then -- Object definition: Lets start by creating a meta table with some properties local toggle = {} toggle.__index = toggle function Toggle(x, y, width, height, defaultState, label, font, onClick) self = {} self.x = x or 0 self.y = y or 0 self.h = height or 20 self.w = width or 20 self.state = defaultState or false self.label = label or "" self.onClick = onClick self.font = font or function() return loadFont("Montserrat",self.h * 5.5) end return setmetatable(self, toggle) end -- create a function that creates a new font if required, otherwise returns the font identifier from the properties local function getFont() local font = self.font if type(font) == "function" then font = self.font() end return font end -- draw method that draws the toggle on the screen unit function toggle:draw(layer) -- Collect cursor coordinates local cx, cy = getCursor() -- check if the mouse is over the button if cx >= self.x and cx <= self.x + self.w and cy >= self.y and cy <= self.y + self.h then -- handle the click action if getCursorReleased() then self.state = not self.state if self.onClick ~= nil then self.onClick() end end -- set the mouseover colors if self.state and getCursorDown() then setNextFillColor(layer, 1, 0.6, 0.8, 1) elseif getCursorDown() then setNextFillColor(layer, 0.6, 1, 0.8, 1) elseif self.state then setNextFillColor(layer, 1, 0.2, 0.2, 1) else setNextFillColor(layer, 0.2, 0.8, 0.2, 1) end elseif self.state then -- set color if toggle is true setNextFillColor(layer, 0.2, 0.2, 0.6, 1) else -- set color if toggle is false setNextFillColor(layer, 0.2, 0.2, 0.2, 1) end -- draw the button toggle local w, h = self:getBounds() addBox(layer, self.x, self.y + (h / 2 - self.h / 2), self.w, self.h) setNextTextAlign(layer, AlignH_Left, AlignV_Middle) addText(layer, getFont(), self.label, self.x + self.w + (self.w / 5), self.y + (h / 2)) end -- simple example helper method that returns the current state of the toggle function toggle:getState() return self.state end -- simple example helper method that sets the state of the toggle function toggle:setState(newState) if type(newState) == "boolean" then self.state = newState end end -- simple example helper method that changes and returns the new state of the toggle function toggle:switch() self.state = not self.state return self.state end -- helper method that returns the width and height of toggle function toggle:getBounds() local tx, ty = getTextBounds(getFont(),self.label) return self.x + self.w + (self.w / 5) + tx, math.max(self.h, ty) end end ---------------------------------------------------- -- Display ---------------------------------------------------- -- Collect the screen resolution for positioning local rx,ry = getResolution() -- Create a layer to draw the toggles local exampleLayer = createLayer() -- Create a font for all the toggles local font = loadFont("Montserrat", 20) -- Run this portion only once on screen startup, this creates the toggle objects if not init then init = true example1 = Toggle(rx / 7, ry / 5 - rx / 40, rx / 20, rx / 20, true, "Example Toggle 1 (Linked)", font) example2 = Toggle(rx / 7, ry / 5 * 2 - rx / 40, rx / 20, rx / 20, false, "Example Toggle 2 (Linked)", font) example3 = Toggle(rx / 7, ry / 5 * 3 - rx / 40, rx / 20, rx / 20, true, "Example Toggle 3", font) example4 = Toggle(rx / 7, ry / 5 * 4 - rx / 40, rx / 20, rx / 20, false, "Example Toggle 4", font) exampleA = Toggle(rx / 7 * 4, ry / 5 - rx / 40, rx / 20, rx / 20, false, "Example Toggle A (Linked)", font) exampleB = Toggle(rx / 7 * 4, ry / 5 * 2 - rx / 40, rx / 20, rx / 20, true, "Example Toggle B (Linked)", font) exampleC = Toggle(rx / 7 * 4, ry / 5 * 3 - rx / 40, rx / 20, rx / 20, false, "Example Toggle C", font) exampleD = Toggle(rx / 7 * 4, ry / 5 * 4 - rx / 40, rx / 20, rx / 20, true, "Example Toggle D", font) end -- for toggle, make the toggle next to it always the opposite and then draw the toggle example1:setState(not exampleA:getState()) example1:draw(exampleLayer) example2:setState(not exampleB:getState()) example2:draw(exampleLayer) example3:draw(exampleLayer) example4:draw(exampleLayer) exampleA:setState(not example1:getState()) exampleA:draw(exampleLayer) exampleB:setState(not example2:getState()) exampleB:draw(exampleLayer) exampleC:draw(exampleLayer) exampleD:draw(exampleLayer) -- render cost profiler if true then local layer = createLayer() local font = loadFont('Play-Bold', 14) setNextFillColor(layer, 1, 1, 1, 1) addText(layer, font, string.format('render cost : %d / %d', getRenderCost(), getRenderCostMax()), 8, 16) end -- Refresh the screen requestAnimationFrame(1)  

  10. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Sample Scripts (Advanced)
    These more advanced scripts demonstrate what can ultimately be achieved with this API. Perhaps they will inspire you a bit!
     
    Entropy Like the bouncy ball example, but fancier!
    local ballCount = 5000 local speed = 20 local rx, ry = getResolution() --[[ init ]]-------------------------------------------------------------------- if not init then init = true function randF (a, b) return a + (b - a) * math.random() end function randExp () return -math.log(1.0 - math.random()) end balls = {} for i = 1, ballCount do local e = {} e.x = randF(0, rx) e.y = randF(0, ry) e.r = 1 + 1.0 * math.log(1.0 - math.random()) ^ 2.0 e.vx = randF(-1, 1) * randExp() e.vy = randF(-1, 1) * randExp() e.cx = 1.0 * math.random() e.cy = 0.1 * math.random() e.cz = 0.4 * math.random() e.ca = math.random() table.insert(balls, e) end end --[[ simulation ]]-------------------------------------------------------------- local dt = speed * getDeltaTime() for _, v in ipairs(balls) do v.x = v.x + dt * v.vx v.y = v.y + dt * v.vy if v.x < 0 or v.x > rx then v.x = v.x - dt * v.vx v.vx = -v.vx end if v.y < 0 or v.y > ry then v.y = v.y - dt * v.vy v.vy = -v.vy end end --[[ rendering ]]--------------------------------------------------------------- local l = createLayer() -- render balls for _, e in ipairs(balls) do setNextFillColor(l, e.cx, e.cy, e.cz, e.ca) addCircle(l, e.x, e.y, e.r) end -- render title local font = loadFont('Play-Bold', 64) addText(l, font, '{{ E N T R O P Y }}', 32, ry - 32) requestAnimationFrame(1) --------------------------------------------------------------------------------  
     
    Radial Menu Practical example of how responsive UI elements could be designed, creates 3 radial menu's using the same function.
    Left: Example with transparency on the circle layer to demonstrate layering and the use of math to define the circle stroke.
    Top right: Example without transparency, drawn to a different scale.
    Bottom right: Example of a collapsible version saving screen real-estate and reducing rending costs while collapsed. (Only a dot is drawn while collapsed)
    -- Get screen resolution and center coordinates local resolutionX,resolutionY = getResolution() local centerX,centerY = resolutionX/2, resolutionY/2 -- Draw radial menu layers local triLayer = createLayer() local circleLayer = createLayer() local textLayer = createLayer() -- Define some helpers to convert degrees to radians local d2r = math.pi/180 -- Helper function to get distance of cursor from coordinates function getCursorDistance(x,y) local curX,curY = getCursor() curX = curX - x curY = curY - y return math.sqrt(curX^2+curY^2) end -- Helper function to check if cursor is inside a radial menu section function isCursorInside(radialCenterX,radialCenterY,radiusMin,radiusMax,degMin,degMax) local curX,curY = getCursor() curX = curX - radialCenterX curY = curY - radialCenterY local magnitude = getCursorDistance(radialCenterX,radialCenterY) local deg = math.atan(curY/curX)/d2r if curX < 0 then deg = deg + 180 end if curX > 0 and curY < 0 then deg = deg + 360 end if magnitude <= radiusMax and magnitude >= radiusMin and deg > degMin and deg < degMax then return true end end -- Define function that can draw a radial menu on demand function drawRadialMenu(posX,posY,radius,steps,hide,strokeAndCenterAlpha) -- Define default variables local posX = posX or centerX local posY = posY or centerY local radius = radius or 200 local steps = steps or 15 if hide ~= nil then hide = hide else hide = true end local strokeAndCenterAlpha = strokeAndCenterAlpha or 1 -- minimum of 4 segments steps = math.max(steps,4) -- work out the depth of the triangular facets along the circumferance and use it to determine the outer circle stroke width local aX,aY = radius*math.cos(0) , radius*math.sin(0) local bX,bY = radius*math.cos(((360/steps)*1)*d2r) , radius*math.sin(((360/steps)*1)*d2r) local stroke = math.min(-(radius-math.sqrt(((aX+bX)/2)^2+((aY+bY)/2)^2)),-3) if not hide or (hide and getCursorDistance(posX,posY) < (radius)) then -- Draw triangular segments local alpha = 0.5 for step=1,steps,1 do -- calculate the corners of the triangles on the radius local aX,aY = posX + (radius*math.cos(((360/steps)*(step-1))*d2r)) , posY + (radius*math.sin(((360/steps)*(step-1))*d2r)) local bX,bY = posX + (radius*math.cos(((360/steps)*step)*d2r)) , posY + (radius*math.sin(((360/steps)*step)*d2r)) -- call helper function to determine if cursor is inside the segment and change color if true if isCursorInside(posX,posY,radius/3,radius,((360/steps)*(step-1)),(360/steps)*step) then setNextFillColor(triLayer,1,1,1,1) -- create center number while the cursor is over a selection local font = loadFont('Montserrat', math.floor(radius/4)) setNextFillColor(textLayer, 1, 1, 1, 1) addText(textLayer, font,step, posX-((radius/12)*#tostring(step)), posY+math.floor(radius/12)) else setNextFillColor(triLayer,1,0,0,alpha + 0.5) end -- Draw the triangle based on pre-calculated coordinates and with the color now preset addTriangle(triLayer,posX,posY,aX,aY,bX,bY) -- flip the alpha between 1 and 0 for the next segment alpha = 0.5 - alpha end -- Create the outer stroke which should be a negative value so it overlaps the triangle facets setNextStrokeColor(circleLayer,0.5,0.5,1,strokeAndCenterAlpha) setNextFillColor(circleLayer,0,0,0,0) setNextStrokeWidth(circleLayer,stroke) addCircle(circleLayer,posX,posY,radius) -- Create the inner stroke and fill to create the "hole" in the center at 1/4th of the radius. setNextStrokeColor(circleLayer,0.5,0.5,1,strokeAndCenterAlpha) setNextFillColor(circleLayer,0,0,0,strokeAndCenterAlpha) setNextStrokeWidth(circleLayer,stroke) addCircle(circleLayer,posX,posY,radius/4) else -- Create a transparent dot to indicate its position while its hidden. setNextFillColor(circleLayer,0.5,0.5,1,0.2) addCircle(circleLayer,posX,posY,radius/4) end end -- Draw three examples -- Example 1 (large one on left) with transparency to demonstrate how the circular shape was achieved with the triangles. drawRadialMenu(resolutionX/3,centerY,200,6,false,0.5) -- Example 2 (Top right) Does not hide, shows the result without transparency drawRadialMenu((resolutionX/3)*2,resolutionY/3,100,8,false) -- Example 3 (Bottom right) Remains hidden and un-rendered until the cursor enters its area, reducing render cost by ~120000 while hidden. drawRadialMenu((resolutionX/3)*2,(resolutionY/3)*2,80,36) -- render cost profiler if true then local layer = createLayer() local font = loadFont('Play-Bold', 14) setNextFillColor(layer, 1, 1, 1, 1) addText(layer, font, string.format('render cost : %d / %d', getRenderCost(), getRenderCostMax()), 8, 16) end -- Request a refresh requestAnimationFrame(1)  
     
    Text positioning Demonstration example of the various alignment points for text using Font Metrics and Text Align.
    local fontName = 'Play' local fontSize = 32 local font = loadFont(fontName, fontSize) local fontDebug = loadFont('FiraMono', 12) local back = createLayer() local layer = createLayer() local rx, ry = getResolution() local ascender, descender = getFontMetrics(font) local text = "??" frame = (frame or 0) + 1 setDefaultFillColor(layer, Shape_Text, 1, 1, 1, 1) setDefaultFillColor(layer, Shape_Circle, 1, 0, 0, 1) function drawTextBox (x, y, alignH, alignV) local w, h, ascent, descent = getTextBounds(font, text) if alignH == AlignH_Left then x = x elseif alignH == AlignH_Center then x = x - w/2 elseif alignH == AlignH_Right then x = x - w end if alignV == AlignV_Ascender then y = y h = ascender - descender elseif alignV == AlignV_Top then y = y elseif alignV == AlignV_Middle then h = ascender - descender y = y - h/2 elseif alignV == AlignV_Baseline then y = y - ascent elseif alignV == AlignV_Bottom then y = y - h elseif alignV == AlignV_Descender then h = ascender - descender y = y - h end setNextFillColor(back, 0.1, 0.5, 1.0, 1.0) setNextStrokeWidth(back, 1) addBox(back, x, y, w, h) end function main () if frame % 2 == 0 then text = "acemnor " else text = "aCjÅkö[]|!r " end -- display debug info about font if true then local infoLines = { string.format(" font : %s", fontName), string.format(" size : %d", fontSize), string.format(" ascender : %d", ascender), string.format("descender : %d", descender), } for i, line in ipairs(infoLines) do setNextTextAlign(layer, AlignH_Left, AlignV_Top) addText(layer, fontDebug, line, 16, 16 + (i - 1) * 12) end end local alignHs = { 'AlignH_Left', 'AlignH_Center', 'AlignH_Right', } local alignVs = { 'AlignV_Ascender', 'AlignV_Top', 'AlignV_Middle', 'AlignV_Baseline', 'AlignV_Bottom', 'AlignV_Descender', } -- draw text boxes with various alignments local sx = 300 local sy = 80 local x0 = rx/2 - sx * (#alignHs-1)/2 local y0 = ry/2 - sy * (#alignVs-1)/2 for y, alignVName in ipairs(alignVs) do for x, alignHName in ipairs(alignHs) do local px = x0 + sx * (x - 1) local py = y0 + sy * (y - 1) local alignV = _ENV[alignVName] local alignH = _ENV[alignHName] drawTextBox(px, py, alignH, alignV) setNextTextAlign(layer, alignH, alignV) addText(layer, font, text, px, py) addCircle(layer, px, py, 3) end end -- draw AlignH labels for x, alignH in ipairs(alignHs) do setNextTextAlign(layer, _ENV[alignH], AlignV_Descender) setNextFillColor(layer, 0.5, 0.5, 0.5, 1) addText(layer, fontDebug, alignH, x0 + (x - 1)*sx, y0 - 16) end -- draw AlignV labels for y, alignV in ipairs(alignVs) do setNextTextAlign(layer, AlignH_Right, _ENV[alignV]) setNextFillColor(layer, 0.5, 0.5, 0.5, 1) addText(layer, fontDebug, alignV, x0 - 16, y0 + (y - 1)*sy) end end if not isFontLoaded(font) then requestAnimationFrame(10) else main() requestAnimationFrame(120) end  

  11. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Sample Scripts (Basic)
    The following scripts are intended to minimally demonstrate basic usage of render script.
     
    Checkerboard Creates a simple checkerboard pattern by covering the screen with rectangles of alternating color. 
    local resX, resY = getResolution() local tilesX, tilesY = 32, 16 local tileW, tileH = resX / tilesX, resY / tilesY local layer = createLayer() for y = 0, tilesY - 1 do local color = y % 2 for x = 0, tilesX - 1 do if color == 0 then setNextFillColor(layer, 1, 1, 1, 1) else setNextFillColor(layer, 0, 0, 0, 1) end addBox(layer, x * tileW, y * tileH, tileW, tileH) color = (color + 1) % 2 end end  

     
    Simple Text Draws some lines of text. 
    local lines = { "Lua screen units are great!", "They can render lots of contents...", "even really smooth, complex animations!", "They're very fast.", "They're not as easy to use as HTML,", "...but once you get the hang of them...", "you'll be unstoppable.", } local fontSize = 32 local font = loadFont('Play', fontSize) local layer = createLayer() local y = fontSize + 16 for i, line in ipairs(lines) do addText(layer, font, line, 16, y) y = y + fontSize + 2 end  

     
    Bouncy Ball Demonstrates usage of both one-time initialization for local persistent data, as well as requestAnimationFrame(), to draw a moving ball that bounces off the edges of the screen.
    local resX, resY = getResolution() local dt = 100 * getDeltaTime() -- initialize ball position once if not init then init = true ball = {} ball.x = resX * math.random() ball.y = resY * math.random() ball.vx = 2 * math.random() - 1 ball.vy = 2 * math.random() - 1 ball.radius = math.min(resX, resY) / 8 end -- animate ball position ball.x = ball.x + ball.vx * dt ball.y = ball.y + ball.vy * dt if ball.x < ball.radius or ball.x > (resX-ball.radius) then ball.x = ball.x - ball.vx * dt ball.vx = -ball.vx end if ball.y < ball.radius or ball.y > (resY-ball.radius) then ball.y = ball.y - ball.vy * dt ball.vy = -ball.vy end -- render local layer = createLayer() addCircle(layer, ball.x, ball.y, ball.radius) requestAnimationFrame(1)  
     
     
  12. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Technical Details
    How to use Properties Different shapes support different properties that can be set to alter various aspects of the rendering of the shape. The current properties are: 
    fillColor - the interior color of the shape 
    rotation - the angular orientation of the shape about its center 
    shadow - the shadow of the shape 
    strokeColor - the color the border around the shape 
    strokeWidth - the size, in pixels of the border around the shape; negative values produce an interior border, while positive values produce an exterior border 
    The current API design requires that these properties are set before adding the shape, so that, for example, rendering a red rectangle looks as follows: 
    local layer = createLayer() setNextFillColor(layer, 1, 0, 0, 1) -- red with 100% alpha addBox(layer, 0, 0, 16, 16)  
    When you set a property, it does not persist. Thus, to render many red rectangles, you must call setNextFillColor each time. 

    Note that every shape rendering command consumes all properties for that layer even if the shape does not support them. 
    This is for safety and to ensure that the global state does not impact your script in confusing ways. 
    For example, although images do not currently support a stroke color or width, if you call setNextStrokeWidth before addImage, the stroke width will be reset to the default after the call to addImage. 
    Consult the above API specifications to learn which properties are supported for which shapes. 
     
    Shape Render Order When you need explicit control over the top-to-bottom ordering of rendered elements, you should use layers.
    As stated in the createLayer() API documentation, each layer that is created within a script will be rendered on top of each previous layer, such that the first layer created appears at the bottom, while the last layer created appears at the top.
     
    Shapes that live on the same layer do not offer as much control. Among the same type of shape, instances rendered later will appear on top of those rendered before. So if you add two boxes to a layer, the last box added will appear on top.
     
    However, the situation is more complex when mixing different shapes. For efficiency, rendering happens in batches, so that all instances of a given shape on the same layer are drawn at the same time.
     
    This means, in general, that all instances of one shape will appear below or above all instances of other shapes, regardless of the relative order in which they were added to the layer.
     
    Currently, the ordering is as follows, from top to bottom:
    addText() -- Top addQuad() addTriangle() addLine() addCircle() addBoxRounded() addBox() addImage() -- Bottom  
    Thus, all boxes will always render below all circles on the same layer, and text on the same layer will appear on top of both.
    It is not possible to control this behavior, nor is it a good idea to rely on it, as it is subject to change in the future.
    If you need to rely on something appearing in front of something else, you should probably use multiple layers. (There is currently a maximum of 8 layers)
     
    Render Cost Limitations Since render script is intended to solve screen unit performance problems, we impose relatively harsh restrictions on content compared to HTML.  
    This does not mean you won’t be able to create amazing, detailed, high-framerate screen contents – it simply means that you’ll need to be aware of the budgeting mechanism. 

    Any render script call that draws a shape (box, circle, line, text..) adds to a hidden ‘cost’ metric, that consumes some of the screen’s total rendering budget.
    The details of how this cost is computed are beyond the scope of this document, and it is likely that they will undergo changes in the future as we learn how to best optimize this technology. Roughly-speaking, however, the render cost incurred by any shape is proportional to the screen-space area of the shape, plus a constant factor.
     
    This means that a box of dimension 16 x 16 consumes roughly four times as much render cost as a box of 8 x 8 . This is fairly intuitive when you realize that the number of pixels filled by the larger box is,  indeed, four times that of the smaller box. Note that four times is not exact, as we neglected to consider the constant factor, but it is close enough. 

    For most render scripts, it is unlikely that the maximum cost will ever be exceeded, so most users probably don’t need to worry too much about this mechanism. However, drawing lots of large text or lots of large, overlapping images, may cause you to exceed the budget. 

    If you wish to dig deeply into this mechanism, you can do so using the built-in API calls getRenderCost() and getRenderCostMax().
    getRenderCost() can be called at any point during a render script to see how much all the contents added so far costs.
    You can use this function to profile the specific cost of specific shapes, if you wish. 
     
    It is easy enough to draw a render cost profiler on top of your screen to help you understand this mechanism. You can add this snippet at the end of your script to do so (you may need to adjust the text’s fill color if your background is a light colour):
    -- render cost profiler if true then local layer = createLayer() local font = loadFont('Play-Bold', 14) setNextFillColor(layer, 1, 1, 1, 1) addText(layer, font, string.format('render cost : %d / %d', getRenderCost(), getRenderCostMax()), 8, 16) end  
    Coordinate Space All render script coordinates are in screen pixels, ranging from (0, 0) at the top-left of the screen, to (width, height) at the bottom-right.
    The width and height of the screen in pixels can be retrieved by calling getResolution().
    For maximal robustness, and the ability to fit different aspect ratios, scripts should be written so as to adapt to the resolution using getResolution().
     
    Control Units and Synchronization It is important to know that screen unit Lua runs locally to the player viewing it, this means that dynamic content seen for one player will likely not match what is seen by another player as the two will often be in differing states. For example the bouncy ball example below will not display the ball in the same location for both players viewing the same screen. To achieve similar results for multiple players you can route communication to a screens getInput() which can be interacted with from the control unit side using screen.setScriptInput(). By setting the screen.setScreenInput() from a programming board, we set a string variable (getInput()) for all viewers of the screen unit that will synchronize about once per second. (This is already a considerable amount faster than HTML does currently.)
     
    Similarly a screen unit can set an output string variable using setOutput() which can then be read by the programming board using screen.getScriptOutput() to receive data back from the screen. It is important to note that a screens output field is local only, this is a technical limitation for synchronization reasons. 
     
    The limits of these fields are currently 1024 characters at any given time.
     
    Using JSON strings it is possible to send back and forth packets of data to and from the screens for the player running both the control unit and the screen.
    All other players are limited to the input field only, this allows for one player to "control" the interaction and data exchange between the programming board and screen unit. And all others to use and operate the screen using the screen Lua.

    An example of how we see this working, would be that the pilot of a ship controls it from the control seat with a screen in front of them which allows them to see various readouts from their ship. Using a mouse they are able to click various interface parts to use the screen to control aspects of their ship. These are relayed back to their control unit using setOutput(). In the meantime fellow crew members are still able to see those readouts animated and moving in real time from their perspective, even if the data they are receiving is only synchronized once per second.
     
  13. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Render Script Lua API (PTS v1.1)
    Shapes Squares
    addBox(layer, x, y, width, height) Add a rectangle to the given layer with top-left corner (x, y) and dimensions width x height. 
    Supported properties: fillColor, rotation, shadow, strokeColor, strokeWidth
     
     Rounded Squares (New!)
    addBoxRounded(layer, x, y, width, height, radius) Add a rectangle to the given layer with top-left corner (x, y) and dimensions width x height with each corner rounded to radius. 
    Supported properties: fillColor, rotation, shadow, strokeColor, strokeWidth
     
     Circles
    addCircle(layer, x, y, radius) Add a circle to the given layer with center (x, y) and radius radius. 
    Supported properties: fillColor, shadow, strokeColor, strokeWidth
     
     Images
    addImage(layer, image, x, y, width, height) Add image reference to layer as a rectangle with top-left corner (x, y) and dimensions width x height. 
    Supported properties: fillColor, rotation 
     
     Lines
    addLine(layer, x1, y1, x2, y2) Add a line to layer from (x1, y1) to (x2, y2). 
    Supported properties: rotation, shadow, strokeColor, strokeWidth
     
     Quadrilaterals
    addQuad(layer, x1, y1, x2, y2, x3, y3, x4, y4) Add a quadrilateral to the given layer with vertices (x1, y1), (x2, y2), (x3, y3), (x4, y4). 
    Supported properties: fillColor, rotation, shadow, strokeColor, strokeWidth 
     
     Text
    addText(layer, font, text, x, y) Add text to layer using font reference, with top-left baseline starting at (x, y).
    Note that each glyph in text counts as one shape toward the total rendered shape limit. 
    Supported properties: fillColor 
     
     Triangles
    addTriangle(layer, x1, y1, x2, y2, x3, y3) Add a triangle to the given layer with vertices (x1, y1), (x2, y2), (x3, y3). 
    Supported properties: fillColor, rotation, shadow, strokeColor, strokeWidth 
     
    Layers Creating a new layer
    createLayer() -> int Create a new layer and return a handle to it that can be used by subsequent calls to the above add shapes. 
    Layers are rendered in the order in which they are created by the script, such that all shapes on layer N+1 will appear on top of layer N. 
    This results in the first created layer being the in the background and the last created layer will be in the foreground.
    Layers now have a fixed render cost and are no longer limited to 8.
     
     Set Screen Background (New!)
    setBackgroundColor(r, g, b) Set the background color of the screen as red (r), green (g), blue (b) in the range [0, 1]
     
    Screen State Functions Get Cursor Coordinates
    getCursor() -> int, int Return the screen location that is currently raycasted by the player in screen pixel coordinates as a (x, y) tuple. 
    Returns (-1, -1) if the current raycasted location is not inside the screen. 
     
    Get Cursor Down State (New!)
    getCursorDown() -> boolean Return true if the mouse cursor is currently pressed down on the screen, false otherwise. 
    Retains its state if dragged out of the screen.
     
    Get Cursor Pressed (New!)
    getCursorPressed() -> boolean Return true if the mouse button changed from being released to being pressed at any point since the last update.
    Note that it is possible for both getCursorPressed() and getCursorReleased() to return true in the same script execution, if the mouse button was both pressed and released since the last execution. 
     
    Get Cursor Released (New!)
    getCursorReleased() -> boolean Return true if the mouse button changed from being pressed to being released at any point since the last update.
    Note that it is possible for both getCursorPressed() and getCursorReleased() to return true in the same script execution, if the mouse button was both pressed and released since the last execution. 
     
    Get Delta Time
    getDeltaTime() -> float Return the time, in seconds, since the screen was last updated. Useful for timing-based animations, since screens are not guaranteed to be updated at any specific time interval, it is more reliable to update animations based on this timer than based on a frame counter.
     
     Get Render Cost
    getRenderCost() -> int Return the current render cost of the script thus far, used to profile the performance of a screen.
    This can be used to abort further render instructions when close to the render maximum preventing the screen from shutting down.
     
     Get Maximum Render Cost
    getRenderCostMax() -> int Return the maximum render cost limit. When a script exceeds this render cost in one execution, an error will be thrown and the contents will fail  to render. 
     
     Get Resolution
    getResolution() -> int, int Return the current viewport resolution as a (width, height) tuple.
     
     Log Message to Lua Chat (New!)
    logMessage(message) Write message to the Lua chat if the output checkbox is checked.
     
    Loading References Loading Images
    loadImage(path) -> int? Return an image handle that can be used with addImage. If the image is not yet loaded, a sentinel value will be returned that will cause addImage to fail silently, so that the rendered image will not appear until it is loaded. Only images that have gone through image validation are available.
     
    Loading Fonts
    loadFont(name, size) -> int? Return a font handle that can be used with addText. The font size is size vertical pixels. If the font is not yet loaded, a sentinel value will be returned that will cause addText to fail silently, so that the rendered text will not appear until the font is loaded.  A maximum of 8 fonts can be loaded for each render.
    Name must be one of the following currently-available fonts:
    FiraMono FiraMono-Bold Montserrat Montserrat-Light Montserrat-Bold Play Play-Bold RefrigeratorDeluxe RefrigeratorDeluxe-Light RobotoCondensed RobotoMono RobotoMono-Bold New: We added monospaced fonts
     
     Is Image Loaded (New!)
    isImageLoaded(imageHandle) -> bool Returns true if the given imageHandle is loaded.
     
     Get Text Bounds (New!)
    getTextBounds(font,text) -> float, float Compute and return the bounding box width and height of the given text in the given font as a (width, height) tuple.
     
     Get Font Metrics (New!)
    getFontMetrics(font) -> float, float Compute and return the ascender and descender height of given font.
     
    Animation  Request refresh in amount of frames
    requestAnimationFrame(frames) Notify the screen manager that this screen should be redrawn in frames frames.
    A screen that requires highly-fluid animations should thus call requestAnimationFrame(1) before it returns. 
    Usage of this function has an obvious and significant performance impact on the screen unit system.
    Scripts should try to request updates as infrequently as possible for their application. 
    A screen with unchanging (static) contents should not call this function at all. 
     
    Properties Defaults (New!) Shape Types (New!)
    For shapeType defaults the following constants are valid:
    Shape_Box Shape_BoxRounded Shape_Circle Shape_Image Shape_Line Shape_Polygon Shape_Text  
     Set Default Fill Color (New!)
    setDefaultFillColor(layer, shapeType, r, g, b, a) Set the default fill color for all shapeType on layer. 
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the fillColor property.
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Default Rotation in Radians (New!)
    setDefaultRotation(layer, shapeType, radians) Set the default rotation for all shapeType on layer. 
    Rotation is specified in CCW radians radians. 
    Has no effect on shapes that don't support the rotation property. 
     
     Set Default Shadow (New!)
    setDefaultShadow(layer, shapeType, radius, r, g, b, a) Set the default shadow for all shapeType on layer with size radius.
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the shadow property. 
     
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Default Stroke Color (New!)
    setDefaultStrokeColor(layer, shapeType, r, g, b, a) Set the default stroke color for all shapeType on layer. 
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the strokeColor property. 
     
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Default Stroke Width (New!)
    setDefaultStrokeWidth(layer, shapeType, width) Set the default stroke width for all shapeType on layer. Width is specified in pixels. 
    Positive values produce an outer stroke, while negative values produce an inner stroke. 
    Has no effect on shapes that don't support the strokeWidth property. 
     
    Properties Set Fill Color for the next shape
    setNextFillColor(layer, r, g, b, a) Set the fill color of the next rendered shape on layer. 
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the fillColor property.
     
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Rotation for the next shape in Radians
    setNextRotation(layer, radians) Set the rotation of the next rendered shape on layer. 
    Rotation is specified in CCW radians radians. 
    Has no effect on shapes that don't support the rotation property. 
     
     Set Rotation for the next shape in Degrees
    setNextRotationDegrees(layer, degrees) Set the rotation of the next rendered shape on layer. 
    Rotation is specified in CCW degrees degrees. 
    Has no effect on shapes that don't support the rotation property. 
     
     Set Shadow for the next shape (New!)
    setNextShadow(layer, radius, r, g, b, a) Set the shadow of the next rendered shape on layer with size radius.
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the shadow property. 
     
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Stroke Color for the next shape
    setNextStrokeColor(layer, r, g, b, a) Set the stroke color of the next rendered shape on layer. 
    Red (r), green (g), blue (b), and alpha (a) components are specified, respectively, in the range [0, 1].  
    Has no effect on shapes that don't support the strokeColor property. 
     
    For users who are used to using colors in the range [0, 255] they can simply divide their color by 255 to achieve a [0,1] range.
    For example: setDefaultFillColor(layer,shapeType, 50/255, 150/255 , 200/255, 255/255)
     
     Set Stroke Width for the next shape
    setNextStrokeWidth(layer, width) Set the stroke width of the next rendered shape on layer. Width is specified in pixels. 
    Positive values produce an outer stroke, while negative values produce an inner stroke. 
    Has no effect on shapes that don't support the strokeWidth property. 
     
     Set Text Alignment for the next shape (New!)
    setNextTextAlign(layer, alignH, alignV) Set the next text alignment for the next rendered shape on layer.
    alignH controls the horizontal alignment of a text shape relative to the draw coordinates.
    alignV controls the vertical alignment of a text shape relative to the draw coordinates.
     
    alignH must be one of the following built-in constants:
    AlignH_Left AlignH_Center AlignH_Right  
    alignV must be one of the following built-in constants:
    AlignV_Ascender AlignV_Baseline AlignV_Top AlignV_Middle AlignV_Bottom AlignV_Descender  
    Note that there is a subtle difference between AlignV_Ascender/AlignV_Descender and AlignV_Top/AlignV_Bottom: the ascender and descender alignment modes anchor a text string to a global top/bottom position of the font, while the top and bottom alignment modes anchor a text string relative to its own bounding box. Thus, while top/bottom are useful for aligning individual text strings with high precision, they depend on the contents of the text string that is rendered. On the other hand, ascender/descender align text in such a way that the alignment will not change depending on the text string. The correct choice will depend on your specific use case and needs.
     
    Control Unit Interaction Get Input (New!)
    getInput() -> string Return a string of input data (or an empty string, if no input has been set) that can be set via a control unit with the screen unit API function screen.setScriptInput(inputString).
     
     
    Set Output (New!)
    setOutput(outputString) Set the script’s output string to outputString, which can be retrieved via a control unit with the screen unit API function screen.getScriptOutput().
     
  14. Like
    Kurock reacted to NQ-Deckard in Lua Screen Units API and Instructions (Updated: 19/07/2021)   
    Introduction
    Updated: 19/07/2021
     
    Lua screen units are a new technology aimed at solving the performance issues associated with our current implementation of HTML on screens, while bringing a new feature allowing complex animations with thousands of graphical elements.
     
    Alongside of setting HTML on screen units, it is now possible to set a render script on screen units instead.
    Render scripts are Lua scripts residing inside the screen units themselves that provide rendering instructions for screen units. 

    Each screen also has a maximum render cost to limit the impact a single screen can have on users that are able to see it, this is important as the impact of a screen is not limited to only the creator of the screen's render script or content but also other nearby players.
     
    Inside the render script the content is built up using layers which are rendered on top of each other in order of creation at the end of the render script.
     
    After creating a layer, it is then possible to optionally specify a few parameters for the next shape you intend to add to that layer. Such as stroke color, fill color, stroke width and rotation depending on what's available for that shape. Once those parameters are defined we then use "add" functions that draw shapes on those layers using those parameters.
     
    If the screen is intended to be a static display there is no further requirement, if you wish for the screen to be animated we can then add:
    requestAnimationFrame(<frames>) Using this at the end of the script which will aim to refresh the screen after <frames> amount of frames.
    This should only be used when required for animated content. For performance reasons we recommend not calling this function if no animations are intended to be run.
     
    In future we will further expand on this feature to provide more functionality and synergy with control units allowing for far more efficient and responsive screens in the game.
     
    Getting Started
    To start creating renderscripts we start by right clicking the screen and using the context menu option Edit Content (Ctrl+L) to start editing the screen.

     
    This will bring up a new Lua & HTML editor window which will allow you to switch between Lua (Render Script) and HTML display modes.

     
    In Lua mode it will provide error reporting for the render script in the lower portion of the screen, and to aid in debugging processes there is a check box which will allow you to write to the Lua Channel using logMessage("message") inside the renderscript. This is logging method is local only to you if you have the check box enabled and will be automatically be disabled when the screen is unloaded.
     
    Drawing screen contents with render scripts consists of creating one or more layers, on which shapes will be drawn, and then adding shapes to these layers to populate the screen contents. The API currently supports relatively basic shapes: rectangles, circles, lines, triangles, quadrilaterals along with images and text. In the future, we will consider adding more complex shapes as we receive your feedback. 
     
    Take a look at this basic ‘hello world’ render script: 
    local layer = createLayer() -- Creates a new layer local rx, ry = getResolution() -- Gets the resolution of the screen local font = loadFont("Play", 20) -- Loads the "Play" font at size 20 setNextFillColor(layer, 1, 0, 0, 1) -- Prepares the fill color (Red,Green,Blue,Alpha) for the next shape to be drawn on the layer addBox(layer, rx/4, ry/4, rx/2, ry/2) -- Adds a box shape to layer, starting a 1/4th into the screen, with half the screens width and height addText(layer, font, "Hello world!", rx/3, ry/2) -- Adds a text shape without any properties to layer, using the font, one third from the left, and half way down.  
    This script renders a red rectangle that is half the size of the screen, in the center of the screen, and "Hello World!" on top. Simple! 

     


  15. Like
    Kurock got a reaction from Wolfram in YARSOS #1   
    YARSOS aka Yet Another Repair System Overhaul Suggestion

    Preface:
    No one likes the elements breaking on X lives system. The factory owners want it to be 1 life (because that's quanta in the bank eh?) and other players don't want it at all.  
    @Volkier has been fighting the good fight and while the "multiple lives system" was suggested by (the factory owner) players and implemented, the great ideas of Volkier go unacknowledged.  So the hopes of this suggestion getting more than a passing glance is low indeed.
     
    The suggestion:
    How about instead of lives for elements, have a maintenance meter that degrades randomly with element use. If the maintenance meter reaches 0 then there is a chance elements will be destroyed *while in use*. HP loss from crashing and PvP makes the maintenance meter take a big dive.  An element at 0 hp means it's broken and unusable (as usual). Scrap repairs hp as normal. (hp is so gamey... calling it "integrity" would be better especially since that is a trait sorely lacking in some). Just repairing an element with scrap will make it functional as normal however if it is *used* (just to reiterate) there is a random chance it would be destroyed permanently.  Finally, maintenance takes enough time (or maybe the construct needs to be stationary, or maybe you have a better suggestion so comment below) that it can’t be done in combat and in if the maintenance meter has degraded enough, it may require *parts* to bring the meter up again. A fully maintained element is the same as a completely new element.
     
    But why?
     - It's far less grievous if the *permanent* destruction of elements is under the players control: aka a choice led to that outcome.  Whether that is going into a PvP zone or deciding to not do maintenance.  Hopping landing gear leading to entire ship exploding should not be part of this equation at all.
    - Why "on use"? - computation is off-loaded onto the client in DU... mostly... except for factories.  That is why the batches of factories had to be increased as well as the schematic nerf.  Can't have all players DOSing the server with millions of factories now can we?  Anyway, what uses items but players themselves.  Hence on use.
     
    What does this mean?
    The side effect of this kind of system would be:
     - Even elements on static/space constructs would not be exempt from needing maintenance.  Those doors and buttons will need to be oiled and kept in working order... or just them break and buy replacements.  More choice for players. And factory owners should be happy right?  (no they will never be happy until your whole ship has to be replaced after moving 5 meters)
     - Mechanics! Imagine actual mechanic shops.  Job creation right there. (throw in some maintenance specific talents and we are golden)
     - Accidents (especially those caused by glitches and pop-in towers) are far less infuriating.  There will always be glitches, that is the nature of the beast. An entire ship should not need to be replaced because the (default) flight script decided to get stuck in a perpetual left turn.
    - More of a market for parts.  Will that make factory owners happy?  My guess is no ?
    - Elements that aren't used directly won't need maintenance... this covers those decorative items:  Leave them looking good without being a pain. No need to remember to water those plants.
    - PvP and salvaging rewards...  fully functional elements: with some parts and scrap they are good as new.
    - Industry needs maintenance.  You want to keep your expensive factory working?  Keep it maintained.  The reality is the initial cost isn't the problem, its keeping them going that is the real trick. No fire and forget factories.
     
    Future improvement suggestions:
     - Talents - Make maintenance fun with a talent tree for your mechanic
     - Tuning - alter you engine to have less performance but a larger maintenance meter.  Or the opposite.  
     - Efficiency - Have engines become less efficient as they are less maintained... requiring a visit to your local mechanic for a tune up.
     - Recyclers - Take those destroyed elements and recycle them into parts which could be used to maintain the not-destroyed elements.
     
    Element lives don’t matter... I demand maintenance.

    TLDR: Random destruction chance with mitigation mechanics (maintenance meter) gives players a chance against mishaps. 
  16. Like
    Kurock got a reaction from ParagonExploiter in The day the planets ran dry   
    The day the planets ran dry
    Memoir of a miner
     
     
    <Recording started, datalog 2021112315311321…>
     
    Ahem. This thing on? Good. I’ve been mining since… Bah. Delete the recording before this. Damn technology, anticipating needs I don’t have. Just do as I say and not...
     
    <Recording ended.>
     
     
    <Recording started, datalog 2021112315313512…>
     
    I've been mining since the day I stepped off the Arkship. It was boring but I never got bored. That’s an old mining joke. Along with the one where we are all old enough to drink at bars. With the bad jokes out of the way I can get down and dirty.
     
    Back on Earth we were lucky to have a jackhammer or a sonic destabilizer, but right off the Arkship we were giving nanoformers. Amazing bits of technology, able to detect nearby concentration of ores, vaporize the ground to get to the ore and pack the ores neatly into a container. The abuse of nano-fields and Calabi-Yau space at its finest.
     
    Then came the territory scanners. Able to scan a square kilometer and give a read out of the ores in the ground. They were prospecting made easy. The big business scanner-heads went out in their tri-scanner ships and deployed arrays of tri-scanners to be able to prospect on upwards of twenty-four square kilometers at the same time. But before all that it was just me and my trusty ship with a territory scanner bolted to its side. That's all a miner really needed. 
     
    Then Aphelia, the Arkships AI, made the Arkship fire its big beam deep into the planet. That can’t be good for the planet.  The Novark spat small ore rocks all over the planet's surface. Must have done it to all the planets and moons of the Helios system, because the rocks were everywhere. But no self respecting miner would waste time picking those up. The yields were simply too small, compared to the expansive nodes in the ground. And then there were the motherloads: the mega-nodes. Where one ore node held thousands of liters of ore, the mega-nodes held millions. I personally found and cleared out more mega-nodes than you can shake a stick of carbon at. Those were good times.
     
    But just because a territory scanner said there was ore in the ground, it didn’t say where it was. It was still an art to find those elusive ore nodes, even the megas. My preferred method was walking the compass. Find your level, walk north or south whichever makes the blip get closer. Then east west. Once the blip gets no closer, you know the ore is right above or below you. But never dig straight up or down. How are they gonna get back up?  What if there is a giant desert scorpion or worse… ecologists.  But some did dig straight down. The well-diggers or shafters as I called them, wanted to get down to where the ore was quickly. And they didn’t waste time digging back to the surface either. Instead they would just kill themselves, trusting their resurrection nodes on the surface wouldn’t malfunction. But sometimes they did. That’s why the shafters were either desperate or psychopaths.
     
    The proper way of mining was always just the miner and their trusty tool. No floating mining chairs, auto-mappers or follow-me container bots. Just you and the dark. And occasionally a miner would be treated to the midnight sun: the hallucinations of sunlight seemingly streaming through the surrounding rocks to illuminate the tunnels or caverns they were traversing. A beautiful sight but also a warning you might have been mining for too long. Maybe all miners were a little touched in the head.
     
    <Evacuation order received. Ignored.>
     
    If you have been paying attention, you will have noticed I kept saying “was”. Aphelia predicted it would take decades to empty the planets of a fraction of their precious ores, but it’s been a single year and they are all gone. We dug them all out. The purer ores anyway. But humanity always needs more resources. So Aphelia made mining units available, each capable of extracting the diffuse ores from the ground. She gave molecular quantum tunneling or some such nonsense as the reason. But I think it's something else: Nanites.

    A high energy beam is used to inject nanites deep into the ground. They suffuse the surrounding rock and gravel through Brownian motion. Each nanite is specially calibrated to search for a specific group of molecules and, once found, they construct nanotubes to pipe their payload towards where they are densest, eventually up the beam. The demonstrations that show diminishing yields within 48 hours seems to collaborate this theory.
     
    Either way, the side effect of preparing the ground is the liquification of the rocks that then fill in most of the caves and tunnels already dug. Some might think it’s like turning the rock to magma but there is no heat involved. Just a dark grey sludge that quickly settles back into natural rock again. The mining shafts will be the first to go. Followed by the engulfing of any underground structures. You would think that a solid box of iron would prevent the rock from flowing in. No. This ‘quantum’ rock that flows right through everything contained only by the gravity of the planets. And moons. The moons will be receiving the same treatment.
     
    <Evacuation order received. Ignored.>
     
    Us Noveans that have served for years as extraction experts have been relegated to asteroid harvesting duty. But Bert, they say to me, Bert, Mining is mining, right? No, I say. Besides micro-gravity leaching of muscle and bone, Asteroids are the turds of the Helios system, attracting pirates like the flies they are. Asteroids just don’t have the same, heh, gravitas of planets. 
    What if this is how the dinosaur fossils were made: Subterranean creatures caught in a planetwide rock liquefaction event. Heh. That’s a thought.
     
    <Warning. Warning. Approaching anomaly.>
     
    Ah. Here comes the grey ooze...
     
    <Recording ended.> 
                - Contents of databank found 6km below the surface of an icy planet.
     
     
    Want more? www.spaceshipdrama.com
  17. Like
    Kurock got a reaction from Scavenger in Make the glowy rocks stop glowing   
    I like immersion.  DU's asthetic is supposed to be scifi but more on the side of realism.  The glowing rocks simply arn't realistic.  Now there are certain rocks that do glow under certain lights but not nearly to the extent depicted in DU 
     

     
    Idea: These rocks should look like normal rocks except when you have the harvest tool equipped then it they look like this.  This makes them easy to find when you want to harvest but doesnt look terrible when you don't.
     
    What say you?
  18. Like
    Kurock got a reaction from huschhusch in Make the glowy rocks stop glowing   
    I like immersion.  DU's asthetic is supposed to be scifi but more on the side of realism.  The glowing rocks simply arn't realistic.  Now there are certain rocks that do glow under certain lights but not nearly to the extent depicted in DU 
     

     
    Idea: These rocks should look like normal rocks except when you have the harvest tool equipped then it they look like this.  This makes them easy to find when you want to harvest but doesnt look terrible when you don't.
     
    What say you?
  19. Like
    Kurock got a reaction from StarWuz in The day the planets ran dry   
    The day the planets ran dry
    Memoir of a miner
     
     
    <Recording started, datalog 2021112315311321…>
     
    Ahem. This thing on? Good. I’ve been mining since… Bah. Delete the recording before this. Damn technology, anticipating needs I don’t have. Just do as I say and not...
     
    <Recording ended.>
     
     
    <Recording started, datalog 2021112315313512…>
     
    I've been mining since the day I stepped off the Arkship. It was boring but I never got bored. That’s an old mining joke. Along with the one where we are all old enough to drink at bars. With the bad jokes out of the way I can get down and dirty.
     
    Back on Earth we were lucky to have a jackhammer or a sonic destabilizer, but right off the Arkship we were giving nanoformers. Amazing bits of technology, able to detect nearby concentration of ores, vaporize the ground to get to the ore and pack the ores neatly into a container. The abuse of nano-fields and Calabi-Yau space at its finest.
     
    Then came the territory scanners. Able to scan a square kilometer and give a read out of the ores in the ground. They were prospecting made easy. The big business scanner-heads went out in their tri-scanner ships and deployed arrays of tri-scanners to be able to prospect on upwards of twenty-four square kilometers at the same time. But before all that it was just me and my trusty ship with a territory scanner bolted to its side. That's all a miner really needed. 
     
    Then Aphelia, the Arkships AI, made the Arkship fire its big beam deep into the planet. That can’t be good for the planet.  The Novark spat small ore rocks all over the planet's surface. Must have done it to all the planets and moons of the Helios system, because the rocks were everywhere. But no self respecting miner would waste time picking those up. The yields were simply too small, compared to the expansive nodes in the ground. And then there were the motherloads: the mega-nodes. Where one ore node held thousands of liters of ore, the mega-nodes held millions. I personally found and cleared out more mega-nodes than you can shake a stick of carbon at. Those were good times.
     
    But just because a territory scanner said there was ore in the ground, it didn’t say where it was. It was still an art to find those elusive ore nodes, even the megas. My preferred method was walking the compass. Find your level, walk north or south whichever makes the blip get closer. Then east west. Once the blip gets no closer, you know the ore is right above or below you. But never dig straight up or down. How are they gonna get back up?  What if there is a giant desert scorpion or worse… ecologists.  But some did dig straight down. The well-diggers or shafters as I called them, wanted to get down to where the ore was quickly. And they didn’t waste time digging back to the surface either. Instead they would just kill themselves, trusting their resurrection nodes on the surface wouldn’t malfunction. But sometimes they did. That’s why the shafters were either desperate or psychopaths.
     
    The proper way of mining was always just the miner and their trusty tool. No floating mining chairs, auto-mappers or follow-me container bots. Just you and the dark. And occasionally a miner would be treated to the midnight sun: the hallucinations of sunlight seemingly streaming through the surrounding rocks to illuminate the tunnels or caverns they were traversing. A beautiful sight but also a warning you might have been mining for too long. Maybe all miners were a little touched in the head.
     
    <Evacuation order received. Ignored.>
     
    If you have been paying attention, you will have noticed I kept saying “was”. Aphelia predicted it would take decades to empty the planets of a fraction of their precious ores, but it’s been a single year and they are all gone. We dug them all out. The purer ores anyway. But humanity always needs more resources. So Aphelia made mining units available, each capable of extracting the diffuse ores from the ground. She gave molecular quantum tunneling or some such nonsense as the reason. But I think it's something else: Nanites.

    A high energy beam is used to inject nanites deep into the ground. They suffuse the surrounding rock and gravel through Brownian motion. Each nanite is specially calibrated to search for a specific group of molecules and, once found, they construct nanotubes to pipe their payload towards where they are densest, eventually up the beam. The demonstrations that show diminishing yields within 48 hours seems to collaborate this theory.
     
    Either way, the side effect of preparing the ground is the liquification of the rocks that then fill in most of the caves and tunnels already dug. Some might think it’s like turning the rock to magma but there is no heat involved. Just a dark grey sludge that quickly settles back into natural rock again. The mining shafts will be the first to go. Followed by the engulfing of any underground structures. You would think that a solid box of iron would prevent the rock from flowing in. No. This ‘quantum’ rock that flows right through everything contained only by the gravity of the planets. And moons. The moons will be receiving the same treatment.
     
    <Evacuation order received. Ignored.>
     
    Us Noveans that have served for years as extraction experts have been relegated to asteroid harvesting duty. But Bert, they say to me, Bert, Mining is mining, right? No, I say. Besides micro-gravity leaching of muscle and bone, Asteroids are the turds of the Helios system, attracting pirates like the flies they are. Asteroids just don’t have the same, heh, gravitas of planets. 
    What if this is how the dinosaur fossils were made: Subterranean creatures caught in a planetwide rock liquefaction event. Heh. That’s a thought.
     
    <Warning. Warning. Approaching anomaly.>
     
    Ah. Here comes the grey ooze...
     
    <Recording ended.> 
                - Contents of databank found 6km below the surface of an icy planet.
     
     
    Want more? www.spaceshipdrama.com
  20. Like
    Kurock reacted to Mjrlun in Research table and industry revamp idea: Thaumcraft-like progression   
    Look up "Thaumcraft 4 research" and "Thaumonomicon" for reference.

    Tech tree examples ^
     
     
    In Thaumcraft you have a 'tech tree' and in this you can unlock various recipes for items (eg. schematics in DU). In this tech tree it gives a relatively defined path, however, sometimes the triggers to research steps are not clear. The user must unlock precursor items and precursor research point types, or trigger certain events to progress to the next step on the tree. However, the best part about this tree is when you click on an element on the tree there is usually a detailed description of the item (not just the item's tool tip description).
     
    There is also a research table where you can scan various blocks (elements, voxels, honeycomb, and terrain decor in DU's case) for these research points of various types (ex. Terra, Lux, Aqua, etc.), which you can use in the research table to make patterns in a puzzle to unlock the recipe of an item. In DU's case this would translate to getting a schematic in return. Now, this could replace the need to sell schematics by bots, because the required stuff to make these researches could be engineered to only found after exploration (and prospecting). 
     

    Left: List of various research point variants that the user can use for the research puzzle.
    Right: The research puzzle itself, the pre-existing "nodes" must all be connected to each other in some way complete the puzzle.

    These research points in Thaumcraft can be combined, and all but the most elemental are a form of 'recipe tree', where they are made by, or make other research point types. These research points connect in the puzzle to other research points that they either are made of 1 tier down, or make 1 tier up.
     
    To scan there'd probably be talents related to it, as well as a tool to scan the various objects. Items could be further investigated by actually putting  them into the table. All items/objects should be scannable (to return some research points).
     
     
     
    How to replicate a schematic once you "unlock" it at this table? The best way to enrich the replication of schematics is to add a new layer of depth to item scanning. Once you unlock certain parts of the tech tree, it should be possible to re-scan various elements (without telling the player what changed, after all, it's research). The player could then re-play the puzzle mini-game to attain more schematics. This should naturally create a limit on the amount of schematics possible by an individual, due to this budgeting.
     
    Lastly, the research table "puzzle", unlike Thaumcraft, is fundamentally random to some extent. The player will always be presented with the same "research stems" in the puzzle, however, their positions are entirely random, unlike the original, making the replay-ability much higher.
  21. Like
    Kurock reacted to Haunty in Make the glowy rocks stop glowing   
    Also reduce the number of rocks, they are way too dense. Then just give them more liters each.
  22. Like
    Kurock reacted to DecoyGoatBomb in Make the glowy rocks stop glowing   
    Everytime I fly around the game it looks great until I get low enough for the rocks to load in and my eyes burn. I can't emphasize how much these rocks destroy the look of the game. There are so many ways to make this not the case. Like in your pole... why do they not just glow when the tool is activated or use the same green outline mechanic as undergound mining if the desire is to make them standout from the background. Plz change this NQ. 
  23. Like
    Kurock reacted to Squidrew_ in Make the glowy rocks stop glowing   
    100% agree. At the very least, I'd say reduce the brightness to a fraction of what it is now, and maybe reduce the amount of rocks as well. They're so abundant it shouldn't matter if the spawn rate was limited to, say, 1/3rd for example.
     
    They also lessen diversity and uniqueness among planets and moons in my opinion, as having such a large portion of the area around you be covered with these rocks makes all the planets feel just that little bit more the same.
     
    Here's another gorgeous shot just to add:

  24. Like
    Kurock reacted to IvanGrozniy in Make the glowy rocks stop glowing   
    Remove emission from rocks.
    Remove that nebula.
    Leave space alone.
     
    Give @Eternal his landscaping business back. I worry about his commute.
  25. Like
    Kurock got a reaction from W1zard in Make the glowy rocks stop glowing   
    I like immersion.  DU's asthetic is supposed to be scifi but more on the side of realism.  The glowing rocks simply arn't realistic.  Now there are certain rocks that do glow under certain lights but not nearly to the extent depicted in DU 
     

     
    Idea: These rocks should look like normal rocks except when you have the harvest tool equipped then it they look like this.  This makes them easy to find when you want to harvest but doesnt look terrible when you don't.
     
    What say you?
×
×
  • Create New...