local preset = (function() local GROUP, MODE, INTENSITY, GLOW, RADIUS, RED, GREEN, BLUE, FADE_MS, FLAGS, RECORD_SIZE, PREFIX, PAGE_SIZE, create, presetCount, pageKeys, label, select, setMode, setIntensity, setFadeMs, setFullbright, save, selectedKey, currentMode, currentIntensity, currentGlow, currentRadius, currentFadeMs, isEnabled, isFullbright, currentColor GROUP = 0 -- u8 MODE = 1 -- u8, zero-based mode ID INTENSITY = 2 -- u8, whole percent GLOW = 3 -- u8, whole percent RADIUS = 4 -- u8, whole meters RED = 5 -- u8, sRGB GREEN = 6 -- u8, sRGB BLUE = 7 -- u8, sRGB FADE_MS = 8 -- u16, milliseconds FLAGS = 10 -- enabled, fullbright, castLight, sync, ownerOnly RECORD_SIZE = 11 PREFIX = "p:" -- one linkset data key per preset, e.g. p:Evening PAGE_SIZE = 10 -- dialog labels per page, leaving two navigation buttons local PATTERN = "^p:" local MODES = {"steady", "fade", "pulse", "cycle"} local ENABLED = bit32.lshift(1, 0) local FULLBRIGHT = bit32.lshift(1, 1) local key = "" local record --- Read once per selection and kept in variables for code that runs often. local mode = "steady" local intensity = 0 local glow = 0 local radius = 0 local fadeMs = 0 local flags = 0 local color = vector.create(0, 0, 0) --- Save a new preset with default settings. Returns false if the name is taken or the write fails. function create(name: string): boolean assert(#name >= 1 and #name <= 24, "names need 1 to 24 bytes") local presetKey = PREFIX .. name if ll.LinksetDataRead(presetKey) ~= "" then return false end local data = buffer.create(RECORD_SIZE) buffer.writeu8(data, GROUP, 1) buffer.writeu8(data, INTENSITY, 75) buffer.writeu8(data, GLOW, 5) buffer.writeu8(data, RADIUS, 10) buffer.writeu8(data, RED, 255) buffer.writeu8(data, GREEN, 200) buffer.writeu8(data, BLUE, 128) buffer.writeu16(data, FADE_MS, 250) buffer.writeu8(data, FLAGS, ENABLED) return ll.LinksetDataWrite( presetKey, llbase64.encode(data) ) == LINKSETDATA_OK end --- Counts saved presets without loading their keys. function presetCount(): number return ll.LinksetDataCountFound(PATTERN) end --- Keys for one dialog page, in alphabetical order. SLua's `ll` counts `start` from 1. function pageKeys(page: number) return ll.LinksetDataFindKeys(PATTERN, page * PAGE_SIZE + 1, PAGE_SIZE) end --- The button label for a key: its name without the prefix. function label(presetKey: string): string return string.sub(presetKey, #PREFIX + 1) end --- Load one preset and read its values. Returns false if the key holds no preset. function select(presetKey: string): boolean local saved = ll.LinksetDataRead(presetKey) if saved == "" then return false end key = presetKey record = llbase64.decode(saved, true) mode = MODES[buffer.readu8(record, MODE) + 1] intensity = buffer.readu8(record, INTENSITY) / 100 glow = buffer.readu8(record, GLOW) / 100 radius = buffer.readu8(record, RADIUS) fadeMs = buffer.readu16(record, FADE_MS) flags = buffer.readu8(record, FLAGS) color = ll.sRGB2Linear(vector.create( buffer.readu8(record, RED) / 255, buffer.readu8(record, GREEN) / 255, buffer.readu8(record, BLUE) / 255 )) return true end function setMode(modeId: number) assert( modeId >= 0 and modeId < #MODES and modeId == math.floor(modeId), "invalid mode" ) buffer.writeu8(record, MODE, modeId) mode = MODES[modeId + 1] end function setIntensity(value: number) assert(value >= 0 and value <= 1, "intensity must be between 0 and 1") local percent = math.floor(value * 100 + 0.5) buffer.writeu8(record, INTENSITY, percent) intensity = percent / 100 end function setFadeMs(value: number) assert( value >= 0 and value <= 65535 and value == math.floor(value), "invalid fade duration" ) buffer.writeu16(record, FADE_MS, value) fadeMs = value end function setFullbright(enabled: boolean) flags = enabled and bit32.bor(flags, FULLBRIGHT) or bit32.band( flags, bit32.bnot(FULLBRIGHT) ) buffer.writeu8(record, FLAGS, flags) end --- Write the selected preset back to its own key. Keep the edit if this returns false. function save(): boolean local status = ll.LinksetDataWrite( key, llbase64.encode(record) ) return status == LINKSETDATA_OK or status == LINKSETDATA_NOUPDATE end function selectedKey(): string return key end function currentMode(): string return mode end function currentIntensity(): number return intensity end function currentGlow(): number return glow end function currentRadius(): number return radius end function currentFadeMs(): number return fadeMs end function isEnabled(): boolean return bit32.band(flags, ENABLED) ~= 0 end function isFullbright(): boolean return bit32.band(flags, FULLBRIGHT) ~= 0 end --- Linear color for `PRIM_POINT_LIGHT`, converted once per selection. function currentColor() return color end return { GROUP = GROUP, MODE = MODE, INTENSITY = INTENSITY, GLOW = GLOW, RADIUS = RADIUS, RED = RED, GREEN = GREEN, BLUE = BLUE, FADE_MS = FADE_MS, FLAGS = FLAGS, RECORD_SIZE = RECORD_SIZE, PREFIX = PREFIX, PAGE_SIZE = PAGE_SIZE, create = create, presetCount = presetCount, pageKeys = pageKeys, label = label, select = select, setMode = setMode, setIntensity = setIntensity, setFadeMs = setFadeMs, setFullbright = setFullbright, save = save, selectedKey = selectedKey, currentMode = currentMode, currentIntensity = currentIntensity, currentGlow = currentGlow, currentRadius = currentRadius, currentFadeMs = currentFadeMs, isEnabled = isEnabled, isFullbright = isFullbright, currentColor = currentColor } end)() local first = preset.pageKeys(0)[1] if first and preset.select(first) then print("Selected " .. preset.label(first) .. ": " .. preset.currentMode()) else print("No presets yet. Call preset.create(name) to add one.") end