I am making a text based adventure game, for it to work I require a way to have an inventory. I want to be able to click a button on one card and have it update the grid on another card. How do I do that?
A multimedia sketchbook · By
That's a great question! There are several ways to tackle this, so let's go through it step by step.
To begin with, let's say we have a card with a normal button "chester" and a checkbox "frobnicate":
If we wanted clicking on chester to toggle frobnicate, we'd write a script for him like so:
on click do frobnicate.value:!frobnicate.value end
We're able to write code like this because, from the perspective of a script on a widget, all the widgets on the same card are available in Lil variables corresponding to their name; in this case "frobnicate".
The same is true of cards: every card in the deck is available in a variable corresponding to its name. If the card we're on is named "home", we could also write the above script like this:
on click do home.widgets.frobnicate.value:!home.widgets.frobnicate.value end
Or, to be a bit less repetitive,
on click do f:home.widgets.frobnicate f.value:!f.value end
The above script will now work even if "chester" is on a different card from "frobnicate" entirely: what matters is that "frobnicate" is a widget on the card named "home". If we wanted to be really explicit, we could look up the card in "deck" (another "magic variable"), and then look up the widget in that card:
on click do f:deck.cards.home.widgets.frobnicate f.value:!f.value end
Side track on naming: All the names of cards and widgets we've seen so far are also valid variable names in Lil: all one word with no spaces, composed of letters, digits, and underscores, and they don't start with a digit. If names aren't valid Lil variable names, like if our card was named "My Fantastic Card", we would have to use bracket-indexing with a string into deck.cards any time we wanted to reference it:
on click do f:deck.cards["My Fantastic Card"].widgets.frobnicate f.value:!f.value end
Prefer Lil-compatible names for things you expect to interact with in scripts!
We now know how to reference widgets on a distant card, so we have the building blocks necessary for an inventory system. For a simple game that only has a handful of possible inventory items, it might be easiest to represent each possible item with an individual widget on an "inventory" card. Perhaps they're locked checkbox widgets, toggled with the .value attribute as in our previous examples, or perhaps they're canvas widgets with a default .show property of "none" that appear when you've picked up the item:
inventory.widgets.sworde.show:"solid" alert["ye have acquired ye sworde!"] ... if inventory.widgets.sworde.show="solid" alert["ye slaye yon wickede beaste withe ye sworde!"] alert["forsooth, ye sworde is all gross and sticky, so we shall discarde it."] inventory.widgets.sworde.show:"none" go["slayed beaste"] else alert["alas, without ye sworde, yon wickede beaste hast devoured thou!"] go["game over"] end
If you have a lot of items, using a grid might be desirable instead. Let's say the grid on our inventory card is named "items" and it initially contains an empty table with a single column, "name":
Now we'll need to manipulate the table inside our grid to pick up items or test for their presence. Our script above might now look something like:
i:inventory.widgets.items i.value:insert name:"sworde" into i.value alert["ye have acquired ye sworde!"] ... i:inventory.widgets.items if "sworde" in i.value.name alert["ye slaye yon wickede beaste withe ye sworde!"] alert["forsooth, ye sworde is all gross and sticky, so we shall discarde it."] i.value:select where !name="sworde" from i.value go["slayed beaste"] else alert["alas, without ye sworde, yon wickede beaste hast devoured thou!"] go["game over"] end
If we're manipulating an inventory frequently throughout our deck, it might be a good idea to factor some of this logic out into "Deck-level" functions. Functions you define in a Deck script can be called from any other script. While we're at it, we can define a function for resetting the game to its initial state. You can set Deck scripts via "File -> Properties... -> Script...":
on get_items do inventory.widgets.items end on add_item n do i:get_items[] i.value:insert name:n into i.value end on remove_item n do i:get_items[] i.value:select where !name=n from i.value end on has_item n do n in get_items[].value.name end on reset_game do i:get_items[] i.value:0 take i.value end
With these new utility functions we could rewrite our game scripts once again to be more concise and easier to understand:
add_item["sworde"] alert["ye have acquired ye sworde!"] ... if has_item["sworde"] alert["ye slaye yon wickede beaste withe ye sworde!"] alert["forsooth, ye sworde is all gross and sticky, so we shall discarde it."] remove_item["sworde"] go["slayed beaste"] else alert["alas, without ye sworde, yon wickede beaste hast devoured thou!"] go["game over"] end
And we can also test and manipulate the inventory using these functions in the Listener:
Does that point you in the right direction?