Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Internet Janitor

812
Posts
51
Topics
7,148
Followers
22
Following
A member registered Aug 02, 2018 · View creator page →

Creator of

Recent community posts

State in a deck, like a score counter, needs to live in a widget, on some card.

The straightforward way to have a global score counter would be to make a field (or slider) widget somewhere on a specific card; let's call the card "inventory" and the field "score". Scripts on another card can then refer to that field by one of its "fully-qualified" names:

deck.cards.inventory.widgets.score
inventory.widgets.score

For example, to add 100 points to the score, a button script might be

on click do
 inventory.widgets.score.data:100+inventory.widgets.score.data
end

Or, using a local variable to reduce repetition,

on click do
 s:inventory.widgets.score
 s.data:100+s.data
end

In fact, if you're referring to this widget constantly throughout your deck, you might consider setting up an alias in the deck-level script:

scoreCounter:deck.cards.inventory.widgets.score

After which point you could just use the name "scoreCounter" in any script, on any card.

Each card could have their own "score" field (perhaps volatile) that is drawn from the central source-of-truth score counter and updated when the card gets a view[] event:

on view do
 score.data:scoreCounter.data
end

(Note that when you change the score counter you will generally also want to call view[] to perform this update.)

Does that make sense?

If you're interested in making this even tidier- at the cost of some additional up-front complexity- I could describe how to use a contraption to build a "game HUD" that behaves like a single object shared across many cards.

For the record, I'd strongly recommend updating Decker.

v1.54 is over 8 months old; we're up to v1.61.

There are many reasons a card can be sent a view[] event, including exiting editing tools, (re)opening a deck, or closing a dialog box. It should not be understood as an event that is fired when a card is visited at all, but rather a request to update the contents of a card. I do not recommend attempting to use timing as a method to disambiguate the origin of a view[] event; such methods are inherently brittle and error-prone.

To address may's problem, I would recommend altering the script on the originating card to send a new event- one not generated by Decker itself- to the destination card after the transition. Let's call the new event "visited":

on click do
 go["Next" "SlideDown"]
 deck.card.event["visited"]
end

(Note that after go[] has completed, "deck.card" will refer to the card we just navigated to.)

The destination card can then fire the dialog in response to this "synthetic" event:

on visited do
 dd.open[deck a]
 dd.say["Kindling Village"]
 dd.close[]
end

Update: the jam page for Dec(k)-Month The Third is up!

Reviewing vactur, it seems like some of these operations reimplement Lil primitives or brief idioms. For example,

vac.constant: on _ n c do
  on _ do c end @ (range n) end
end

Replicates n copies of a constant c. This could be implemented in terms of the primitive "take"; possibly with an enclosed version of c if it may be something other than an atom:

3 take 5
# (5,5,5)
3 take list 11,22
# ((11,22),(11,22),(11,22))

Likewise,

vac.isin: on _ v s do
  t: vac.constant[(count v) 0]
  each x in s
    t: t | (v = x)
  end
  t
end

The core of this loop could be expressed as

max each x in s v = x end

But the whole operation appears to be equivalent to the primitive "in":

(2,3,5) in (4,11,17,5,3)
# (0,1,1)

The "partitioning[]" function is implemented in terms of "where" (a primitive in some APL-family languages, but not Lil); 

vac.where: on _ m do
  extract index where value from m
end
vac.partitioning: on _ m do
  vac.where[m],vac.where[!m]
end

But we can do the equivalent in a single query by using a "by" clause to group rows:

extract index by value from m

Not only does this reduce overhead, it makes the formulation more general:

vac.partitioning[1,0,1,1,0,1]
# (0,2,3,5,1,4)
extract index by value from 1,0,1,1,0,1
# (0,2,3,5,1,4)
extract index by value from "ABAABA"
# (0,2,3,5,1,4)

Please don't take this as discouraging your efforts; just pointing out some alternatives.

There are quite a few similar pirate websites spawned from the overwhelming popularity of WigglyPaint. None of them are by me, or created with my permission. I have no ability to update the version of WigglyPaint they provide, I cannot vouch for those sites being free of ads or malware, and many of them don't link to my itch.io pages or my personal website (beyondloom.com), so they actively prevent the discovery of my other projects.

I also have never published Android or iOS "apps" for WigglyPaint or Decker; anything you find to the contrary is illegitimate.

