Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

demonview

14
Posts
A member registered Mar 05, 2025

Recent community posts

(1 edit)

With the latest dev version of Decker, this no longer crashes, and memory stabilizes when left to play the first card. You'd have to build it yourself from github, or wait for the next release.

Currently Decker seems to be able to draw with different brushes, but there's no difference at all in what's drawn, and this is discouraged by Decker switching away from the Fill tool on a change to the brush. So rather than an additional fill function, there's room in the design for the existing fill function to repeat the brush so drawing with different brushes would be useful.

Still, patterns also have animation and have this interesting effect: if you get a pattern into a canvas and move the canvas, the pattern reflects card coordinates rather than canvas coordinates. Paste this widget and move it around to see that:

%%WGT0{"w":[{"name":"canvas1","type":"canvas","size":[25,38],"pos":[282,72],"image":"%%IMG2ABkAJgAMGwUAExsHABIbBwASGwcADBsEAAIbCAAKGwYAARsIAAkbCAABGwcACRsIAAEbBwAJGwgAARsHAAkbCAABGwgACBsRAAgbEQAIGxEACBsSAAcbEgAHGxIABxsTAAYbCQABGwkABhsJAAEbCQAGGwkAARsJAAcbCAABGwoABhsTAAYbEwAGGxMABxsSAAcbEgAHGxIABxsSAAcbEgAIGwgAARsIAAgbCAABGwgACBsIAAEbCAAIGwgAAhsHAAkbBwADGwUAChsHAAQbAwAMGwUAFRsDACY="}],"d":{}}

A very minor reason to have a Linux download on itch.io is that itch.io's official application won't install Decker otherwise: you're stopped at "No compatible downloads were found"

Since the better way to fix that is to have the itch.io app offer Proton like Steam does, it should also eventually work to leave it as is.

(1 edit)

I never noticed xdg-install.sh, neat! You don't need sudo for it though. Just ./xdg-install.sh will install it for your user.

On Steam Deck I found it inconvenient to get a build system because that requires some extra steps to make the image mutable, that I worry an update will wipe out, so I added the Windows version instead and set it to use Proton.

(1 edit)

Even if you assign an 8,8 image to a pattern using the patterns interface, it gets turned into b/w. But, you can dynamically change what "b/w" means, which is what pink decker jam does.

I've tried Decker on a few e-ink devices, including a Kindle's browser, and animations suffer of course, and drawing is also very laggy, to the point that trying to draw a circle results in a triangle getting drawn. I didn't explore performance much, but I could handwrite with some care and add widgets and it wasn't that bad.

Actually much worse than those devices is a 1Ghz RISCV device, the ClockworkPI. All animations are amazingly slow: going from the first card to the second card in the demo deck takes 19 seconds due to the sliding animation. Even opening a menu and moving the mouse down it has Decker struggling to keep up with inverting the colors of the menu item hovered over. But, it does work! It's slow, but it gets there! The main issue here is probably that this is a single-core machine and Decker uses all of it, a case where optimizing SDL refresh rates a bit might really help.

But if you mean a 15-year-old consumer laptop that was good at the time, Decker should be fine on that. Lightly retro hardware might even work a little better than the most recent hardware because you'll get features like Tracing Mode that worked on X but have been broken by Wayland. Decker itself should be fine, and I think you can track the additional burden that you add with the script profiler.

I really like the slider suggestion. A nice thing about it is that you can go to that card and manipulate the sliders yourself to test your logic, or hop over while going through your quiz to see that the sliders are as you expect.

Here's a card. If you copy this and paste it into a deck, it'll add a new card with three sliders, a personality field, and a grid of tests and results:

