Skip to main content

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

Internet Janitor

883
Posts
56
Topics
7,751
Followers
25
Following
A member registered Aug 02, 2018 · View creator page →

Creator of

Recent community posts

Dialogizer runs in a "blocking" manner; Decker won't process click[] events or other interactions with ordinary widgets while Dialogizer is active, so you wouldn't be able to furnish that kind of functionality with ordinary buttons on a card.

You could, however, use the animate[] event that Dialogizer continuously emits while dialog boxes are open, in combination with the Pointer interface- which provides live-updating access to the state of the user's pointing device- to explicitly detect clicks on extra buttons.

If pointer.start and pointer.pos are within a given rectangle and pointer.up is truthy, you'll know the "button" corresponding to that rectangle was clicked. You could even physically have buttons with click event handlers on the card if you want. Presuming a button called "menu", you might have an animate[] script something like:

on within pos rect do
 min(pos>rect.pos)&(pos<rect.pos+rect.size)
end
on animate do
 if within[pointer.start menu]&within[pointer.pos menu]&pointer.up
  menu.event.click
 end
end

Make sense?

Note that this technique works best when a dd.ask[] dialog is open; ordinary dd.say[] dialogs will automatically advance for any pointer click irrespective of what else happens in animate[]. If you really want fine-grained control here you might need to make minor alterations to the dialogizer module itself. I'll ponder this kind of use-case and possibly make some enhancements to the module in the future.

Please make sure you're using the latest Decker release; as of this comment v1.65.

Rich-text links can contain any string as their "link payload", and rich-text fields generate a link[] event with this string as an argument when clicked. (See: Events in the decker reference manual.)

If you examine the link[] event handler in the card-level script of the deconstructed twine player example, you can see how the event changes the active passage and renders the new one. Introducing an appropriate convention for the link payload- say, an "@"" prefix- would make it reasonably straightforward to make certain link events trigger different events on the current card instead of changing passage and re-rendering. With some internal surgery you could also make similar changes to the behavior of the PlyPlayer contraption.

Make sense?

itch.io GIF upload was broken for several months. A faulty re-encoder stripped animations to their first frame.

In "Interact" mode, pressing arrow keys fires a navigate[] event to the current card. The default navigate[] event handler flips between cards:

on navigate x do
  if x~"right" go["Next"] end
  if x~"left"  go["Prev"] end
end 

But you can override this behavior in the deck-level script to do nothing instead:

on navigate do end

See "consider a custom navigate event" here for another example: https://ahmwwmaaa.neocities.org/decker/phield-notes#publication

It would be best to explain more broadly what you're trying to accomplish. Is the entire statement dynamically constructed from text, or are you simply trying to, for example, use indirect references to the widget names?

n1:"canvas_16"
n2:"canvas_1"
cw:images_color.widgets
c1:cw[n1]
c2:cw[n2]
c1.paste[c2.copy[]]

The eval[] function can be used to evaluate a dynamic snippet of Lil; it returns a dictionary with any syntax error information (if the snippet was malformed) or a result and variable bindings:

eval["2+3"]
# {"value":5,"vars":{}}

By default, eval[] is only aware of variable bindings (including definitions of built-in functions like eval[] itself, deck parts, etc) that you explicitly pass into it as a dictionary- its second optional argument. This makes eval[]-ed code "safe by default"; it is sandboxed to play only with what you give to it.

eval["a+b" ("ab" dict 11,22)]
# {"value":33,"vars":{"a":11,"b":22}}
z:99 eval["z+5"]
# {"value":5,"vars":{}}

If you do want to include local bindings, a truthy third argument will opt into including them; this means everything that was in scope from the caller's perspective will be available to the code snippet:

a:11
b:33
eval["a+b" () 1]
# {"value":44,"vars":{}}

As always, when exploring unfamiliar scripting functionality I recommend trying things step-by-step in the Listener.

Per your question, something like the following might work:

eval["canvas_16.paste[images_color.widgets.canvas_1.copy[]]" () 1].value

But, again, please take a step back and make sure this is what you really want to do. As problem-solving tools go, eval[] is something of a sledgehammer, and you may only need scissors or a pair of tweezers.

Make sense?

(1 edit)

In the example snippet for this contraption, it's placed on a card with the following script:

on view do
 prev.locked:!chat1.hasprev
 next.locked:!chat1.hasnext
end

The "prev" button has the script:

on click do
 chat1.prev[]
 view[]
end

And the "next" button has the script:

on click do
 chat1.next[]
 view[]
end

Together, these scripts automatically lock/unlock the buttons based on whether or not the contraption has a previous or next chat message available to display, and updates that status every time the user pages through messages. None of this is strictly necessary; it's just there to make the UI more legible. You can customize these buttons as you please or get rid of them entirely and advance the chat log via some other means if you prefer.

Does that help clarify the behavior you're seeing?

Just in case you didn't see it already, we have a library available for using Decker in concert with Twine: Twee.

In the simplest cases you can write normal Twine passages using the "Ply" story format, which is like an extremely stripped-down version of Harlowe, export a .twee file and pop it into a "plyPlayer" contraption; no other coding required. With a little scripting there are many ways to make a Ply story interact with a deck or vice versa- change cards, display visuals, play sound effects, etc.

I hope you'll find that Decker is a very flexible digital arts-and-crafts medium that you can use for a wide range of creative projects, tools, toys, and games. Welcome to the community!

If you want background music playing while a deck remains interactive, you will definitely need a more complicated approach than sleep[]; the discussion in this thread has some examples that may do what you need.

Hmm. I can tinker with the internals of image.rotate[] to try to make it better behaved. When you need exact 90 degree rotation increments, image.transform[] is more efficient, and will always be precise. Here's a routine you could use as a workaround for now:

on rotated img steps do
 # modify 'img' in-place based on 45 degree steps:
 if     steps~0 # do nothing
 elseif steps~2 img.transform["right"]
 elseif steps~4 img.transform["right"].transform["right"]
 elseif steps~6 img.transform["left"]
 else           img.rotate[steps*pi/4]
 end
 img
end

In that case you might want to consider giving the buttons a script that delegates to your link[] logic:

on click do
 link["Back"]
end
(1 edit)

Hmm. Tricky.

How about something like this?

on link x do
 here:deck.card
 send link[x]
 there:deck.card
 if !(here.widgets.channel.text=there.widgets.channel.text)
  go[here]
  go["Back" "Wink"]
 end
end

Since you're overriding this behavior on many cards, it's easiest to define an overload for the link[] event at the deck level; see Deck -> Properies... -> Script... via the menu. I'll assume for the sake of this example that cards are named with a "group_cardname" convention, so we can use the portion of the name preceding the underscore to determine whether two cards belong to the same group:

on link x do
 on groupname x do
  first "_" split x
 end
 if groupname[deck.card.name]~groupname[x]
  send link[x]
 else
  go[x "BoxIn"]
 end
end

Make sense?

You may have a different method of distinguishing groups, like invisible metadata fields on cards; I'd be happy to clarify how to adapt this approach to your deck, if needed.

If you haven't already read it, I recommend checking out All About Sound.

In a simple linear script, like part of a cutscene, playing sounds in sequence is straightforward with sleep["play"]:

play["sound1"]
# do anything else you want while the sound is playing...
# ...
sleep["play"] # wait for all playing sounds (sound1) to finish
play["sound2"]

In more elaborate cases, with sounds playing while a deck remains interactive, you may want to rely upon the loop[] event or keep track of your own timer to trigger subsequent sound effects.

Does this help point you in the right direction?

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!