Skip to main content

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

Internet Janitor

869
Posts
56
Topics
7,664
Followers
23
Following
A member registered Aug 02, 2018 · View creator page →

Creator of

Recent community posts

Given a reference to the deck, you can manually obtain the closure for a module like so:

deck.modules.dd.value

See the reference documentation for the Module Interface.

In most cases, though, I'd recommend breaking down the responsibilities of a contraption such that it doesn't internally access or depend upon modules or other contraptions. You might consider a contraption which dispatches events to external scripts, and have those external "widget-level" scripts interact with modules or other deck-level resources, for example. Ideally, modules and prototypes are freestanding, reusable parts.

(1 edit)

I am aware of the terms of my licenses. The licensing of this project does not allow or encourage people to misrepresent themselves as me, or misrepresent their forks as my project. Several users have published customized, extended forks in good faith, and I have no wish to constrain them, or others like them.

I recently published a retrospective for WigglyPaint on my personal site, including my feelings on its explosive popularity, and- unfortunately- its rampant theft, misrepresentation, and fraudulent resale by bad actors: http://beyondloom.com/blog/onwigglypaint.html

Patched. Linux users can rebuild from source immediately.

You might also find the new sys.ops performance counter in v1.64 handy; rearranging expressions to do as much work as possible in a "vector context" rather than a "scalar context" can have a big impact on how many ops and how much wall-clock time is consumed by a hot loop. The "performance" example on the lil playground offers a worked example of micro-benchmarking with this tool.

I've completed an initial draft of the next installment in the beloved All About series, this time focusing on transition effects:


http://beyondloom.com/decker/transitions.html

As always, I'd love to know what you folks think. Any questions? Noticed omissions or typos? Did you learn anything new?

The next release of Decker will include "transitions.deck" in the examples directory, subsuming the previous "PublicTransit.deck" example. Everything is still there, just in a new wrapper, much like the reorganization of the old font editor when I introduced All About Fonts. I've included a tombstone for the web-decker build of the old example on the main Decker site.

Currently native-decker doesn't include a "stretch-to-fit" fullscreen mode.

I suspect that what's happening is you've placed e.g. the string "50,20" into a cell rather than a pair of numbers.

There are several ways of converting a string of comma-separated numbers into a proper list. For example,

"%i,%i" parse "50,20"
0 + "," split "50,20"
eval["50,20"].value     # (this is opening a walnut with a sledgehammer)

Alternatively, if you want to store structured data within a grid cell, you can specify a column's format as "j" (JSON data) or "J" (LOVE data, a superset of JSON) and then enter your pairs like so in CSV mode:


or like so in JSON mode:

Let's say I have a grid widget like this:


The .value attribute of a grid widget is a table value.

Tables can be indexed by a string (a column name) to retrieve a column as a list, or by a number (a row number) to retrieve a row as a dictionary:

somegrid.value[1]
# {"fruit":"cherry","price":0.35,"amount":15}
somegrid.value.price
# (1,0.35,0.75,2.99,0.92)

Thus, if you want a specific cell, you can index by row and then column, or column and then row, whichever way you find convenient:

somegrid.value[1].price  # 0.35
somegrid.value.price[1]  # 0.35

Any questions?

I think Decker v1.64 will help out for this and future similar projects. With no additional alterations, the new image compression functionality brought the 20.3mb version of this deck to 4.7mb. All the other techniques we discussed for shrinking file size are valid, and can still be applied separately, but if the filesize and loading time is acceptable they may no longer be necessary.

(1 edit)

It's a perfectly reasonable question!

The binary "in" operator can- among other things- check for the presence of a substring in a larger string:

"beef" in "ham & beef"      # 1
"apple" in "ham & beef"     # 0

If the left argument is a list of strings, you'll get back a list of 1s or 0s indicating whether each substring was found:

("beef","ham","apple") in "ham & beef"   # (1,1,0)

If you're just asking whether "any" word is found, you can take the "max" (maximum) of the result:

max ("beef","ham","apple") in "ham & beef"   # 1
max ("bone","apple","tea") in "ham & beef"   # 0

Note that all these comparisons are case-sensitive! If you want a case-insensitive comparison, the easiest way is to lowercase the string you're searching and make sure all your substrings are already lowercase:

("beef","ham","apple") in "%l" format "Ham & BEEF"   # (1,1,0)

Does that make sense?