%%CRD0{"c":{"name":"home","script":"on quiz do\n on f t do eval[t (\"cat\",\"dog\",\"duck\") dict (cat,dog,duck)..value] end\n p:first extract result where (f@test)..value from logic.value\n personality.text:\"\" fuse \"You \", p\nend","widgets":{"cat":{"type":"slider","size":[100,25],"pos":[80,32],"script":"on change val do\n quiz[]\nend","interval":[0,5],"format":"cat: %f","style":"bar"},"dog":{"type":"slider","size":[100,25],"pos":[80,64],"script":"on change val do\n quiz[]\nend","interval":[0,5],"format":"dog: %f","style":"bar"},"duck":{"type":"slider","size":[100,25],"pos":[80,96],"script":"on change val do\n quiz[]\nend","interval":[0,5],"format":"duck: %f","style":"bar"},"personality":{"type":"field","size":[192,16],"pos":[224,64],"value":"You are an animal hater?!"},"logic":{"type":"grid","size":[448,160],"pos":[32,160],"bycell":1,"format":"ss","value":{"test":["0=cat+dog+duck","(0,0,1)~0=cat,dog,duck","(0,1,0)~0=cat,dog,duck","(0,1,1)~0=cat,dog,duck","(1,0,0)~0=cat,dog,duck","(1,0,1)~0=cat,dog,duck","(1,1,0)~0=cat,dog,duck","15=cat+dog+duck","1"],"result":["are an animal hater?!","only like mammals","only dislike dogs","only like cats","only dislike cats","only like dogs","only like ducks","are a true friend to all creatures","have unexpected tastes! Sorry!"]},"row":6,"col":1}}},"d":{}}

Room for improvement: if you like all the animals just a little, like 1,1,1, then the personality is "You have unexpected tastes! Sorry!". Also, if you change the logic table so that three different tests all pass, then you still only get the first result. You could check the number and do something about ambiguous results like that.

Or, maybe that quiz code is too clever. Here's something else that still uses that logic grid:

on quiz do
 vars.cat:cat.value
 vars.dog:dog.value
 vars.duck:duck.value
 i:0
 while i < count logic.value
  test:logic.value[i].test
  if eval[test vars].value then
   personality.text:"" fuse "You ", logic.value[i].result
   i:count logic.value # stop here
  end
  i:i+1
 end
end

And, here's a totally different way to do it:

%%CRD0{"c":{"name":"card1","script":"on quiz do\n personality.text:\"You have unexpected tastes! Sorry!\"\n (extract value where \"button\"=typeof@value from card.widgets)..event[\"click\"]\nend","widgets":{"cat":{"type":"slider","size":[100,25],"pos":[80,32],"script":"on change val do\n quiz[]\nend","interval":[0,5],"format":"cat: %f","style":"bar"},"dog":{"type":"slider","size":[100,25],"pos":[80,64],"script":"on change val do\n quiz[]\nend","interval":[0,5],"format":"dog: %f","style":"bar"},"duck":{"type":"slider","size":[100,25],"pos":[80,96],"script":"on change val do\n quiz[]\nend","interval":[0,5],"value":5,"format":"duck: %f","style":"bar"},"personality":{"type":"field","size":[192,16],"pos":[224,64],"value":"You only like ducks"},"button1":{"type":"button","size":[96,32],"pos":[32,160],"script":"on click do\n if me.value:0=sum (cat,dog,duck)..value then\n  personality.text:\"You're an animal hater?!\"\n end\nend","text":"likes none","style":"check","value":0},"button2":{"type":"button","size":[96,32],"pos":[144,160],"script":"on click do\n if me.value:(1,0,0)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only like cats\"\n end\nend","text":"only cats","style":"check","value":0},"button3":{"type":"button","size":[96,32],"pos":[32,208],"script":"on click do\n if me.value:15=sum (cat,dog,duck)..value then\n  personality.text:\"You're a true friend to all creatures\"\n end\nend","text":"loves all","style":"check","value":0},"button4":{"type":"button","size":[96,32],"pos":[144,208],"script":"on click do\n if me.value:(0,1,0)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only like dogs\"\n end\nend","text":"only dogs","style":"check","value":0},"button5":{"type":"button","size":[96,32],"pos":[144,256],"script":"on click do\n if me.value:(0,0,1)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only like ducks\"\n end\nend","text":"only ducks","style":"check","value":1},"button6":{"type":"button","size":[96,32],"pos":[256,160],"script":"on click do\n if me.value:(0,1,1)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only dislike cats\"\n end\nend","text":"all but cats","style":"check","value":0},"button7":{"type":"button","size":[96,32],"pos":[256,208],"script":"on click do\n if me.value:(1,0,1)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only dislike dogs\"\n end\nend","text":"all but dogs","style":"check","value":0},"button8":{"type":"button","size":[96,32],"pos":[256,256],"script":"on click do\n if me.value:(1,1,0)~((cat,dog,duck)..value)>0 then\n  personality.text:\"You only like mammals\"\n end\nend","text":"all but ducks","style":"check","value":0}}},"d":{}}

