Skip to main content

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

Internet Janitor

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

Creator of

Recent community posts

Different browsers have inconsistent behavior regarding file:// and localhost-served lone pages; in many cases a user would need a local webserver and a self-signed ssl certificate. It's a very frustrating foot-gun that's especially difficult and confusing for people who aren't already steeped in web development.

When a row or cell of a grid widget is clicked, it is sent a click[] event with the active row as an argument. See Events in the Decker reference manual.

on click row do
 if row~0
  alert["you clicked the first one"]
 elseif row~1
  alert["you clicked the second one"]
 else
  alert["you clicked something else"]
 end
end
(2 edits)

Currently Dialogizer doesn't support keyboard navigation because "blocking" scripts don't have any way to poll the state of the keyboard. I've been hesitant to expose such an interface in the past because- implemented or used carelessly- it could easily result in decks that are unusable on less-common keyboard layouts or on devices with no physical keyboard, like a phone or tablet. This functionality can also pose problems for parity between web-decker and native-decker, since browsers reserve many keys and key combinations for themselves which webapps cannot intercept.

I have been considering the addition of some kind of low-common-denominator interface that provides the equivalent of, say, an NES gamepad (when a gamepad or joystick is available) that is mirrored by various keyboard keys (when a keyboard is available) and exposed as a "live-updating" interface like Pointer. If something like this was available, I would certainly extend Dialogizer to consider the "action button"(s) as an alternative way of advancing text prompts. Having a "Gamepad" interface would also make it easier to write action-oriented video games with Decker, which seems to be a relatively common feature request.

edit: also, for the record, browser gamepad APIs require access to a "secure context" (in short, a document delivered via https); this makes it essentially impossible for web-decker to support physical gamepads, as well as many other recent web apis with similar restrictions. It's an infuriating design constraint.

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.