Having a lot of fun with Decker and Lil.
Thank you.
Is this expected behaviour for joining arrays?
a1:("aa","bb","cc","dd") a2:("x") show[ a1 join a2 ] --> (("aa","x"),("bb","x"),("cc","x"),("dd","x")) show[ a2 join a1 ] --> (("x","aa"))
From the lil reference manual:
If join is applied to non-table arguments, it produces a list by pairing adjacent elements from x and y. If either argument is a number, it is considered "range" of that argument, and otherwise it is interpreted as a list. For example, "ABC" join 3 gives (("A",0),("B",1),("C",2)).
As you can see, it has a preference for preserving the length of the left argument if they don't match. Some functional languages call this operation on lists "zip()". Applying "join" to tables performs a natural join.
If you simply want to concatenate lists and atoms, use the comma operator (,):
a1,a2 # ("aa","bb","cc","dd","x")
(Lil doesn't have list literals per se, just atom literals and the comma operator.)
Does that make sense?
The only video I’ve found is this one.
It’s not bad and kind of takes you on a journey of someone figuring out Decker, while you watch it to figure out Decker. It’s very meta. ;-)
Not a video, but here is a handy list of screenshots of these Decker examples.
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.
Hmm. Could be tricky to make this fast in Lil.
In general, using queries is much more efficient than loops. You can slightly simplify
where (on f x do "r" in x end)@value
as
where value like "*r*"
and you could hoist that "in" out of the loop, since it accepts a list as a left argument. Together, these ideas can make the query much more concise:
on strip x do "" fuse "r" drop x end still:select word:value wod:strip@value where (strip@value) in words where value like "*r*" from words
...but probably not much faster.
You can also avoid stripping the r's twice by using a "subquery"
still:select where wod in words from select word:value wod:strip@value where value like "*r*" from words
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.
I made some localized improvements to C-Lil's implementation of the "in" operator. Using this dictionary file:
https://github.com/dwyl/english-words/blob/master/words_alpha.txt
and this version of the entire script:
words:extract where 5<count@value from "\n" split read["words_alpha.txt"] on strip x do "" fuse "r" drop x end still:select where wod in words from select word:value wod:strip@value where value like "*r*" from words show[table random[still -10]]
The patch brings execution time on my laptop from about 12 minutes (oof) to about half a second.
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 strRight now I have
((),(),("l","l","l"),("o","o"),(),(),(),())
(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"
You could use a conditional to compute the extracted column (this is evaluated once per group):
extract if count value first value else () end where 1<count index by value from str
Or you could use the "gindex" column to make the filter retain at most one element per group:
extract where (!gindex)&1<count index by value from str
How's that?
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
Hi, I have a question about sounds. In my current project, I have an options card for various things and I want to have an option to mute all sounds in the deck. So far, I thought of putting this script everytime a sound is played, with a checkbox button named "soundsandmusic" on the options card:
if options.widgets.soundsandmusic.value=1 play["sound"] end
And I was wondering if there was a more simple way to do this, instead of putting an if statement everytime a sound is played.
You could define a function which "wraps" the default play[] in the deck-level script, preventing the original from being called unless your flag is set:
on play x y do if options.widgets.soundsandmusic.value=1 send play[x y] end end
The "send" statement is specifically designed to make it easier to set up these kinds of overrides for existing functions. (Overriding go[] has some interesting potential!)
If you think there are some situations where you may want to call play[] normally, a less invasive approach would be to give your wrapper a different name instead of shadowing the built-in function, and to change e.g. all the instances of play["foo"] within your own scripts to sfx["foo"]:
on sfx x do if options.widgets.soundsandmusic.value=1 play[x] end end
Keep in mind that scripts within modules and contraptions don't "see" deck-level definitions, since they're supposed to be reusable from deck to deck. If you're setting up sound effects for dialogizer text, for example, you'll need to perform the same flag-check. This can also be done in a centralized place with deck-level functions.
Does that make sense?
Very very new to the program here. I am trying to make a system where there is a button that is unavailable to use (Whether it be locked or invisible or whatnot) until a slider has reached a certain value, but without it flickering each time that the slider gains or loses value (Which is what happens when i use the toggle function for this.). So if its 70 or above, it can be pressed, and if its any lower than 70 it can't.
Hello, welcome! I have a simple script for you. Where to put it is going to vary a little bit based on what you're doing.
If you're changing the number inside the slider by having the user click on it, you could put something like this in the slider's script:
on change val do if slider1.value > 69
button1.locked:0 end end
Or, explaining what that means: "Whenever my value is changed (by the user clicking on me), check if my value is above 69. If yes, unlock the button. End (for the if statement). End (for the on change val do event)."
This only unlocks the button -- it doesn't re-lock it if the number goes back down. I'm not sure if that's even something you want, but you could have it re-lock below 70 with an added "else":
on change val do if slider1.value > 69 button1.locked:0 else button1.locked:1 end end
I'm locking and unlocking the button in this example but you can use nearly the same snippet to change the button's visibility instead.
Rather than using .toggle here I'd prefer to use .show just to feel like I had complete control over it:
on change val do if slider1.value > 69 button1.show:"solid" else button1.show:"none" end end
If you end up hiding the button you'll probably want to use .show:"none" rather than .style:"invisible" in the script.
The difference between them is that even though "None" buttons are effectively locked AND hidden from view... Invisible buttons can still be clicked. This is useful for users who are drawing scenes and want to use buttons as containers for their clickable areas without the standard appearance of a button.
The above scripts are assuming that the slider's value was changed by the user clicking on it and that the script is inside the slider.
But if the slider's value is going up because of something happening in a script somewhere else you could add the "check if >70, unlock or show button" snippet to the end of that other script instead.
For example, a script inside a button which adds +10 to the value of the slider, and also unlocks a button if the new number is more than 70... could look like this:
on click do slider1.value: slider1.value+10 if slider1.value>69 button1.locked:0 end end
I hope this points you in the right direction! If this isn't what you needed, then I'm happy to try again.
(not sure if this is a decker question or a lil question sorry in advance)
i'm making a small contraption with a grid widget, and i'd like to ensure that the rows are unable to be deleted but for a single column to be editable. I'm using a column format of LLLs to lock the columns I'd like to not be edited. I know "locking" the grid would prevent deleting, but it would also prevent editing...
And whenever I select a row or a cell, the backspace key will delete the entire row...
Any way to get the desired behavior?
Hey! I'm trying to figure out a way to implement a simple 60 second timer that doesn't block the player from interacting with the game, like with the sleep command. Once the timer is complete, I just need it to send out an event. I tried to reverse engineer the timer on the GUI examples, but I quickly realized I had no clue what I was doing since I'm still newish to Decker and coding. Was wondering if someone could give me a hand.
I often make a timer in a basic widget when I need one.
A simple example:
If you make a field and set it to be "animated" (select the widget, then in the menu select Widgets > Animated) it will experience 60 view events per second.
Since this example is using a field we can also ask it to store a number to keep count of how many view events have happened. And when it reaches a certain number (60 view events multiplied by the number of seconds -- for 60 seconds this is 3600) tell it to do something special.
on view do me.text:me.text+1 if me.text=3600 #the stuff you want to happen# end end
(When you're writing a script inside a widget, you can refer to that widget as 'me', which is what I'm doing in the script here.)
This may not be needed but you can also make a custom event handler pretty easily:
on view do me.text:me.text+1 if me.text=3600 my_event[] end end
I'm using the example name "my_event" but you can name it something more specific and then define what happens during this event inside of the same widget's script that uses it (or on a higher level like the card or deck-level scripts) by writing it out like any other event handler:
on my_event do # the stuff you want to happen # end
Also there was a recent thread about making timers for someone else's game that has some more discussion, if it's of any use to you: (link here)
Yes, by sending events. A script on CardA can call function "foo" with the argument "bar" on CardB like so:
CardB.event["foo" bar]
This expression returns the result of the function call. In this way you can design cards that act as "APIs" to be called by scripts elsewhere in the deck.
You can likewise call .event[] on widgets. This "synthetic" event is indistinguishable to the target (thatButton) from an event generated by Decker itself:
thatButton.event["click"]
When an event is sent, all the deck parts and other magic variables will be in scope from the perspective of the target: widgets will "see" other widgets on the same card, "me" will be bound to the target, etc.
Yes! A widget's order placement can be modified with .index
For example:
on click do me.index:999 end
index:0 places a widget at the bottom of the order, index:999, or any other sufficiently large number, places a widget at the top.
You can put this snippet into an "on drag do" event handler as well.
Hey! I love the engine and trying to understand it as much as I can so this might be a silly one, but in the guided tour you mention that single grids could be used as a selection input. How do you know what value is selected with it? With Buttons with Checkboxes you have buttonName.value that you can compare, but I don't see how that would work with the grid widget. Thank you for the help!
I also don't understand how to add new items to a grid like an inventory system. I made an items grid that has two columns of Name And Description. I've looked at you helping someone with an inventory system yet when I try to re-create this function in the Decker script
on add_item n do i:get_items[] i.value:insert name:n into i.value end
I'm met with an error of "Expected name, but found:. It's an error that happens when I try to put this
n into i.value
I've even tried to add this code to a button
on click do
i:inventory.widgets.items
i.value:insert name:"sworde" into i.name
alert["ye have acquired ye sworde!"]
end
But I still get the same issue as before what am I doing wrong?
If you refer to the Decker Reference Manual, grid widgets have a ".rowvalue" property that can be used to obtain the data in the selected row; this will be a dictionary. The "first" of such a dictionary will obtain the value for the first column:
on click do f.text:first g.rowvalue end
In this particular example it would be equivalent to ask for the "key" entry of the dictionary, as that is the name of the first column:
on click do f.text:g.rowvalue.key end
As for adding rows, it looks like you were consulting a very old example from before the "insert" syntax was overhauled. Given a grid "i" with a single column "name", I think you want something closer to:
i.value:insert name with "sworde" into i.value
The Lil Reference Manual has detailed explanations and many examples of the "insert" query form.
Does that make sense?
Is there an easy way in Lil to round a fraction to an integer?
I’m trying to render text to a canvas, centered within a given area. That’s easy enough, I can just measure the text string, subtract it from the available width, divide by 2, and use that as the left edge:
# Measure the text, minus the extra space at the end
textsize:canvas.textsize[text] - (canvas.font.space,0)
# Render the text centered
canvas.text[text pos+(size-textsize)/2]
If I’m drawing an even-width string into an even-width box, or an odd-width string into an odd-width box, that works perfectly. Otherwise, the coordinates wind up with a fractional part, which Decker truncates to 0, meaning that text that can’t be mathematically centred is shifted to the left.
That’s just mathematics and there’s not much to do about it, except that the strings I’m rendering tend to be Title Case, so they’re already visually weighted to the left. I think it would look a little better if fractional coordinates were rounded up rather than down, balancing the Title Case rather than reinforcing it, while leaving the mathematically-centred text (no fractional part) alone. Again that’s pretty easy:
canvas.text[text pos+(0.5,0)+(size-textsize)/2]
That works perfectly… unless the box I’m rendering into is also centred, which means it already has a 0.5 offset. In that case, the even-string-in-even-box and odd-string-in-odd-box wind up worse - instead of being rounded away, the extra 0.5 adds up to extra offset. I figure if I can round pos
before using it in calculations, I can prevent errors from accumulating, something like this:
# I wish this worked!
pos:makeint[pos]
Playing around, it looks like I can probably do this with:
pos:bits.or[pos 0]
…but that seems a bit hacky. Is there a cleaner way to do this?
You may be looking for the "floor" primitive? There's no corresponding "ceiling" primitive but you can take the negation of the floor of the negation:
select value down:(floor value) up:(-floor-value) from .1*range 21 +-------+------+----+ | value | down | up | +-------+------+----+ | 0 | 0 | 0 | | 0.1 | 0 | 1 | | 0.2 | 0 | 1 | | 0.3 | 0 | 1 | | 0.4 | 0 | 1 | | 0.5 | 0 | 1 | | 0.6 | 0 | 1 | | 0.7 | 0 | 1 | | 0.8 | 0 | 1 | | 0.9 | 0 | 1 | | 1 | 1 | 1 | | 1.1 | 1 | 2 | | 1.2 | 1 | 2 | | 1.3 | 1 | 2 | | 1.4 | 1 | 2 | | 1.5 | 1 | 2 | | 1.6 | 1 | 2 | | 1.7 | 1 | 2 | | 1.8 | 1 | 2 | | 1.9 | 1 | 2 | | 2 | 2 | 2 | +-------+------+----+
This thread has some examples of how to make things happen on mouseover/hover.
It also has an important caution about how mouseover triggers can't work the same way on mobile and touchscreen devices since they don't have a mouse pointer. It's an extra thing to think about if you'd like your project to be playable on those platforms (and if the mouseover event isn't just a optional detail that won't be missed if it doesn't work).
Yes. Lilt is a command-line wrapper for the Lil programming language with a few additions to its standard library like the ability to shell out to other programs and interact with the filesystem. It's easy to build from source on MacOS, Linux, or BSD. I also provide a multi-platform prebuilt binary here on itch.io alongside Decker releases.
If you just want to try something out quickly or share a snippet, TryLilis a browser-based sandbox for Lil.
The Decker repo includes basic syntax highlighting profiles for vim, emacs, Sublime Text, and CodeMirror; you can use those as a starting point for a syntax profile for other editors.
Recently, I encountered a problem when learning text dialogue and canvas bit movement painting: 1. DD follows the steps in the tutorial, and the dialog box cannot appear
2. Zazz can't exercise according to the steps in the tutorial
Even if you copy the card from the tutorial to your local computer, it has no effect.
Did you import the zazz and dd modules into your deck using the Font/DA Mover?
Hi, I have a question about alert when it can prompt for a string. Probably the most basic use case imaginable, how can I write that input into a widget? E.g.
alert ["What's your name?" "string"]
And then write that name to a widget called "field1" or something? Sorry if folks have answered this before, I found a few cool threads on logging text from a field but I genuinely couldn't find anything on how to use this for alert specifically.
You can just say something like:
field1.text:alert["What's your name?" "string"]
Here’s an Ask button that asks the question, and sticks the response into a field:
%%WGT0{"w":[{"name":"field1","type":"field","size":[100,20],"pos":[154,108],"value":"blorp"},{"name":"button1","type":"button","size":[60,20],"pos":[174,74],"script":"on click do\n field1.text:alert[\"What's your name?\" \"string\"]\nend","text":"Ask"}],"d":{}}
This is going to be such a stupid question but I just feel so lost....how do I import an image to a canvas to use with dialogizer? Do I have to create the images in the canvas and lock it in Decker? If I try to import an image as is, it just becomes a static object I can't interact with after I'm done changing its size.
All questions welcome!
When you import an image into Decker, it has the selection box around it, right? And when you click outside the box, the image is placed on the back of the card.
But you can also copy (or cut) the image while it's still inside that selection box, switch to Widget Mode... and in the Edit menu select "Paste as new Canvas".
Personally I like to turn on the Toolbars (Decker > Toolbars) for this, to save a click while switching back and forth between importing/drawing and pasting my images into canvases.
Also, anytime your image is on the back of a card you can draw a selection box around it and then use the Edit menu option "Tight Selection" to shrink the selection to only contain your artwork.
Hi! Probably basic question incoming, but I’m stuck…
I’m trying to track whether the player is wearing a cloak to display certain widgets or to send them to a specific card. I’ve created a checkbox for this, and when manually clicked on/off, the conditionals are tracked properly.
The issue I’m running into is that I can’t seem to change the value of that checkbox via Lil directly. As in, if the player click the Start button, the checkbox is enabled (because they wear the cloak at the start of the game), and if the player clicks the button “Hang Cloak”, the checkbox is disabled. Or it should be.
~~So far, I’ve tried:
Cloakroom.widgets.WearCloak.value:true
and
Cloakroom.widgets.WearCloak.value:true
Cloakroom.widgets.WearCloak.event["change" Cloakroom.widgets.WearCloak.value]
~~
I feel like I’m missing something but I don’t know what/how…
EDIT: I FOUND THE ISSUE! I wrote true
instead of 0/1
….
Also, follow up question: if the display of the checkbox is “Invisible”, is the value of the Checkbox still accessible? (If not, I’ll just hide it behind some text :P )
Thanks in advance!
Just for the record, you can represent counters using a field widget via its .text or .data attributes, or alternatively you could use a slider widget via its .value attribute. Sliders are slightly more convenient than fields for representing numeric values within a known range, since their .value is always a number and is automatically "clamped".
Widgets set to "Show None" behave identically to visible widgets from a scripting perspective; they "remember" their contents across events and when the deck is saved. If there are any widgets whose contents you don't want preserved, you can mark them as Volatile. Sometimes volatile widgets are useful for "resetting" the state of a game.
Does that help clarify?
Not actually a question, but a thing that tripped me up and was confusing me. I wrote a bit of code to iterate over an array of strings and slow-print them to a canvas, terminal style. But I was finding some bizarre results when I went to test it: the first line would draw fine, but subsequent lines would go bananas, because somehow the Y coord was changing within the inner loop.
Some simplified code looks like this:
each line y in script each c x in line x*6,y*13 end end > (((0,0),(6,0),(12,0),(18,0),(24,0),(30,0),(36,0),(42,0),(48,0),(54,0),(60,0),(66,0),(72,0),(78,0),(84,0),(90,0)), ((0,0),(6,13),(12,26),(18,39),(24,52),(30,65),(36,78),(42,91),(48,104),(54,117),(60,130),(66,143),(72,156),(78,169),(84,182),(90,195),(96,208),(102,221),(108,234),(114,247)), ((0,0),(6,26),(12,52)))
Notice how the Y coord keeps going up where it's not supposed to?
At first I thought it was something weird with how canvas.text[] works, or that i had my offset or loop logic wrong, but then I realized after comparing this to a version without the multiplication:
each line y in script each c x in line x,y end end > (((0,0),(1,0),(2,0),(3,0),(4,0),(5,0),(6,0),(7,0),(8,0),(9,0),(10,0),(11,0),(12,0),(13,0),(14,0),(15,0)), ((0,1),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),(18,1),(19,1)), ((0,2),(1,2),(2,2)))
That looks like what you'd expect, right?
Turns out the problem is operator precedence. Since Lil can do matrix math, what's happening is it's actually multiplying x by a vector of (6,y), then multiplying that by 13, so you get nonsense. Wrapping the two multiplication operations in parens fixes the problem. It didn't occur to me at first 'cause in most languages, `,` isn't an operator, it's just syntax.
This was enough of a weird surprise I thought I would share with folks, in case y'all run into similar.
Depending on what you're trying to do, either a combination of ! and = (for a scalar boolean value, logical XOR is the same as "not equal to") or possibly bits.xor[].
TLDR eval->dd.say isn’t passing rich-text into the dialogizer In your Dialogizer deck, on the rich-text card, you mention that it’s easier to style by throwing rich-text into a field and having it parse using the eval method. So I’ve got a field, i’ve got it set to invisible in the card script, I’ve got a button that does the eval method. Everything is working well except that it isn’t showing the styling I’ve set in the field. I’ll mention, just as I typed this I tried to add a hyperlink in the same way (just manually add it to the field) and it also didn’t pass through the parsing operation.
Forgive me, I’m one of those dudes who got by in the 00s by just manually deleting html and css stuff and adding it back to “create my own myspace profile”. Like, that’s my level of coding know-how. I’m sure this question is voiced annoyingly; I’m trying to use words I’m not fluent with. Thank you for everything you do here IG.
Ok forgive me, I didn’t follow your operation faithfully, I now see. Your method was to use a button to point at one field that itself pointed at another field (which contained the rt formatted text). I was skipping the middle-person field and apparently, by doing that, it doesn’t render the rt formatting. I’m leaving this post because it may come up in the future; perhaps IG (or someone else) could simply verify that this was indeed my problem and if anything maybe propose how I might’ve skipped the middle-person step and kept the RT formatting.
eval is a handy tool for making code snippets visible in the documentation, but it's not often needed while making projects with dialogizer. And it's probably working against you here.
This:
dd.open[deck] dd.say[yourcoolfield.value] dd.close[]
should be enough to play a scene written in a rich text field, with all of the formatting intact.
It's important that you point dd.say at the field's .value (which includes the rich text formatting) and not .text (which is plain text only)
If you strip it down and just put the above code snippet (with "yourcoolfield" renamed to be correct for your project) does it work?
I’ve got another, probably totally silly question, (I promise I look through all the documentation, the problem is that I’m basically illiterate in basic coding). As you’ll see in this video (how do you folks get those cool embedded videos to happen without using youtube?) I’m running a card script which evals the field on the left (called cardscript; yes i know the text above the field has a space, trust me it’s labled without the space). I’ve defined a (dunno what to call it) thing called “fields” which I want to use as a bucket to hold all of the fields i wish to make invisible at when I click “eval below”. As you can see, when I just define the thing as ’source’ it works, but when I add a comma and include ‘cardscript’, not only does it fail to include cardscript, but now source remains visible.
Secondary issue: The !alert works, but does not retain the richtext formatting.
to get ahead of “why are you doing this in fields”, the answer is, “I’m trying to teach myself how to code AND how to code in Decker and it’s a ton of friction having to click back and forth between widgets and interact, into the card script etc. and theoretically, it shouldn’t matter provided I do this correctly."
It's totally fine to be new and have questions! Decker is a pretty friendly environment to try things and figure them out. And I think most of the things that look like videos in the thread are actually gifs. I know I use a screen-to-gif recorder when I need to make a small example of something.
Okay, I think these are your main questions:
1) How can I make Decker use the whole list of widgets I defined earlier?
You just need a tiny change to how you're referencing them. You need two periods, instead of just one in this case.
fields..show:"none"
2) How can I make my !alert use my rich text formatting?
In-line commands run by dialogizer don't carry over any of the rich text features. I'm pretty sure they're just run as code.
However, you can point to another field's value inside of your !alert[] in the same way you point to source.value inside of dd.say[].
!alert[problem.value]
I know it's another step, and another thing to have to manage while you're editing but it should be pretty straightforward.
Another thing that I think might help you sometimes is the ScriptViewer contraption. You can have it display the card script and let your edit it from your card in interact mode instead of having to use a workaround with eval. And because the ScriptViewer only displays scripts that exist elsewhere you can safely delete the ScriptViewer contraption when you're done.
Just another possibility, in case it helps!
Hi, I noticed a discrepancy in behaviour between native decker and exported HTML decker and was wondering if anyone knew why.
Basically wanted a looping sound to play “on view” of the very first card in the deck:
on view do
play[“sound1””loop”]
end
In native decker, this seems to work. I believe I also got it to work in web decker directly. But I am finding in a web decker protected export from native, it only plays the sound once (and weirdly, after clicking into the deck). However, if you do it as a second card you view through some other trigger, the same code works in web / native.
Are there a special considerations for doing “view” actions on the very first card?
Many thanks in advance if anyone knows…
As a general rule, web applications are not allowed to start playing sound without user interaction. Web-Decker attempts to start its audio subsystem when the user first clicks or types on the keyboard. It's possible there's some additional inconsistency in behavior, but what you're describing sounds to me like normal webapp sandboxing constraints.
Hello! I'm discovering Lil and setting myself some little challenges. While I'm used to a lot of other languages, I'm wondering if there is a simple way to declare lists of lists.
My intuition would be
(0,1), (2, 3)
but this actually is the same as (0, 1, 2, 3). I guess that while () is the empty list, parenthesis aren't lists delimiters as [] in other languages. I understand that comma is the concatenator for lists and that the correct solution to my problem would be
(list 0 1), (list 2,3)
but I'm wondering if there's a more elegant solution.
Thank you!
Lil doesn't have a per se list literal syntax. Your example is one straightforward way of making nested lists:
(list 0,1),(list 2,3)
Another fairly general way of declaring static data is to use "parse" on a string of JSON/LOVE:
"%J" parse "[[1,2,3],[4,5],[6,7,8,9]]" # ((1,2,3),(4,5),(6,7,8,9))
Within Decker it's often a good idea to store lengthy static data in a Field widget (where it can be encoded/decoded to/from LOVE via the .data attribute), or as a table stored in a Grid widget, where it can be viewed and directly manipulated. Modules (Lil libraries for Decker) don't have direct access to a deck, so they have a dedicated mechanism called a KeyStore.
If the sublists are of equal size, you could turn a flat list into a nested one using a primitive like "window":
2 window range 6 # ((0,1),(2,3),(4,5))
You could also construct a nested list piecemeal by index in a named variable:
a[0]:1,2,3 a[1]:4,5 a # ((1,2,3),(4,5))
This last approach is particularly handy for nested dictionaries; see the code examples in the reference manual entry for the Array Interface.
Does any of that help?
Hi! This isn't an answer yet, just some clarifying questions:
Are you working on using the "Make Magnet" example to make magnets that are created with scripts in them that use things like rect.overlaps and rect.constrain from the same example deck?
And did you want to make it so that only some of them do something when they're overlapping, and others don't? (based on some condition, or your mention of a whitelist).
Actually if you could give a specific description of what you're trying to do then I think we could help make sure you have all of the pieces of it that you need.
Yes, the example contraption, and I more specifically want to apply a general script to all of the canvases made with it to make them work with the Overlaps mechanic. It's the mechanic with a script applied to a draggable canvas -
on release do
if rect.overlaps[me target]
alert["You hit the target!"]
end
end
It would then execute an action if placed on the specifically named widget.
To make a project as a beginner, I'd just like to collate different contraptions to make games and potentially study the code and provided documentation.
The script on the “Make Magnet” button just creates a new Canvas widget and adds it to the card. You can set or retrieve the script of a widget with the .script
attribute. So:
mycanvas.script:"on click do alert[\"boo\"] end"
Alternatively, when a widget doesn’t have a handler for a particular event, that event bubbles up to the card, then the deck. If you want a bunch of widgets that all have the same behaviour, you can put the on click ... end
handler in the card, and not have to worry about adding it to each widget individually.
A deck contains cards, cards contain widgets.
When you click a button in the “interact” mode, or a script runs somebutton.event["click"]
, Decker looks inside the button’s script for an on click do ...
handler.
If it can’t find one, the event “bubbles up” to the card, and Decker looks for an on click do ...
handler there.
If it can’t find one, the event “bubbles up” to the deck, and Decker looks for an on click do ...
handler there.
If it can’t find one, Decker has built-in default handlers for certain events (like dragging on a canvas to draw on it), but most events wind up being ignored.