For other kinds of string-searching you might want to consult the Lil reference manual on the "like" operator (which performs glob-matching with * wildcards) and the Decker reference manual on the "rtext.find[]" or "rtext.replace[]" utility functions, which retrieve the index of found words or perform substitutions, respectively. Everything in the "rtext" interface will work on a plain string, too.

The enum contraption exposes its string value through a .value attribute. Internally, like any contraption, this is handled by get_value[] and set_value[].

I think you're going to need to describe more about what you're trying to do- and especially show the scripts you're trying to use- for us to diagnose what's going wrong.

I think of canvas widgets as being sort of like <canvas> elements on web pages: a drawing surface that contains one image at any given time and which can be manipulated with scripts.

Building on what Ahm said, it's certainly possible to create contraptions that bundle together a canvas and storage for a sequence of animation frames, with logic for playing those sequences back. The WigglyKit deck comes with a contraption along these lines called wigglyPlayer.

Here's a little demo video of using a WigglyPlayer to create looping animated "sprites" on top of a drawing, with no custom scripting required:


the random[] function can select from sequences, too:

random[]                 # random float [0,1)
random[5]                # random integer [0,4]
random[5 10]             # list of 10 random integers [0,4]
random["abc"]            # random character "a","b", or "c"
random["abc" 10]         # list of 10 random characters "a","b", or "c"
random[("apple","pear")] # random string "apple" or "pear"
random["abcdef" -3]      # list of 3 shuffled characters "a"-"f" without repeats

See the built-in functions section of the Decker Reference Manual.

flawless ƨƨɘlwɒlʇ

Puppeteer includes a simple screenshake effect. If you're using that library already, you can just include the !shake command in your dialog scripts like in the example. Here's a simplified version of how that works internally that could be used elsewhere- adjusting "shake_mag" and the duration of the transition (here 15) can tune how the effect looks:

go[card on shake_trans c a b t do
  local shake_mag:30,30
  local m:shake_mag*1-t
  c.pattern:1
  c.rect[0,0 c.size]
  c.paste[a (random@2*m)-m]
end 15]

Unfortunately, supporting non-Latin-alphabet languages in Decker is not straightforward. Phinxel has a few suggestions for workarounds, but any way you slice it a great deal of work would be involved.

To hide Decker's main menu, you'll need to lock the deck. (The following page describes how you can save a "protected" copy of the current deck.) Whether you're exporting a protected copy of a deck or using the ordinary save menu, all you need to do to create a web-playable version of a deck is save with an ".html" extension.

When you create your project page on Itch.io, upload the .html file you saved, and mark it as "played in browser". It'll look something like this:

There are also additional settings to configure how your game will be embedded in the web page:

The defaults are mostly fine, but I recommend enabling the "fullscreen button" for most projects.

In K, you'd solve this kind of problem with the "where" (&) operator:

 &0 1 0 0 1
1 4
 *&0 1 0 0 1
1

Lil doesn't have an independent monadic primitive for this operation; it's folded into the query language:

first extract value where 0=map@value from np

This is a little bulky when you're doing something simple.

When you only need a single result, you could consider forming a dictionary:

((0=map@np)dict np)[1]

It's also possible to perform an equivalent "masking" operation just with arithmetic:

m:0,0,0,1,0     # "one-hot" boolean vector
m*keys m        # (0,0,0,3,0)
sum m*keys m    # 3
(1 edit)

In Lil, dot-indexing with an identifier and bracket-indexing with a string are equivalent:

card.parent.event.moving
card.parent.event["moving"]
card.parent["event"]["moving"]
card["parent"]["event"]["moving"]

This is a very convenient shorthand when functions take a single string as their argument, but I can see how it might leave you puzzled about passing extra arguments to the "event" method of deck-parts. You're looking for something like:

card.parent.event["moving" which.text]

Make sense?

I hope y'all think the new features are

See The System Interface: sys.seed.

Given a list,

x:("A","B","C","D")

There are several ways we could find the index of a specific element.

Building a dictionary:

(x dict keys x)["C"]

Querying:

first extract index where value="C" from x

Arithmetic:

sum (keys x) * x="C"

Variants of these methods can also be used to search for several items in parallel:

(x dict keys x) @ "C","A"
# (2,0)
extract index where value in "C","A" from x
# (0,2)
extract index where value="C" from "ABCBABBCC"
# (2,7,8)

