inkle / ink

inkle's open source scripting language for writing interactive narrative.
http://www.inklestudios.com/ink
MIT License
4.1k stars 490 forks source link

[Discussion] conditional choice text evaluation #75

Open crustforever opened 8 years ago

crustforever commented 8 years ago

So I've noticed that if you embed logic in the text of a conditional choice, that logic will be evaluated even if the conditional is false:

This means that the choice text is evaluated even though the conditional dictates the choice will not be available.

I am wondering if this behavior could be changed to postpone evaluation of the choice text until after the conditional (or perhaps there is a reason it can't or shouldnt be changed)?

I know that it doesn't make a whole lot of sense to put game state changes in the choice text, as they will happen whether that choice is picked or not, but here's an example from my game that shows a situation where it could be desirable because of external function bindings:

~ temp hasEnoughFood = Food() > 10

The key part here is the ShowFoodChoice external function. That is a function that replaces the 0th choice label that would be created in the GUI with a special choice label in the GUI that visually shows the player the amount of food being used (10 in this instance) and also prints the text "eat food".

This is not a perfect example, and I know that I could just put the ShowFoodChoice function in the conditional part and pass the boolean into it, but I'm just curious as to what you guys think of the evaluation of logic inside conditional choice text.

EDIT: it printed my *'s as bullet points

crustforever commented 8 years ago

Actually, even if this change was made, my whole plan here is kind of busted due to the fact that failed conditional choices are not preserved in the Story.currentChoices list. So the choice index that I used in the example above is no good.

The only thing I can think of to hack this behavior in is to have some string identifier in the beginning of the choice text that I look for in order to mutate my UI choice label at runtime... but that is pretty gross.

I suppose the real functionality I desire is for choice text to not be available until some function is called on the Choice object. But that would probably not be very user friendly.

Has anyone at Inkle ever wanted to do something like this?

crustforever commented 8 years ago

I am looking at Story.ProcessChoice and I can see that this change would not be trivial due to the evaluation stack...

Seems like inserting some string at the beginning of the choice text to alter runtime behavior is my best bet.

joethephish commented 8 years ago

So yeah, as you discovered, all of the components of a choice (condition, text, etc) are generated in advance. However, an alternative is to nest your choice within a conditional block:

{ yourCondition:
    * the choice
}

I'm not sure if that entirely solves your specific problem, but it won't run the choice content at all if the condition evaluates to false...

crustforever commented 8 years ago

Yes that works for my original case!

However, if I want to handle a choice in some special way unity-side at runtime (as I badly attempted to exemplify above) I think I would still need to know the choice index while writing the ink (which is not possible given the fact that I'm talking about conditionally available choices).

So I decided to tackle this by embedding some special characters into the choice string with an external function call that gives some meta info to the choice that lets me handle it differently than a normal choice (while still preserving the Story.ChooseChoiceIndex() branching functionality). It's pretty gross but I think it'll work for my purposes.

I'll show you what I mean with a more specific example once I get it visualized and maybe you see a better solution.

crustforever commented 8 years ago

Okay so here's my example finally. Note the choice I pick and its special behavior:

animation 3

The Ink for that choice currently looks like this:

choice_ink

Which says to only show that choice if the player has at least one unit of the species "liziachrus" in his party. Then, if shown, handle the choice with the special case, SpeciesPicker. This lets the player delegate one of his "liziachrus" units to do the action in the choice.

The way this works is really gross. The external function call in the choice text, {SpeciesPicker("liziachrus")}, is embedding some special mumbo jumbo into the text which then gets pulled out by my display choices function to handle it in a special way (i.e. display a unit picker for the given unit arguments, and then when picked, set the global variable "chosen_unit" to that unit.

I guess I am wondering if there is a better way to do this. I can't figure it out. The current way works I suppose, but it makes it a bit weird if I ever wanted to give players the ability to author their own stories.

joningold commented 8 years ago

I've only read your post once so far, but that's basically what we'd do, I think, only I'd put the "mumbo jumbo" in the ink itself as just some text, so maybe the choice becomes something like

[Return the gesture - SPECIESPICK LIZIACHRUS]

... because when I read that I know what it means and it "looks like" the intended UI. Then you pick out the all-caps from the choice text and handle it in the UI, as you're doing now.

cheers jon

On Mon, May 9, 2016 at 6:51 PM Jack notifications@github.com wrote:

Okay so here's my example finally. Note the choice I pick and its special behavior:

[image: animation 3] https://cloud.githubusercontent.com/assets/6498456/15121939/c96287c8-15ea-11e6-97b9-4824ca650b89.gif

The Ink for that choice currently looks like this:

[image: choice_ink] https://cloud.githubusercontent.com/assets/6498456/15122084/6c37184c-15eb-11e6-85f8-4326ee7a8916.PNG

Which says to only show that choice if the player has at least one unit of the species "liziachrus" in his party. Then, if shown, handle the choice with the special case, SpeciesPicker. This lets the player delegate one of his "liziachrus" units to do the action in the choice.

The way this works is really gross. The external function call in the choice text, {SpeciesPicker("liziachrus")}, is embedding some special mumbo jumbo into the text which then gets pulled out by my display choices function to handle it in a special way (i.e. display a unit picker for the given unit arguments, and then when picked, set the global variable "chosen_unit" to that unit.

I guess I am wondering if there is a better way to do this. I can't figure it out. The current way works I suppose, but it makes it a bit weird if I ever wanted to give players the ability to author their own stories.

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/inkle/ink/issues/75#issuecomment-217937491

joethephish commented 8 years ago

(Game looks cool!)

Yeah, as Jon said, what you're doing looks like the right approach. The only thing I'd say in terms of the general elegance of approach is to just try to make the content of the choice string as human-friendly as possible, as if you were writing it to be played by a human in a terminal.

I was wondering whether you could actually write it in such a way that the ink produces three choices, since that's semantically speaking what the player is choosing, and then wrap that up in the UI. Though I don't know whether it's technically feasible, since I'm not sure where the sub-choices actually come from!

crustforever commented 8 years ago

Thanks!

Good to know that's how you would approach it. As to the reason why I'm using an external function to embed the special text -- it's so that I can reuse the UnitPicker choice functionality, which takes unit IDs as arguments (e.g. I can use the same UnitPicker function for all units in the party that have ICE elemental affinity or whatever). So that external function is fetching the arguments and then embedding them into the function call meta text. Easier to do it that way as the party search is just a one-liner.

Joe -- that is interesting. How would I inject choices into the Ink at runtime? Those spawned choices would all point to the same divert, but that would at least allow me to add the setting of the "chosen_unit" into the Ink itself -- and that would be less hacky.

joethephish commented 8 years ago

Hrm, after playing around, my idea doesn't work. Never mind! :)