I would appreciate it if people refrained from linking to these pirate sites and apps. I don't use social media, so it is impossible for me to even consider correcting the countless misinformed users who share these URLs with one another. It is very frustrating to offer my work for free and still have it stolen.

When the arrow keys are pressed, a navigate[] event is sent to the current card with an argument of "up", "down", "left", or "right". If you wanted to make these events move a canvas, you might have an event handler in the card-level script something like

on navigate dir do
 deltas:("up","down","left","right") dict ((list 0,-1),(list 0,1),(list -1,0),(list 1,0))
 myCanvas.pos:myCanvas.pos+deltas[dir]*32
end

If you wanted that canvas to trigger "exits" when it matches certain positions, you could mark them with buttons (perhaps set to "Show None") and then send them a "click" event on an overlap; the buttons could in turn go[] to other cards, trigger alert boxes, etc.

on navigate dir do
 deltas:("up","down","left","right") dict ((list 0,-1),(list 0,1),(list -1,0),(list 1,0))
 myBug.pos:myBug.pos+deltas[dir]*32
 each button in extract value where value..type="button" from card.widgets
  if button.pos~myBug.pos button.event["click"] end
 end
end


For a fully-worked example of a game that uses arrow keys for tile-based movement, you should take a look at Decker Sokoban.

If you give columns in a grid widget a format character of "L", "I", or "T", the column will not be user-editable. L displays cells in a column as plain strings (preformatted), I interprets cells as an icon index, and T displays cells as non-editable Rich Text.

Grids are containers for tables. To rearrange the rows of a table you probably want a query with an orderby clause. For example, sorting the contents of a grid "myGrid" by the "price" column, ascending, might require a button with a script like

on click do
 myGrid.value:select orderby price asc from myGrid.value
end

Note that the default order[] event handler for grids offers something like this out of the box; you could overload this on a grid-by-grid basis if you wanted more elaborate behavior:

on order col do
 if !me.locked
  me.value:select orderby me.value[col] asc from me.value
 end
end

If you have a column in a table, you can index it by name to get a list. The "fuse" operator can convert any list into a flat string:

myGrid.value.fruit
# ("cherry","banana","elderberry","apple","durian")
"" fuse myGrid.value.fruit
# "cherrybananaelderberryappledurian"
"|" fuse myGrid.value.fruit
# cherry|banana|elderberry|apple|durian

The simplest way to perform ciphering would be to use a dictionary. We can make a dictionary that maps characters to other characters like so:

rot13: "abcdefghijklmnopqrstuvwxyz" dict "nopqrstuvwxyzabcdefghijklm"

Given a string, we can use each character to index into our dictionary with the "@" operator:

rot13 @ "something else"
# ("f","b","z","r","g","u","v","a","t",nil,"r","y","f","r")

For characters like spaces, which aren't defined in the rot13 dictionary, we get a nil. As a catchall, we can use "fill" to substitute a generic replacement for all nils:

" " fill rot13 @ "something else"
# ("f","b","z","r","g","u","v","a","t"," ","r","y","f","r")

And if we wanted to collapse that sequence of characters into a flat string, we can use "fuse". Note that this step isn't necessary if we're stashing the result in a field's .text attribute; it implicitly fuses lists of strings as a convenience:

"" fuse " " fill rot13 @ "something else"
# "fbzrguvat ryfr"

Putting that all together, given fields "input" and "output", give "input" a change[] handler:

on change do
 rot13: "abcdefghijklmnopqrstuvwxyz" dict "nopqrstuvwxyzabcdefghijklm"
 output.text:" " fill rot13 @ input.text
end

And now we have a (crude) rot13 translator. It would probably be much more useful if we preserved all the non-alphabetic characters intact instead of turning them all into spaces. We can be a little fancier about how we construct the translation dictionary and address this:

on change do
 a1:"abcdefghijklmnopqrstuvwxyz"
 a2:"nopqrstuvwxyzabcdefghijklm"
 A1:"%u" format a1
 A2:"%u" format a2
 chars:"%a" format list range 256
 rot13:(chars dict chars),(a1 dict a2),(A1 dict A2)
 output.text:rot13 @ input.text
end

If you want to do a translation based on fixed-size bigrams or trigrams, or something along those lines, "window" might be useful:

2 window "SomeWords!"
#("So","me","Wo","rd","s!")

And you can use "drop" to exclude characters you wish to ignore (or conversely, "take" to retain only characters you care about):