If you're ever specifically searching within strings, consider rtext.find[] (which can optionally ignore case, too).

first @ rtext.find["ALPHABETICAL APPLES" "A"]
# (0,4,10,13)

See this thread.

The background image of a card is "card.image", an Image Interface. You can do some kinds of drawing and compositing directly with an Image, but it is less general than a Canvas Interface. Images are simple values which can be created on the fly, while Canvases are widgets that are attached to a deck and have awareness of e.g. the deck's palette, brushes, and fonts.

You could make a canvas the size of an entire card, if you like, and/or draw to a canvas elsewhere and use .copy[] and .paste[] to transfer image data to the card background.

Lil variables are mutable; Lil primitive datatypes (numbers, strings, lists, dictionaries, tables) are immutable. Decker provides several "interface" types which can represent mutable data structures: Array, Image, Sound, and (much more flexibly in the v1.64 Decker release than in v1.63 and earlier) KeyStore.

Within the Decker environment, most closures only live as long as a single event, though "blocking"/synchronous scripts could persist for many frames.

There is no built-in mechanism for serializing a function along with its captured closure, so you can't e.g. stash a callback for later in a field's .data attribute. None of Decker's APIs are designed around callbacks for this reason; we instead send events to deck-parts like widgets or cards.

Modules have closures which persist as long as a deck is open and their script isn't modified, so in principle they offer opportunities for weird and fanciful (ab)uses of mutable closure.

And of course, in Lil scripts run with Lilt, outside the Decker environment, the world is your oyster.

I'm sure many of us have encountered Neko from time to time during our computing adventures. I spent a little time tinkering, and whipped up a version of this iconic pixelated companion for Decker:


If you want this little scamp in your decks, you can copy and paste it from here: http://beyondloom.com/decker/goofs/deko.html

This contraption uses Kenji Gotoh's Neko sprites, as is traditional. If any of you are feeling like some adventure, it's a simple matter to edit the frames stored in the internal "sprites" field to customize your Deko. Perhaps you could add new behaviors, more interactivity, or sounds? Feel free to share your variations or ask question!

(1 edit)

I've made some clarifications to that table to be more precise.

Primitive widgets will ONLY be sent automatic view[] events when they are set to be "animated" AND the card upon which they appear is the active card.

Before the .animated property existed, a cruder way of obtaining a 60hz event pump was to go[] to the current card without any transition effects, indirectly scheduling the view[] event for the card to be re-triggered. One advantage of this old approach- which still works fine- is that by making the "dispatch plumbing" within  a card explicit, it's very easy for blocking scripts to send their own "synthetic"view[] events to the current card and keep everything ticking along in the background. Dialogizer provides a synthetic animate[] event while its blocking dialogs are open that can be forwarded to view[] if desired.

There's nothing inherently wrong with long scripts. If you can describe what your code is doing I might have some suggestions for ways you can simplify and shorten them?

I don't think the load time is too bad at 20mb; it's noticeable, but compared to games made in Unity or Ren'Py loading in a few seconds is still pretty fast.

Looks like you built this game with Decker 1.62; in the 1.63 release I fixed a bottleneck that was slowing down deck loading a bit in native-decker, but it was most noticeable in debug builds. Still, upgrading might make your development a little bit snappier. I'm tinkering with some other performance improvements presently.

Using contraptions as reusable graphical assets is certainly one way to cut down on repeated background images; it looks like you're doing this pretty extensively already for puzzle elements. If any elements are one-offs you could also use canvas widgets as graphical elements you can show/hide, move around the card, or copy from place to place. I was a little surprised to see that you aren't using canvases at all in this deck!

The macFieldWindow contraption is designed for displaying "rich text", which can contain hyperlinks, inline images, and text spans with customized patterns or fonts, but cannot contain arbitrary embedded widgets, like a button. It would be possible to add buttons to the contraption by editing it, but I recommend sidestepping that route for now if possible.

I'm not clear on what you mean by "the font importer". In this tutorial post I describe steps one could follow for building a special-purpose tool for converting bitmaps of glyphs from a particular font collection into Decker fonts, but it makes a variety of simplifying assumptions: assuming glyphs are a particular size, that they appear in ASCII order, that the imported image is a black-on-transparent image that's already completely ready to use, and so on. The image you posted contains antialiased white-on-transparent glyphs; you'll have a much easier time importing and editing them as black-on-white or black-on-transparent, and they will need to be flattened into 1-bit color.