This has the same sliders and personality field, but the 'quiz' just clicks every button on the card. Each button is a checkbox that sets itself to the value of a test and sets the personality if that value is true. This method makes the logic a little harder to see, as it's in each individual button, but when messing with sliders you can directly see what tests succeed or not, and with meaningful button names you could have other cards perform logic based on what's checked so far in the quiz

This is a very neat solution. With different translation tables you can have support for other languages, and you can easily test to see if translation failed: if you're on a Korean version of Decker and tried to load a deck that uses Latin1 characters, those would translate to the invalid Unicode character. It's "yet another standard" but it's an internal standard only, so some deliberate incompatibility doesn't seem that painful.

Chinese is still out, of course. Hiragana+Katakana doesn't fit, but Hiragana would on its own and you could sometimes use a Katakana font. The entire Cyrillic subset of Unicode wouldn't fit, but the top 8 Cyrillic languages altogether only need 110 letters. Right-to-left languages would fit in the translation tables but have other bigger problems with rendering.

Ah, it's shown under Prototype/Attributes...

I managed to look everywhere else.

I placed eggtimers on a bunch of cards, and then wanted to loop through them and change the Seconds value. I've done this in the Listener with Field widgets, but how do I do it with eggtimers? This doesn't seem to work, to change it for a single eggtimer:

eggtimer1.event["set_seconds" 180]

Both of those work! I didn't know about gindex, and that seems useful. I kept looking for some way to have a 'where' apply after an earlier 'where' without multiple queries, and the conditional shows a way to do that.

The conditional also suggests this solution:

 extract () unless first value where 1<count index by value from str

Is there a nicer way to skip the empty lists resulting from this query?

 str:"hello world"
 extract list value where 1<count index by value from str 
((),(),("l","l","l"),("o","o"),(),(),(),())
Right now I have

 (list ()) drop extract list value where 1<count index by value from str 
(("l","l","l"),("o","o"))

  With the aim of showing only repeat elements, so

 "" fuse first @ (list ()) drop extract list value where 1<count index by value from str 
"lo"
 "" fuse first @ (list 0) drop extract first value where 1<count index by value from str
"lo"
 "" fuse extract L where C>1 from select L:(first value) C:count value by value from str 
"lo"
wods:"\n" split "r" drop words:read["/usr/share/dict/words"]
words:"\n" split words
still:select word:words@index wod:value
  where (wods in words)*(extract value like "*r*" from words)
  from wods
show[table random[still -10]]

this gets an answer in 13min, or 880ms without the 'wod in words' test. I've tried a few alternatives (words dict 1, readdeck of a grid, parsing and reading a json of a table) and nothing seems to cut that down. It seems like building large tables is slow. Maybe due to the allocator?

But, this was only for learning purposes and I gained a better appreciation of the query language from it.

(1 edit)

I have this Lil code to print a table of 10 English words that remain words when the 'r's are removed from the word:

words:wods:still:0
each w in "" drop "\n" split read["words"] # /usr/share/dict/words"]
  words[w]:1
  if "r" in w
    wods["" fuse "r" drop w]:w
  end
end
each v k in wods
  if k in words
    still[v]:k
  end
end
show[select word:value wod:wods@value from random[still -10]]

which produces output like

+--------------+---------------+
| word         | wod           |
+--------------+---------------+
| "evaluated"  | "revaluated"  |
| "emigated"   | "emigrated"   |
| "boated"     | "borated"     |
| "estated"    | "restated"    |
| "expatiated" | "expatriated" |
| "elatedness" | "relatedness" |
| "pedated"    | "predated"    |
| "ungated"    | "ungrated"    |
| "pated"      | "prated"      |
| "gated"      | "grated"      |
+--------------+---------------+

if reading a 'words' file that contains only the 3k words that include "ated", this runs in 11s (lila.awk) or 16s (lilt). So, actually reading /usr/share/dict/words with its half a million words, is pretty infeasible.

Is there a much better way to do this in Lil, or is this sort of batch processing out of scope for Lil?

Oh, I had a second question about selecting from an array using an array of booleans, but I found that 'select' can do that. Which gives this code that runs in 22ms in lilt, instead of 16s:

words:extract value where 5<count@value from "\n" split read["words"]
still:select word:value wod:(on f x do "" fuse "r" drop x end)@value
    where (on f x do ("" fuse "r" drop x) in words end)@value
    where (on f x do "r" in x end)@value
  from words
show[table random[still -10]]

but this needs 4s to process only 50k words, so the full dictionary's still out.