(" ","!") drop "Some Words!"
# ("S","o","m","e","W","o","r","d","s")

For even fancier find-and-replace scenarios you could use RText.replace[]. Note that this returns a rich-text table and should be assigned to the .value attribute of a field, rather than .text, or converted into a string with rtext.string[].

rtext.replace["some text" ("me","ex") ("you","post")]
# +----------------+------+-----+-----+
# | text           | font | arg | pat |
# +----------------+------+-----+-----+
# | "soyou tpostt" | ""   | ""  | 1   |
# +----------------+------+-----+-----+
rtext.string[rtext.replace["some text" ("me","ex") ("you","post")]]
# "soyou tpostt"

Does any of that help?

You have several approaches available.

The @ operator can be used to index a list, dictionary, or table by a list of indices or keys. For example:

items.value
# +--------------+-------+--------+
# | fruit        | price | amount |
# +--------------+-------+--------+
# | "apple"      | 1     | 1      |
# | "cherry"     | 0.35  | 15     |
# | "banana"     | 0.75  | 2      |
# | "durian"     | 2.99  | 5      |
# | "elderberry" | 0.92  | 1      |
# +--------------+-------+--------+
items.value.fruit @ 0,2,3
# ("apple","banana","durian")

Indexing a table in this way gives us a list of rows (each a dictionary), but we can glue those back together with the "table" operator:

items.value @ 0,2,3
# ({"fruit":"apple","price":1,"amount":1},{"fruit":"banana","price":0.75,"amount":2},{"fruit":"durian","price":2.99,"amount":5})
table items.value @ 0,2,3
# +----------+-------+--------+
# | fruit    | price | amount |
# +----------+-------+--------+
# | "apple"  | 1     | 1      |
# | "banana" | 0.75  | 2      |
# | "durian" | 2.99  | 5      |
# +----------+-------+--------+

To clarify my earlier examples, in most applications the "in" operator attempts to check whether an item on the left (or each item in a list on the left) appears within a list on the right:

3 in 2,4,5        # 0
5 in 2,4,5        # 1
(3,5) in 2,4,5    # (0,1)

Within a query, you could do something roughly equivalent to our first example by referencing the implicit "magic column" named "index":

select where index in 0,2,3 from items.value
# +----------+-------+--------+
# | fruit    | price | amount |
# +----------+-------+--------+
# | "apple"  | 1     | 1      |
# | "banana" | 0.75  | 2      |
# | "durian" | 2.99  | 5      |
# +----------+-------+--------+

If your desired indices are stored in a table, you'll need to pluck out a column of that table before you can compare it to another column with "in" or index with "@":

invgrid.value
# +-----------+
# | itemIndex |
# +-----------+
# | 0         |
# | 2         |
# | 3         |
# +-----------+
invgrid.value.itemIndex
# (0,2,3)
table items.value @ invgrid.value.itemIndex
# +----------+-------+--------+
# | fruit    | price | amount |
# +----------+-------+--------+
# | "apple"  | 1     | 1      |
# | "banana" | 0.75  | 2      |
# | "durian" | 2.99  | 5      |
# +----------+-------+--------+
select where index in invgrid.value.itemIndex from items.value
# +----------+-------+--------+
# | fruit    | price | amount |
# +----------+-------+--------+
# | "apple"  | 1     | 1      |
# | "banana" | 0.75  | 2      |
# | "durian" | 2.99  | 5      |
# +----------+-------+--------+

You might consider keeping track of inventory items based on their names, or some other semantically meaningful "id" or key column, rather than by index; it could be more human-readable, and if you make any mistakes writing your queries it may be more obvious where something has gone wrong.

I would also like to note that grids aren't the only way to store collections of data within Decker, and may not always be the simplest choice. For example, you could also store a list of indices in a field widget via its .data attribute. Say we have a field named "myList":

myList.data:11,22,33

The field will visually show the data as "[11,22,33]". If we read myList.data we can reconstitute the list:

myList.data   # (11,22,33)

Does that help?

Grid widgets, like this example named "items":


contain tables. You can read or write the table of a grid widget via the .value attribute of the grid. Tables can be indexed numerically (looking up rows) or by strings (looking up columns):