If you have a set of glyphs as an image, a somewhat more general approach to building a font from them would be to use the Font Editor that comes with Decker (examples/fonts.deck) and which I linked previously. The "sheet" mode of this tool is designed to break the card background image into uniform-sized glyphs based on the boundaries designated by the invisible button named "workzone". The grid overlay and "snap to grid" mode can make it easier to select and reposition glyphs within this tool so that Decker will understand which DeckRoman character they each correspond to. You'll need to make sure your new font's glyph size is set properly and that "workzone" is positioned to enclose all the glyphs before you can "Apply" your changes to the sheet, and if you don't want a monospaced font you'll probably need to make additional refinements to each glyph's sizing in the "glyph" tab.

1. Decker supports a limited set of unicode characters within text, and unrecognized characters are turned into character 255, "UNKNOWN", upon ingestion. You can, however, very easily customize how this character appears in a given font using the font editor. (The font editor is part of a larger deck All About Fonts that contains a variety of other notes you might find useful).

2. It isn't possible to use a different font within a field exclusively while text is highlighted, but you could certainly make intentionally illegible custom fonts that could still be copied to the clipboard as ordinary text. This would be mechanically a bit different, but might still preserve a similar idea. Perhaps there could be some affordance within the game for giving a player a "scratchpad" field they can paste into? You could alternatively build a contraption of some kind which encapsulates swapping out the font of a field when it is clicked, but it would be difficult to provide this kind of behavior on a granular level and still permit selection. In a locked field, links could be used to trigger arbitrary scripts when particular text fragments are clicked, but they do have a recognizable visual appearance: a dotted underline.

3. You cannot select text within a locked field, but there are a few subtle consequences of how field and text patterns work which might allow you to accomplish something similar to what you're describing:

  • A transparent field on top of a black background (with text in the default pattern 1) will appear to be invisible until it is selected, in which case it will appear white.
  • If you change the pattern of a span of text within a "rich text" field to white (rather than changing the default pattern for the entire field), it will appear to be invisible until it is selected, in which case it will appear white within a black selection highlight.

As a general note, some of the limitations of text and selections within Decker are a consequence of its approach to ensuring that fields are usable on keyboardless devices like tablets. The "touch input" mode (which can be manually enabled via the Decker menu) will interfere with some kinds of nasty text obfuscation tricks.

(1 edit)

Yep. There really isn't much I can do about it.

I feel deeply violated by people stealing my projects and building their own side-hustles on top of them. It is profoundly demotivating.

It absolutely is not. I have only published WigglyPaint here (on itch.io) and on newgrounds.com. If you see people sharing links to pirate sites, please ask them to link to this page on itch.io instead. Pirate sites may harbor malware or ads, and are frequently outdated and encrusted with misleading slop.

I exchanged emails with SssAdmin and they agreed to change the name of their App.

You might find it useful to take a look at lildoc.lil in the Decker repository. It's a markdown processor and Lil syntax highlighter used for generating all of Decker's documentation.

Depends on what you want, I suppose?

Out of the box, with no extensions or scripting, Decker is already a perfectly serviceable presentation tool: make slides as cards, put text in fields, doodle on card backgrounds, flip through them with arrow keys.

The included PDF module offers a starting point for making slide-decks capable of exporting printable thumbnail versions of themselves.

If you wanted slides to have rigid "templates" and a centralized way of altering or applying these styles across a large deck, you could consider making a series of full-card-sized contraptions- a contraption for title cards, a contraption for bulleted lists with a heading, etc- and placing one contraption instance on each slide.

It would also certainly be possible to write a lilt script that "compiled" a textual syntax like Adelie into decks, or perhaps a module that interpreted them on the fly.

(1 edit)

I was not asked for my permission. I don't appreciate my free software being repackaged and sold for a profit, especially when it's re-published using the same name as my original software, using domain names that likewise imply that their products are somehow "official".

The dialog boxes controlled by dialogizer are canvas widgets located on a specific card. You shouldn't go[] to a different card while a dialog box is open. There are plenty of other ways to hide a transition if you're really insistent, like hiding and showing other widgets on the current card or even using the sledgehammer approach and replacing the background image of the current card with a screenshot of a different one:

card.image.paste[app.render[someOtherCard]]

(Note that this will destroy any previous background image on the current card!)