Happy almost-Birthday Decker!
ahmwma
Creator of
Recent community posts
It's so useful to see how to use a grid for this stuff! I've known it should be possible but I never had a project that required it so I've never gone through the steps of it myself. And now there's a helpful guide!
Thank you so much for writing this up! Seeing more of the ways that people work in Decker is always a gift to the community.
(Just one of your many gifts to us, Missooni!)
Somehow, since this is the spiritual sequel to last year's Decker VN jam... my group of art friends from last year (who made the silly Picnic Investigation story) wanted to get together and make something silly again. There's nine of us this time, incredibly, so the first half of the event has been a lot of back-and-forth talking and drawing.
Also, on a personal level I'm trying to work more with the "fancy puppets" aspect of the puppeteer module, since that's something that's felt kind of out of reach in the past, and setting up more backstage tools to make it easier for all of the team members to write and edit scenes regardless of familiarity with Decker.
I.... have faith in us that we will achieve a playable game by the end, whether or not it's what we currently have planned.
(Also I'm sorry for not getting a change to explore the suggested themes this time... I'm very much looking forward to having time to play everyone's games.)
Good call on making testing easier!
There's ways to hide cards from people by changing the navigate[] event that lets you move through the deck with the arrow keys.
Like, for example, disabling navigate[] in Interact Mode so people can only move between different cards by using buttons that you create. Or making it so it only loops between a specific set of cards, or otherwise skips over something you want to keep hidden.
But, on the other hand, if you only need to hide this one widget, then it might be simpler to just hide it on a card you're already using.
You'll need to change the card name in the "full address" in the script we talked about earlier to refer to the other card you've moved it to.
But once you're done testing you can just set the slider to "none" visibility and it'll be completely hidden from your users while still working for your scripts.
To hide something: Select the widget, and use the menu: Widgets > Show None
(And if you'd prefer to change the navigate[] event instead, let me know and I'll write up a way to do that)
Yeah, you can use sliders like that! This looks really long, but I'm just explaining a lot of little things.
1: Sliders
Sliders hold numbers (their .value) within a specific range, and only allow specific steps between numbers. So a slider with a range of 1 to 5, that allows steps of 1 can only have 1, 2, 3, 4, 5 in them. It won't allow things like 3.14159 or 6. (The range and steps can be edited in the properties dialog for that slider)
This makes them really reliable if you know exactly what kinds of number you want to be able to store, and it sounds like a good option for what you're doing.
In scripts, you can generally decrease a slider's value (the stored number) by 1 like this:
myslider.value: myslider.value-1
You could read that as "myslider's value becomes myslider's current value minus 1"
Or you could just assign a specific number to it like this:
myslider.value:3
"myslider's value becomes 3"
2: Referring to Widgets somewhere else
If the slider lives on one specific card, but we need to be able to change the value from anywhere else in the deck, then we'll need to refer to it by a sort of "full address" to make sure the scripts can find it.
If it was on a card called "home" then you could refer to it like this from anywhere else in the project:
home.widgets.myslider
But it can also get a little unwieldy in scripts, so if you're referring to stuff on other cards a lot you can define a shortcut to it.
home.widgets.myslider.value: home.widgets.myslider.value-1
# That's pretty long! But if we define a shortcut like this:
counter: home.widgets.myslider
counter.value: counter.value-1
# we can use "counter" as a short way to refer to the longer address.
3. Updating your Fields based on the Slider's value (in a general sense)
So the displayed "4 Approvals Remaining" text is in a field, and we gotta change that. I'm assuming that there's something non-linear about your game that makes it so just having lower numbers on later cards doesn't work, and you need it to be able to update live.
If the field only contains the number, it's very simple:
myfield.text: myslider.value
Literally, "myfield's text becomes myslider's value". If the slider's value is 3, the field's text will be changed to say 3.
On the other hand, if your field has the full phrase in it and you just need to update the number part you could write it like this:
myfield.text: "There are now ", myslider.value, " approvals remaining."
Make note of the quotes around the non-code text ("strings") and the commas between the different parts you want to connect together.
(There are some other peculiarities that I'll leave aside for now, like setting things up for specific fonts. Let me know if you need help with something like that later.)
4. Updating Matching widgets on Different cards
By "matching widgets" I mean they're all the same type, have the same name on different cards and are doing the same job.
The things that you want to match each other through the whole project, like your approval countdown.
deck.cards..widgets.myfield.text: "There are now ", myslider.value, " approvals remaining."
This looks for every widget called "myfield" in the entire project and attempts to give them all the same text.
So the names really do have to match! And you don't want anything else that has the same name by mistake.
Putting this together:
So, for this example I'm assuming that:
1. Somewhere in your deck there is a slider holding a number.
2. That there are fields with the same name on every card that need to display the changed text.
3. Your user will click a button
4. The button's script needs to change the number stored in the slider and then update the fields throughout the project.
So, after all this explanation.... here's one way of writing it:
on click do counter: home.widgets.myslider counter.value: counter.value-1 deck.cards..widgets.myfield.text: "There are now ", counter.value, " approvals remaining." end
This defines the shortcut to where the slider actually lives,
then subtracts 1 from the slider's number,
then updates the fields throughout the project to match the new number.
You can put this script in one button, test that it works (please change the widget names in the script to match your actual widgets!) and then copy it around to all the places it needs to be.
Or, optionally, you can create a custom event that all your buttons can use, so you only need to update one script in one place if you want to make changes later.
Bonus Round: Custom Events/Functions
Because this needs to be visible from every card we need to go up "higher" to the deck-level script.
You can get there in the menu by going to File > Properties and clicking "Script...", or if you're in another script editor screen already you can use File > Go to Deck. Things written in the Deck-level script are visible and usable by anything in your project.
So let's define a new thingy up there, called approval.
on approval do counter: home.widgets.myslider counter.value: counter.value-1 deck.cards..widgets.myfield.text: "There are now ", counter.value, " approvals remaining." end
It looks just almost exactly like the last code block, right?
Except it's now what happens "on approval do".... a thing we just made up, that we can now use.
So what do we put in the buttons?
on click do approval[] end
:)
Hopefully this helped a little, and if there's anything confusing or if I explained it badly (always possible) please let me know and I'll swing by and try to clarify later.
Because Decker project can only have one color palette at a time you'll have to use a script to change the palette for a specific cards.
Here's one example in the All About Color deck, here, on the page about Palette Transitions.
Or you could also check out the palettefade module for some additional tools to change colors in cool ways.
But while you're making your project you probably need an easy way to switch between your palettes whenever you want to.
Personally, I tend to use these two widgets for my projects: a button and a grid (styled as a listbox). The button will save your current palette as an option in the list, so you can just click it to switch between them anytime.

%%WGT0{"w":[{"name":"pals16","type":"grid","size":[155,103],"pos":[316,30],"locked":1,"script":"on click row do\n p:\"|\"split me.rowvalue.palette\n if 16~count p\n background.text:p[0]\n foreground.text:p[1]\n red .text:p[2]\n pink .text:p[3]\n orange .text:p[4]\n yellow .text:p[5]\n green .text:p[6]\n darkgreen .text:p[7]\n blue .text:p[8]\n cyan .text:p[9]\n purple .text:p[10]\n lightbrown.text:p[11]\n darkbrown .text:p[12]\n lightgray .text:p[13]\n mediumgray.text:p[14]\n darkgray .text:p[15]\n f:\"%06H\"\n patterns[32]:f parse p[0]\n patterns[47]:f parse p[1]\n patterns[33]:f parse p[2]\n patterns[34]:f parse p[3]\n patterns[35]:f parse p[4]\n patterns[36]:f parse p[5]\n patterns[37]:f parse p[6]\n patterns[38]:f parse p[7]\n patterns[39]:f parse p[8]\n patterns[40]:f parse p[9]\n patterns[41]:f parse p[10]\n patterns[42]:f parse p[11]\n patterns[43]:f parse p[12]\n patterns[44]:f parse p[13]\n patterns[45]:f parse p[14]\n patterns[46]:f parse p[15]\n \n end\nend","headers":1,"lines":0,"widths":[128,82],"format":"ss","value":{"area":["outside","inside"],"palette":["F8F8F0|3F3A3F|807173|C6AFA5|DAC3BC|F0DFBC|23493C|337650|82B26E|715A49|5D463C|FFBB66|D7A371|94C6FF|82ADE6|5566A8","E4E9F3|1A1C2C|5D4650|82695D|444667|947B76|2B499E|38B764|305553|263A6E|3B5DC9|41A6F6|73D0F7|2D3C73|1E2637|1E2128"]},"row":0},{"name":"button5","type":"button","size":[52,34],"pos":[228,67],"script":"on click do\n colors:(\"%06H\" format deck.patterns[32]),\n (\"%06H\" format deck.patterns[47]),\n (each i in range 14 (\"%06H\" format deck.patterns[33+i]) end)\n newpal:\"|\" fuse colors\n newpalname:alert[\"Please name your palette\" \"string\" \"New Palette\"]\n pals16.value:insert area palette with newpalname newpal into pals16.value\nend","text":"Save\nPalette"}],"d":{}}I hope this helps a little! Feel free to start a new thread if you have more questions about how to do specific things with multiple palettes, I think it's something that a bunch of people are interested in.
Hi! I'm just here to ask some questions about the crashing, specifically.
What version of Decker are you using currently? (For example, 1.70 is the most recent version)
What operating system are you using? (Windows, Mac, Linux, etc)
When you say crash, do you mean the entire program closes? Or just the prototype editor? Does the program freeze? Or something else?
Is there any more information you could give me about what is happening right before the crash happens?
I'm not sure if this answers your question, and I'm not sure if this is the best way, but this is usually what I do:
I have a specific folder where my Decker files live, and there's a "Decker" folder inside it, which is where the program and the official example decks are.
It's something kinda like this:
C:\MyDeckerFolder\Decker\
When itch.io lets me know there's a new version (on the feed or by email) I download it and copy the "Decker" folder from inside the zip file and then paste it into "MyDeckerFolder" so it overwrites the existing "Decker" folder with the new version.
This way my shortcut to open the program still points to the same location.
But on the other hand if you've been making your own things with the example decks recently (Like creating fonts with the font editing deck) then you'll want to copy those specific decks to another place first, so they don't get overwritten.
Sorry for the delay, I realized that my existing fancy puppets have a friend's art in them so I made a new one with Phinxel.
Contraption code block is at the bottom of the post.
+ I'll try to explain here about what it's doing, and the different ways it's set up to work for each part.

There's a series of canvases in the visible area and some fields for data storage.
The Fields:
emote = holds the name of the current emote (used by puppeteer)
f, h = holds all of the images of a specific part (face, hat) and a label for that part on the line above or below the image.
In this example I put the string of text on the line below the image, but I'm pretty sure it works the other way too... (Just be consistent about it.)
The Canvases, and what they need to do:
body = Always visible, never changes.
face = Always visible, image can change.
hat = Only visible if the emote calls for it, image can change.
up, down = Multiple canvases for one feature (the tail) and the images do not change but only one of these canvases should be visible at a time.
And now let's look at the code. We got some basics:
on set_emote x do emote.text:x end on get_animate do view end
And then we get into the emotes:
on view do
faceData:insert
emote faceImage tail hat
with
"smile" "smile" up nil
"smileparty" "smile" up "party"
"smilecap" "smile" up "cap"
"delight" "delight" up nil
"delightparty" "delight" up "party"
"delightcap" "delight" up "cap"
"sad" "sad" down nil
"sadparty" "sad" down "party"
"sadcap" "sad" down "cap"
end
The instructions for which combination of canvases + images apply to which named emote all live in a big table.
Quotes around a word mean we're referring to a string of text in one of the fields. When there's no quotes it's either the name of a widget (the canvases: up, down) or specifically nothing (nil).
These next lines let us refer to the images in the hat/face storage fields by their labels (the strings of text) on the neighboring lines:
faceImages:("" drop "\n" split f.text) dict f.images
hats:("" drop "\n" split h.text) dict h.images
And this sets up 'e' to only look at the information in the row for the current named emote:
e:(faceData.emote dict rows faceData)[emote.text]
The face is simple, because there's only one canvas and it's always visible:
face.paste[faceImages[e.faceImage]]
For the hat we also need to check to see if there's supposed to be a hat in the current emote (whether there's a label in the hat column or just nil). So we toggle the visibility based on the contents of the hat column, then paste the relevant image.
hat.toggle["transparent" e.hat] hat.paste[hats[e.hat]]
And finally the tail. For this one we hide the tails in the column and then only show the one that's named for the current emote:
(distinct faceData.tail)..show:"none" e.tail.show:"transparent" end # ending the "on view do" from earlier
Hopefully one of these examples of ways to handle a modular part in a fancy puppet is helpful to you, or to some future forum reader.
(And I'm honored that Phinxel's book has been useful to you! I'm not bothered at all by the association. Keep making your art, Goons.)
%%WGT0{"w":[{"name":"fancyphinxel1","type":"contraption","size":[81,109],"pos":[235,103],"def":"fancyphinxel","widgets":{"down":{},"up":{},"body":{},"face":{},"f":{"value":{"text":["smile\n","i","\ndelight\n","i","\nsad\n","i","\n"],"font":["","","","","","",""],"arg":["","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amuFfntef3Z4ew2QkVGLio2Jj5GZjpVpf2WZmoCIAKANlk+YpYKcQ6WQgpdErqZql3eVs7CxraCxWoaKuY5sg5K2Wr+yrqeoVsnJSarMo7vATaG6xKx1XYzV3GPe2tvgzKvk5cCDm+vpyOKi1G9aIO+U8s5HIPv70ITtnYzwG1gPnb10qAjyCxAE","","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amuFfntef3Z4ilaPgJGObIMADQ2Ek2N/Z5ifeptJoIKJe5+kgqJCqJilhqetmX6md5Gys5KVd7izU1qwo7ivwUO9T1rJscPEyMbMwMh5va7NZczStbzUzcWsrbu208eOkdeppulE2OGafFhr4N3lq8po0U7J+fiQypbh/7JxSQbC3j5GdaSBWLiQX6iEkhhKLFhPH0RTExkGCAI=","","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amt9DQ2GXVpeg22JiYKEVmWIkXiFjF+FkpmDS5h2fqB8mkKRnpmke5ulRKmKk6xvWq2hqqu0XLanlXm+hlB/bpaveGy3Y4fHdb+gwc6CysLU0pROjdeOvrbb097Z2t+2IL3c0aZGIOzs44TNtQHt9ObW1enq9e0BQQ==",""]}},"h":{},"hat":{},"emote":{}}}],"d":{"fancyphinxel":{"name":"fancyphinxel","size":[81,109],"margin":[0,0,0,0],"description":"A little demo of a modular fancy puppet.","script":"on set_emote x do emote.text:x end\n\non get_animate do view end\n\non view do\n faceData:insert\n emote faceImage tail hat \n with\n \"smile\" \"smile\" up nil\n \"smileparty\" \"smile\" up \"party\"\n \"smilecap\" \"smile\" up \"cap\"\n \"delight\" \"delight\" up nil\n \"delightparty\" \"delight\" up \"party\"\n \"delightcap\" \"delight\" up \"cap\"\n \"sad\" \"sad\" down nil\n \"sadparty\" \"sad\" down \"party\"\n \"sadcap\" \"sad\" down \"cap\"\nend\n\n faceImages:(\"\" drop \"\\n\" split f.text) dict f.images\n hats:(\"\" drop \"\\n\" split h.text) dict h.images\n \n e:(faceData.emote dict rows faceData)[emote.text]\n\n face.paste[faceImages[e.faceImage]]\n hat.toggle[\"transparent\" e.hat]\n hat.paste[hats[e.hat]]\n (distinct faceData.tail)..show:\"none\"\n e.tail.show:\"transparent\"\nend","widgets":{"down":{"type":"canvas","size":[19,24],"pos":[47,84],"locked":1,"show":"none","border":0,"image":"%%IMG3ABMAGAZAQABALBqPxIASySwqh83mM8p8TqlOqxar3VK7VqzRK04uy1koWnhGX5Fvszodlh/By3oaxAeBx0p9fWRygnxxe4KIgAGKbXBPh49VXWWERUE=","scale":1},"up":{"type":"canvas","size":[27,21],"pos":[51,71],"locked":1,"show":"transparent","border":0,"image":"%%IMG3ABsAFQZAgHBILBIDSKRxuUw6A8wo4AlySo1OkLaavA6zW23XO0WGt2Nv8ixWktfs9BWMdpPLa+v9+7TvhX1/R36CfFCFg4eIeIR7gVGKhk9qfY+UlY1zk4hB","scale":1},"body":{"type":"canvas","size":[59,83],"pos":[2,24],"locked":1,"show":"transparent","border":0,"image":"%%IMG3ADsAUwZAgHBIDBiPAaJyyWw6nwCkNAmtWqHT6XXLFR5B4DDo2C0/v2Ix2VxcW9HpsJudfRvj6Xk3a8Te8Wp9UXx6gwGBgk1wgGCEhEOLcolMkXGOl0iAhZB/mlKMkp+eVJSdmqCjppaTbYeor56om4ausLa3s7S3u6+5tLW8wY2sS1PCwr6/wMe2yZnMwc6q0M3ESpXUvb7Y2bLWnMvdsEhO3OKg5KXh59qk19Ps7cXw8d6s5vWMevj5ePv0/UbNWxfQHzF+BUMNTCjvHUGGw9y1gmjwmyGKiCQOfJgvmZdnBaVcMdbxURWS4ozVOSkqG8iIHn+d45auHEqX9LY94xgtUs2FIXfmQtjNpDqMY0QqInps3U+HPKFhe/rR6cychXwC7Pnwqdaou8zNqcS0Gkc3ZLdyRZcoLdiwANFOK2tWX9u5atcKBJcnLy+a977cTKlKaass9eBoUTe4qNFyfBMv5kIXl2HKfrmyqVpSI+a3mulkDm2msmXPI0f/tcgS9GrUdlzDhd26X8ylsk9vviiZdu14t23ybLyKdequiNn6Pm43efHlx51Omho8drh/pqjuVjb5cPft3i9D1Q4eHHnz0JUEAQ==","scale":1},"face":{"type":"canvas","size":[57,28],"pos":[2,39],"locked":1,"show":"transparent","border":0,"image":"%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amuFfntef3Z4ilaPgJGObIMADQ2Ek2N/Z5ifeptJoIKJe5+kgqJCqJilhqetmX6md5Gys5KVd7izU1qwo7ivwUO9T1rJscPEyMbMwMh5va7NZczStbzUzcWsrbu208eOkdeppulE2OGafFhr4N3lq8po0U7J+fiQypbh/7JxSQbC3j5GdaSBWLiQX6iEkhhKLFhPH0RTExkGCAI=","scale":1},"f":{"type":"field","size":[83,121],"pos":[111,-22],"scrollbar":1,"value":{"text":["","i","\nsmile\n","i","\ndelight\n","i","\nsad"],"font":["","","","","","",""],"arg":["","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amuFfntef3Z4ew2QkVGLio2Jj5GZjpVpf2WZmoCIAKANlk+YpYKcQ6WQgpdErqZql3eVs7CxraCxWoaKuY5sg5K2Wr+yrqeoVsnJSarMo7vATaG6xKx1XYzV3GPe2tvgzKvk5cCDm+vpyOKi1G9aIO+U8s5HIPv70ITtnYzwG1gPnb10qAjyCxAE","","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amuFfntef3Z4ilaPgJGObIMADQ2Ek2N/Z5ifeptJoIKJe5+kgqJCqJilhqetmX6md5Gys5KVd7izU1qwo7ivwUO9T1rJscPEyMbMwMh5va7NZczStbzUzcWsrbu208eOkdeppulE2OGafFhr4N3lq8po0U7J+fiQypbh/7JxSQbC3j5GdaSBWLiQX6iEkhhKLFhPH0RTExkGCAI=","","%%IMG3ADkAHAZAgHAICBiNxKRyyWwWkczj0UmtJqdRacDKpWKX0q64+VWWx2jhmbhOi8NgKHnrztLNcvu92h7Cr3lxgXN9amt9DQ2GXVpeg22JiYKEVmWIkXiFjF+FkpmDS5h2fqB8mkKRnpmke5ulRKmKk6xvWq2hqqu0XLanlXm+hlB/bpaveGy3Y4fHdb+gwc6CysLU0pROjdeOvrbb097Z2t+2IL3c0aZGIOzs44TNtQHt9ObW1enq9e0BQQ==",""]}},"h":{"type":"field","size":[74,112],"pos":[203,-22],"scrollbar":1,"value":{"text":["","i","\nparty\n","i","\ncap"],"font":["","","","",""],"arg":["","%%IMG3ACEAKgQQyEmrvTjrzbv/VQB+QTli4lSmJ7WqZuu+KytL9XuHeb4DPd8tqJP1frBAo7hbNZaxZun5ZLacVOsIm42euFSoDQTujj1l83caDms3abeXE5efM/X2+5K3d/p+dGxthGKCSlV1e0lEOWp4jZGGKEEzkRqLljVIPHOcQJ+hoqOkpaalEQ==","","%%IMG3ACEAKgZAgHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7XqHgbB4bByTkwGQeg0yh9nsMNKtpqfh7YBTDnD79VJiXmZaf4BVhnxTZm11goFvcY6Ke5GSk1BieI2YT5qbd3mHS5+ga49MpXihqEqqq6eUaJamsaNFdpNvoaJzY5u2cbJgr6C8rcQBEcvAwrZHYcvSosB3Z7jR0nRwbr7K2tu2w8nfj6qEruh9iZW3hlndTEE=",""]}},"hat":{"type":"canvas","size":[33,42],"pos":[13,2],"locked":1,"show":"transparent","border":0,"image":"%%IMG3ACEAKgQQyEmrvTjrzbv/VQB+QTli4lSmJ7WqZuu+KytL9XuHeb4DPd8tqJP1frBAo7hbNZaxZun5ZLacVOsIm42euFSoDQTujj1l83caDms3abeXE5efM/X2+5K3d/p+dGxthGKCSlV1e0lEOWp4jZGGKEEzkRqLljVIPHOcQJ+hoqOkpaalEQ==","scale":1},"emote":{"type":"field","size":[66,19],"pos":[21,-34],"value":"delightparty"}}}}}Hey goons.
I've messed with fancy puppets a couple times, though I haven't published anything using them. Would you like me to throw together an example and an explanation so you have another thing to look at?
I've been storing all my modular pieces inside the contraption itself (images stored in internal fields and copied over to internal canvases when needed) and using them to build various expressions for puppeteer.
It's a delight to help a project come together, don't worry. :)
This may be an incomplete answer but the first thing that stood out to me is that you need to give the "full address" of a widget, when you're referring to it in the deck-level script. Like this:
aspecificcardname.widgets.coins.value
Though you can also make it easier to write by defining a shortcut for that full path in the deck-level script and then using that shortcut:
coins:aspecificcardname.widgets.coins coins.value:coins.value+1
(You may have set something like this up already, but if not then this is the first thing I'd try)
Ah, this is interesting. I think you've found a bug! Contraptions should not be able to be pasted inside of other contraptions. It seems like it's possible, in the current version.... but if you save and reload the deck the nested contraptions will be gone.
I think it's still pretty easy to do what you wanted to do, though.
The eye contraption is relatively simple so it's very possible to incorporate it into another contraption. Its just a matter of putting the relevant widgets and code directly into your other new contraption, without the "Eye Contraption" container.
For the sake of making an example, I built off the code snippet and ideas from Internet Janitor in the post above created new hidden button widgets inside of the contraption to define bounding boxes for the movement of each of the eyes.
So here's a new contraption that contains two moving eyes and an image in a canvas:
%%WGT0{"w":[{"name":"eyezample","type":"contraption","size":[116,142],"pos":[38,100],"def":"eye-example","widgets":{"canvas1":{},"pupil1":{"image":"%%IMG0AAUABfDw8PAA"},"img":{"value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2AAQABAEQ"]}},"b1":{},"pupil2":{"image":"%%IMG0AAUABfDw8PAA"},"b2":{}}}],"d":{"eye-example":{"name":"eye-example","size":[116,142],"resizable":1,"margin":[0,0,0,0],"description":"A quick silly example for the decker forums. ","script":"on get_pupil do\n img.value\nend\n\non set_pupil x do\n img.value:x\n view[]\nend\n\non track_cursor img pupil box do\n pupil.size:img.size\n pupil.clear[]\n pupil.paste[img]\n c:box.size/2\n p:pupil.size/2\n d:pointer.pos-box.offset+c\n m:(c-p)&mag d\n pupil.pos:box.pos+(c-p)+m*unit heading d\nend\n\non view do\n i:first img.images\n track_cursor[i pupil1 b1]\n track_cursor[i pupil2 b2]\nend","attributes":{"name":["pupil"],"label":["Pupil\nImage"],"type":["rich"]},"widgets":{"canvas1":{"type":"canvas","size":[116,142],"pos":[0,0],"locked":1,"border":0,"image":"%%IMG3AHQAjgZAgHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wa2tOr9fjeIB9z6fn1X2BgoN3f04tRHuHhIyIhkl3dlWNgo9FlYl+XXN6ipaBRp6TnJ2kpZp5oJmSR6itg0N8qZysQpKutrWxlJhvfoW5kabBun2nxLBuxqemqIqaspmrjrusolzDv6rVzNTbU7zDWIWu19W63dRU1oRavcGQ6Ofu7OrHcqTyTbhl76X46oBZQvLt36iA/AaGkmUOCi6BCuO1AycuYUSCxiwuKafx4sJ6FL119PiqmbhFjvSRlBgtijaIK5X4Q9kwJsaCT4TZs1lyJpNbJ9c0+llp5CWdLn3SnHgTlNFp8HIylTJUJi2GQVlGlYoTjTOQDq/u5LlqWsqsVmGSLTvPYFi1a7nNe/oRbVxPL0PavftMZdq9cZHV3Oj30+CjhZvSbcOL8OKoj5WOC0d0MTTA5yTrfdhS8diSW/8yovf5pmOjX+3WqgjXFsq3ogGnFr0PLWrMbCW6NR0acemPuh3DFr4bcW7gXIOn5fp7q0WOnx8XP+6ZUm2R0bNljawac7jmxxNCb1q5o+Wqr+UaV1+38XLG2seOZ4leudD4oNn37GofEH7k+okh3Sb/rRdgGANiUyB1Ca6DGxjzGdibgA9+ESGDFQKkzIIBNkhVhgQiJB8/Hg7HxoVQUQchiAqKSN9Z4GVR4mQuvthaixvWaOOMh/jCYXA8ejRbSIEld8+HLNr0UoxaFXkajzc6eVhSTK4VJZVO9kdalvmF6M4fQXZ50CxVIqnhPmOEyduYlVFYJpFsvodgkm+9qWKKc9qpV5y+pUknknoeeaAXaqYHKHaBYplGg5ptUaiJh/4J6aIePirniVfCYelmiZKx6UGSrthpnqE6+ilpp15Xqp9TWpgYmJ2JmqlhrXK6qo+x2jqqId/1yiVwvdb3a3HBknMrrLuKOeymqcKXLIDDTifjsWSKGu2EW177abP3PQvtr9vmaiW1ZgX7CLfFvmrGrKaai6u3pL6rJITlwRvYc+pe22Rm5BYJVK3gEuTbMvquyV6j4KYLMJcKE1wwv5Sl8zDEwgr2cMRwiTsuLOJ1t/BAQ3Xscb5CRiNyQO2xq9AyJx/I30osB7Xadg4rKcrJiO5obz83yzzzyCrHcY3Ibt1Y80XykJiPSXsdvbLS9kR4pdO8Eo1hzk8GTanPUZ902VMv42qwtF/jFna32XWdtsSqau1FEA=="},"pupil1":{"type":"canvas","size":[5,5],"pos":[29,48],"locked":1,"animated":1,"show":"transparent","border":0,"image":"%%IMG0AAUABXD4+Phw","scale":1},"img":{"type":"field","size":[27,28],"pos":[-73,29],"locked":1,"show":"none","border":0,"value":{"text":["","i"],"font":["",""],"arg":["","%%IMG0AAUABXD4+Phw"]}},"b1":{"type":"button","size":[20,9],"pos":[26,48],"show":"none"},"pupil2":{"type":"canvas","size":[5,5],"pos":[72,46],"locked":1,"animated":1,"show":"transparent","border":0,"image":"%%IMG0AAUABXD4+Phw","scale":1},"b2":{"type":"button","size":[20,9],"pos":[71,46],"show":"none"}}}}}