items.value
# +--------------+-------+--------+
# | fruit        | price | amount |
# +--------------+-------+--------+
# | "apple"      | 1     | 1      |
# | "cherry"     | 0.35  | 15     |
# | "banana"     | 0.75  | 2      |
# | "durian"     | 2.99  | 5      |
# | "elderberry" | 0.92  | 1      |
# +--------------+-------+--------+
items.value[3]
# {"fruit":"durian","price":2.99,"amount":5}
items.value.fruit
# ("apple","cherry","banana","durian","elderberry")

In the simplest case of populating another grid with a specific column of a source grid, assigning a list to the destination grid's .value attribute will implicitly cast it to a table:

dest.value:items.value.fruit


For more complex transformations of data, there's Lil's query language:

select fruit price where amount>2 from items.value
# +----------+-------+
# | fruit    | price |
# +----------+-------+
# | "cherry" | 0.35  |
# | "durian" | 2.99  |
# +----------+-------+

A query never modifies tables in-place; it returns a new table. Just like before, we can take the source grid's .value, query it, and assign the result to another grid's .value attribute:

dest.value:select fruit amount where fruit in "cherry","banana" from items.value


There are many examples of using queries on tables in the Lil reference manual section Lil, The Query Language. If you aren't using it already, Decker's Listeneris very helpful for trying queries interactively or generally testing out snippets of code.

Does any of that point you in a useful direction? I can try to furnish more specific examples of how to formulate queries for your project if you provide additional detail about how your data is organized.

This is really fun- thanks for sharing!

If you set the "shortcut" field for the buttons in your "navigator" contraption you can make them respond to the a/w/d keys for keyboard-based movement; it would also be possible to overload the top-level navigate[] event if you wanted to respond to cursor keys or swipe gestures as another input method.

The "rows" primitive turns a table into a list of dictionary, with each dictionary representing a row.

You can thus iterate over the rows of the table stored in a grid something like:

each row in rows mygrid.value
 # ...
end

More broadly, though, what are you trying to do? There might be better alternatives depending on the application.

As a concrete example, assuming the default palette, if you wanted to set animated pattern 28 to red and black alternating as fast as possible you could use The Listener to issue the following Lil snippet:

patterns[28]:35,47

To halve the speed you could double up each pattern index, e.g.:

patterns[28]:35,35,47,47

There are more examples of this sort of thing in All About Color.

Is that clear?

I only publish WigglyPaint on itch.io and newgrounds.com.

Can you believe we've been doing this for three years now?

I just posted a summary of everything added and changed in Decker between versions 1.44 and 1.60: http://beyondloom.com/blog/unionstate3.html

You can set the .widths property of a grid widget to a list of column widths in pixels. Any columns thus set to 0px wide will be completely hidden from view and skipped over when navigating by cell. Grids will also automatically hide any rightmost columns that don't fit within the width of the widget, so making leftward columns a bit wider can scrunch the secret columns out of view. (Columns have resize handles in between column labels while in Interact mode!)

The .text attribute of a field is a string. Comparing numbers to strings in Lil can have surprising results, because in this case Lil makes the comparison lexicographically:

3>"12"  # 1
3>12    # 0

To avoid this problem, you can use an arithmetic identity operation to coerce the string into a number before performing a comparison:

  myField.text  # "12"
0+myField.text  # 12

Alternatively, use the .data attribute of the field instead: this will ask Decker to interpret the contents of the field as LOVE (a superset of JSON):

myField.text     # "12"
myField.data     # 12
otherField.text  # "[11,22,33]"
otherField.data  # (11,22,33)

Yet another option would be to store numbers in Slider Widgets instead of fields; a slider's .value attribute is always a number. Does that help?

I did some pondering and tried to come up with an approach that minimizes the amount of scripting needed:

http://beyondloom.com/decker/goofs/petquiz.html

The structure of the deck is as follows:


  • Question cards are named with a "q_" prefix.
  • Quiz results each have their own sensibly-named card.
  • The options on question cards are each a checkbox whose name corresponds to the name of one of the result cards.
  • The checkboxes can appear in different orders on each question cards, and not all possible results must be present.
  • The checkboxes on question cards are marked as volatile, so that they can all be reset with "deck.purge[]", as a convenience.
  • Clicking a checkbox simply advances to the next card. Since they all have the same behavior, we can put the following in the card script for each question (or, if we're feeling feisty, we could even define it once in the deck-level script):
on click do
 go["Next"]
end

When we get to the last card of the quiz, the user clicks a button to discover their result. The script there will find all the question cards based on the "q_" naming convention, look at all their checkboxes, and count up the number of checks for a given result name. The name which had the most checks will be the name of our result card:

on click do
 cards:extract value where value..name like "q_*" from deck.cards
 counts:sum each card in cards
  raze select key value..value where value..type="button" from card.widgets
 end
 go[first extract key orderby value desc from counts]
end

This is probably a bit daunting. Using the Listener, we can break this script down step-by-step; assume we're on the card "decide" and we've already filled out our quiz responses.

Find the question cards:

cards:extract value where value..name like "q_*" from deck.cards
# (<card>,<card>,<card>)

For now, consider only the first card. Find the button widgets and form a table from their names ("key") and checked status ("value..value"):

select key value..value where value..type="button" from cards[0].widgets
# +-----------+-------+
# | key       | value |
# +-----------+-------+
# | "chicken" | 1     |
# | "fish"    | 0     |
# | "dog"     | 0     |
# | "cat"     | 0     |
# +-----------+-------+

Razing a table forms a dictionary mapping its first column to its second column:

raze select key value..value where value..type="button" from cards[0].widgets
# {"chicken":1,"fish":0,"dog":0,"cat":0}

We'll need to do this process for each card, not just the first one:

each card in cards
 raze select key value..value where value..type="button" from card.widgets
end
# ({"chicken":1,"fish":0,"dog":0,"cat":0},{"fish":0,"cat":1,"chicken":0,"dog":0},{"dog":0,"chicken":1,"cat":0,"fish":0})

If we sum a list of dictionaries, we'll get a dictionary with the union of the keys in each dictionary and the sum of all the corresponding values:

counts:sum each card in cards
 raze select key value..value where value..type="button" from card.widgets
end
# {"chicken":2,"fish":0,"dog":0,"cat":1}

To find the key with the largest value, we'll sort the keys by the values (descending):

extract key orderby value desc from counts
# ("chicken","cat","fish","dog")

The first element of that list is our answer:

first extract key orderby value desc from counts
# "chicken"

So we can go[] to the corresponding card by name:

go[first extract key orderby value desc from counts]

This query doesn't care about how many results exist, how many questions are in the quiz, or how many answers are available for each question; you can easily add to the quiz or make an entirely different quiz without modifying the query.

(Note: this quiz won't work right if your checkboxes are named inconsistently. If you seem to get goofy results, you can try the above step-by-step in the Listener to see if you get any unexpected options in this result list!)

Does that make sense?

You could make a dictionary that maps slider names to result cards and "go[]" to the card corresponding to the top-ranked result:

on click do
 sliders:likesDogs,likesCats,likesFish,likesChickens
 order:extract value..name orderby value..value desc from sliders
 results.likesDogs:"TheDogCard"
 results.likesCats:"TheCatCard"
 results.likesFish:"TheFishCard"
 results.likesChickens:"TheChickensCard"
 go[results[first order]]
end

If your quiz result cards are named to correspond to the sliders, you could skip the dictionary and "go[]" to a card by name:

on click do
 sliders:likesDogs,likesCats,likesFish,likesChickens
 order:extract value..name orderby value..value desc from sliders
 go[first order]
end

(Using consistent naming conventions for cards and widgets can often make scripts a good bit simpler!)

If the sliders aren't on the same card as the result-calculating button, you'll need to reference them through their container card. Say the card with the counters is named "counters":

on click do
 sliders:counters.widgets.likesDogs,
         counters.widgets.likesCats,
         counters.widgets.likesFish,
         counters.widgets.likesChickens
 order:extract value..name orderby value..value desc from sliders
 go[first order]
end

Or- more concisely-

on click do
 sliders:counters.widgets @ "likesDogs","likesCats","likesFish","likesChickens"
 order:extract value..name orderby value..value desc from sliders
 go[first order]
end

Or, if the sliders are the only widgets on that card:

on click do
 sliders:range counters.widgets
 order:extract value..name orderby value..value desc from sliders
 go[first order]
end

Does that help at all?

If you find yourself struggling to apply these ideas, let us know what you've tried and I can create a more complete example for you to study and tinker with. There are a fair number of concepts at play, but you don't need to learn everything at once. :)

(1 edit)

Decker is fairly well-suited to making dollmaker-style games.


I did a brief writeup on modifying The Ornamented Ovum over on the Decker community forum: https://itch.io/t/2769322/the-ornamented-ovum . Anyone is welcome to use this game as a starting point for their own creations!

Decker comes with a number of interactive tutorial decks, but the most immediately relevant ones might be All About Draggable and All About Color.

Uploading animated GIFs on itch.io is presently broken. It's a real bummer and I hope it's fixed soon. :/

If you really want to distinguish a prototype from a contraption, you could check the typeof "card" rather than "card.parent". There are other visible api differences, like contraption.def and prototype.widgets, but most of the time it's desirable for them to seem the same during prototype editing.

FlickGame is a very simple game engine for making short-form point-and-click games; you can find hundreds of games created with this tool here on itch.io alone!

Today I wrote a tool- Flicker- with Decker which can decode .html exports generated by FlickGame and convert them into a deck with the same behavior. It is then possible to use Decker's normal editing tools and scripting capabilities to enhance or modify the games, or simply run them on machines that don't have a web browser. The tool can use a customizable naming convention for producing cards to help support merging multiple FlickGames into a single deck.



Try it here with any of the games on the FlickGame gallery!

I hope this is a helpful tool for FlickGame enthusiasts and a useful demonstration of how Decker can make tools that in turn create other decks. Questions, anyone?

Millie helped me track down the source of your reported crash. A fix is included in Decker 1.60.

With the selection still active, you can choose "Edit -> Resize to Original" or "Edit -> Resize to Card" from the menu to preserve the original dimensions of the image or snap it to the dimensions of the card, respectively.

Wigglypaint is made in Decker. The desktop version of Decker can run a saved .deck or .html export.

(1 edit)

Well, I'm afraid text entry in Web-Decker is a little bit of a mixed bag; it depends on the combination of your operating system, web browser, and your active keyboard layout.

On MacOS, for example, the US-English keyboard layout allows characters like "ñ" to be typed with a "dead-key" combination: option+n to latch in eñe-mode, and then separately "n" to complete the sequence. Problem is, web browsers don't emit any key events for the dead key: Web-Decker only sees "n". If you're using a keyboard layout where ñ can be typed directly- without dead-keys- it will probably work, but it also may not?

On touch devices, Web-Decker will show a touch keyboard, and all the non-ASCII DeckRoman characters can be found under the "alt" key on that keyboard, using a layout loosely adapted from classic MacOS. In unlocked decks, Decker also allows a user to manually summon this keyboard with Edit -> Keycaps... from the menu. The intention is to help people work around limitations of their local keyboard layouts when they need an unusual character, but it only helps for authoring; the menu is hidden in a locked deck.

Copying and pasting text in from another application should always work, as should opening and viewing decks that already contain DeckRoman characters.

Depending on what you're trying to do, some of these workarounds may help, I hope?

Well done. What a delightful- and thematically apt- application of publictransit!

I would recommend reading and asking some questions on the decker community forum. There are many Decker users there that would be happy to help. Try to explain what you want to accomplish in as much detail as possible. It also helps to describe what you have tried previously, so we can get a clearer idea of what you're struggling with.

(1 edit)

In addition to Dialogizer, which offers blocking modal dialogs like a more general version of alert[], WigglyKit includes an example of a full-screen contraption which behaves like a non-blocking modal dialog; might be worth a look. While alert[] is not very visually customizable, it is worth noting that you can specify the first argument as rtext, which includes the possibility of specifying fonts, colors/patterns, and supplying inline images.

(1 edit)

Great explanation, Screwtapello!

In the general case, if you want a specific row of a table at a known index as a dictionary, I'd recommend skipping the query and instead using simple indexing. The following lines are both equivalent to your third example (assuming you just want every column with its original name and order):

piece_data: (rows me.value)[row]
piece_data: me.value[row]

Enable "transparency mask" in the options screen.

Currently grid settings aren't persisted anywhere, but I'll give it some thought; possibly it could be stored in deck files, or perhaps it might make sense to expose scripted control via the app interface.

Sounds similar to issue #78, which was traced to a regression in SDL2.

You can start Decker with the "--no-audio" flag to completely disable sound.

(1 edit)

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?

Decker 1.58 revises the behavior of the text-span styling commands in the Text menu; It's now much easier and less confusing to create links which use special fonts.

As described elsewhere, the source repository for Decker can be found here. It requires both SDL2 and SDL2_image.