The image in my canvas is wrong, obviously. And it doesn't do anything else you may have intended your contraption to do.... But, well. I hope this helps clarify why things were acting weird for you, and also maybe it gives you a way to move forward with your ideas.
Please let me know if you have questions about it, or if you need more help getting it to do what you want it to do!
(The pupil areas can be changed inside the contraption prototype by moving/resizing the widgets named b1 and b2. )
Some supplementary information about how to make this kind of "embodied data" act a little more like global variables:
You can set up a storage card for all of your data-holding widgets. One that (probably) your player will never navigate to (there are ways to make it harder to reach, as well, if your users are meant to navigate with arrow keys). It could have checkboxes for true/false, fields for text, grids for spreadsheet type data, etc.
And then you can set a sort of shortcut variable in the Deck level script (Accessible with with File > Properties > [Script...] Or if you're already in a script editor you can use File > Go to Deck to move to the other script directly.) which is a quicker path to the widgets on that specific card.
Here I'm setting a shortcut to the widgets on a hypothetical card called myvars (using Millie's example):
vars:myvars.widgets
And then be able to use that shortcut variable to streamline your other scripts when you're referring to your other stored data, like:
vars.field1.value
instead of having to write the longer address every time you want to refer to your stored information:
myvars.widgets.field1.value
It's often a little easier this way.
Or you can use this kind of shortcut to go directly to a single widget, if it's one you're updating and referencing often:
thatfield: myvars.widgets.field1
I hope this helps.
I think you mean the decorative backpack image next to the word Inventory?
The code InternetJanitor provided will only move draggable canvases to the new card, so it won't do anything to non-draggable decorative canvases. You may need to copy your backpack canvas to the other cards manually, and this script would only handle the items (draggable canvases) that are overlapping the inventory box.
Honestly, my idea is very similar to InternetJanitor's: using a custom function (by writing your own "on thing do" event handler and and then being able to use thing[] in your scripts to make it happen when you want it to) to make things match on different cards when you move between them. IJ's idea is simpler in some ways because it checks what's currently overlapping the area and makes sure those exact things are present when you go to the next card.
There are a few things that could be confusing about either method, but I'm happy to help answer questions either way!
It's not quite what you're asking for, but the right sidebar has gained some additional usefulness lately!
As of the most recent versions (1.69 and 1.70), the right side toolbar can be used to quickly change the color of widgets (the .pattern attribute) in widget mode and the color of a highlighted span of text in rich text fields while editing them Interact mode.
Hi again! Sorry for the late response, it's been a busy week.
This is just a short reply for now, which may not fit everything you're doing.
I think the group idea you're thinking about is a Contraption.
Basically it's a custom widget made of the other basic types to become something new. Contraptions are things that are meant to be reusable, whether it's something that many kinds of projects like the Calendar example you mentioned or something that can be used many times within one. Like using a contraption to keep track of what items you have. :) So you had the right idea.
Internet-Janitor has just recently posted a contraption version of an Inventory Bar that does some of the things you might want it to do, but not everything.
For example, you can place an InventoryBar on every card and they'll all update each other. And it tracks whether or not something is stored in the inventory so you can write If statements that check it in your code.
But it doesn't use draggable canvases like your project is using doing now, so it would change your gameplay a little bit if you used it as it is.
Is this the kind of thing you were thinking about? (Even if it's not exactly this one.)
On the other hand, there are definitely ways to do this without contraptions. Like having a script that syncs up the locations of your canvases on different cards when you tell it to. But I'd have to come back to write an example.
Hello! And yeah absolutely!
For starters: referring to widgets by a shorter nickname. I think you were on the right track already, but I'll explain it in more detail anyway.
One option is to create a variable which is just the full path to a widget somewhere else in the deck:
doorknock: DormitoryHall1.widgets.DoorDepr
And that should make it possible to use "doorknock" as a shortcut for the longer name.
Or a project that has many info tracking widgets on a secret storage card could just simplify part of the path:
status:secretstoragecard.widgets
And then be able to use that in your scripts like this:
if status.pickedflower.value
And while you can define these shortcuts in the specific scripts where you're using them... you could also put them in the Deck-level script if you use them a lot. Just like Widgets and Cards can have scripts, you can also define events and variables at the Deck level.
Things put in the Deck script are always visible to every widget in the project (unless overridden more locally, but that's a different subject).
You can access this script with File > Properties > [Script...]
Or if you're already in a script editor for something else you can use File > Go to Deck to move there directly.
And this kind of nickname variable doesn't need to be in an event handler or anything, just define the variable and you're done.
Okay, now on to your real questions....
Just like a checkbox can hold 1 and 0 as true/false... other widgets are good at holding other kinds of information.
In this case I recommend a slider widget, specifically. They're very good at storing numbers within a specific range, and you can choose the minimum and maximum of that range, and how big of a step is allowed between each point on the slider.
They're my go-to widget when I'm keeping count of something!
I'd recommend setting the style of your slider to "Compact" in the properties dialog to make the number easier to read while you're testing, and then you can also set the min and max of the range of numbers you want to use.
A nice thing about using a slider for this job is that your existing script doesn't need to change much.
The current number stored in a slider is also called .value in scripts.
And you can adjust it by doing things you already understand how to do:
yourslider.value: yourslider.value +1
If you need to reset it back to the beginning you can just assign it the number you want to start at. Assuming that's zero at the beginning of the game, just do this:
yourslider.value:0
(And, as always, make the names match your actual project)
For the other part... It's true that we can't use combined operators but you can usually get the effect you need by writing things a little differently. I'm not completely sure all the ways you were thinking about using >= so this may not fully answer your question...
But for this script example... I think things could be simplified a little with the power of "else".
(I'm not copying your full script here so I'll just leave placeholders for the different scene possibilities, okay?)
if doorknock.value =1 # "Erm, Hello?" else # "...." end
Basically else is "If the condition wasn't true... do this other thing instead."
Or you could add more possible outcomes with elseif.
The first true thing in the series of possibilities will be the one that happens, so if two things could technically be true at the same time, make sure to put the higher priority one earlier in the list:
if doorknock.value =1 # "Erm, Hello?" elseif doorknock.value =20 # "Please stop knocking!!" elseif doorknock.value > 15 # "...!! >:O" else # "...." end
20 is more than 15, so the event for ">15" could have happened at value=20..... but =20 was earlier in the order of possibilities, so only that one will happen. It's kind of a silly example, but I hope it makes sense.
And while I'm thinking about it, you can also move your dd.open[] and dd.close[] lines to be before and after your branching dialog possibilities if you want. Like this:
on click do dd.open[deck] if doorknock.value =1 dd.say["Erm, Hello?"] else dd.say["...."] end dd.close[] end
I've got to stop here for now but I'm happy to come back and clarify if I wrote things in a confusing way, or if I didn't answer your real question!
Hooray! I'm so glad!
Things are sometimes a little quieter here in the forum during August because one of the official decker jams just happened in July. But there's always enough people stopping by to make sure that questions get answered.
Feel free to reply here or leave a new comment on the thread, I'll keep an eye out for you. :)
I was rushing a bit at the end of my post so let's see....
You figured it out (correctly) that I meant "thebutton" as the name of the button. I didn't know what yours was called, but I also didn't make it clear that I was making up a name for it. Sorry about that! But something is still not right, huh...
Is it possible there's an extra space at the beginning or end of the widget name? I do that all the time... and it will technically affect what the name of the button.
If the button's name is just "GrabFlowerButton", then that's how you should be able to write it in the code.
GrabFlowerButton.locked:1
Also! Since you mentioned things that only work inside a widget's script, I'll also mention this in case it's useful:
When you're writing a script for a widget you can always refer to that specific widget as "me"
So for the script inside the button you can write it like this:
me.locked:1
Though you'll still have to use its name whenever you're referring to it in scripts that live anywhere else in your project.
I hope one of these things can help! I'll check back again later. 🫡
Hello! This looks like a charming game already.
When you want to create a variable in decker to be referred back to later you need to store them in a widget. A temporary variable can be created within a specific script while it's running, but these temporary variables aren't stored anywhere and can't easily be edited unless you put them inside a widget.
But it's pretty easy to do exactly that!
For things where you want to track true/false (has the flower been picked?) a checkbox-style button is really useful. If you created a checkbox called "pickedflower" and gave it the value 1 like this:
pickedflower.value:1
.value means different things for different widgets, but generally it refers to the kind of information stored in them. I'll keep it simple and say that in the case of buttons (including checkboxes) they can only store a boolean 0/1 (false/true) as their value.
To check this value in your if statement you can write it like this:
if pickedflower.value # the rest of your script here end
But... since the checkbox is a widget that will live on a specific card, you'll need to put it somewhere.
From what you explained about your game you want to set the variable while picking up the flower on one card... and then check the variable on another card later to give the flower to someone later, right?
So we also have to talk about how to refer to a widget that's on another card.
If the "pickedflower" checkbox lived on a card called "garden", you could refer to it this way on your scripts, when you need to refer to it's stored value from other cards:
garden.widgets.pickedflower.value
It's a little long to write it like this, but I think that's okay when you only have a couple things to check. If you have a lot of things to track, let me know and I'll come up with some suggestions for how to store them and refer to them more easily!
Also, you can set your checkbox to show:"none" and it'll be completely hidden to your player.
Or you can store all your game state-related widgets on a card that the player never sees (I recommended this if you have a lot of them!)
Optionally, if you don't want to store a variable this way at all... there's other ways to do something similar.
For example: you can check the current visibility of your flower canvas to see if it's been hidden or not:
if theflower.show="none" # something that happens only if the flower's canvas is hidden end
And you can also lock a button if you no longer want it to be clickable.
thebutton.locked:1
If you do this make sure to add unlocking it (thebutton.locked:0) to your reset button too.
I'm happy to explain more if it would be helpful. But I wanted to get an answer for you quickly so you could get started playing around with it.













