Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Tentative attempts towards a framework for an RPG

edited April 2014 in Workshop
Disclaimer: The technique here uses the <<widget>> macro, specific to the Sugarcube story format, and the <<timedcycle>> macro, first described here and modified for Sugarcube on the motoslave site (it can be downloaded from there).

So I said elsewhere I'd be writing actual snippets of prose to discuss for gameplay possibilities, but then I wrote some, thought about what hyperlinks and post-passage options mean in terms of mechanics, then how it would have to be realized in Twine, and finally concluded that the stuff I'd like to do would be a Lovecraftian nightmare of tentacled conditionals. The whole "it would be impossible to actually do"-thing really put a thick, throbbing lovespear right through my wheels, killed my motivation to really write anything.

So then I thought about how it could be solved and I think I've arrived at some tentative answers.

Mine include Sugarcube, so be warned. It also includes heavy abuse of the widget macro.

I

The main two powerhouses of this framework are the aforementioned
<<widget>>
and also the
<<timedcycle 1s>>
macro.

And this is the skeleton:
<<widget wazzup>>
<<print "<<"+$a+">>">>
<</widget>>
When you run this widget like so:
<<timedcycle 1s>><<wazzup>><</timedcycle>>
it starts a continuous loop of printing the <<$a>> macro. Now what's this then? It's also a widget, only one with a changeable name (according to the value of $a). How does $a change then? Like so:
<<set $a = []>>
<<widget add>><<set $a.sort($a.push($args))>><</widget>>
What does this do?
A. It sets $a as an array.
B. It adds the arguments of the <<add>> widget to the array. Ex.: <<add aaa_loc_mountain bbb_pc_barbarian>> will add the values "aaa_loc_mountain" and "bbb_pc_barbarian" to the $a array. (I'll explain the strange names further down.)
C.  It sorts the array in alphabetical order so the "aaa" values will always be first, the "bbb" values always are second, etc.

So when the continuous loop prints <<$a>> it actually prints a widget whose name is the value of $a. In this case it prints the
<<aaa_loc_mountain,bbb_pc_barbarian>>
widget . What does the widget contain then? What is the actual text or code on the page? Well, whatever you want. In this example it would be the text the player sees when he's in the mountains and he's chosen the barbarian class PC. Essentially, a rearranged
<<if $loc == "mountain" and if $pc == "barbarian>>Placeholder text<</endif>>/
Why is this important?

1. You only need to do it once. You set up the framework and goodbye if/else. The only way you interact with this after you set up the framework is like so:
<<add loc_X npc_Y critter_Z>>
Then (or before that) you write a widget with the name that results when you concatenate the above arguments, i.e. "cavern,sidekickJoe,grubmonster" and within that widget you write the text/code that comes out. (Imagine it as writing a passage with that name, only you can cram as many of them within a passage.)

In case I've made it overly confusing, here's what the end-user of the framework would have to do, even if he/she doesn't know a single thing of the above. Here's a sample screen:
<<set $a = []>>
<<widget add>><<set $a.sort($a.push($args))>><</widget>> \\This is the framework's setup
<<widget wazzup>><<print "<<"+$a+">>">><</widget>>





<<widget aa_loc_desert,bb_pc_barbarian>>You are in the desert.

You are a barbarian. Ooga.
<</widget>>



<<widget aa_loc_desert,bb_pc_barbarian,cc_pcstate_wounded>>You are in the desert.

You are a barbarian. Your are wounded. Ouch.
<</widget>>


\\The above is the content.




<<add aa_loc_desert bb_pc_barbarian>>
<<timedcycle 1s>><<wazzup>><<endtimedcycle>>

\This is all the code you need on the actual passage page.

If you need to change it to the text that shows a wounded barbarian, you do this:

<<add cc_pcstate_wounded>>
<<wazzup>>
If for some reason you need to empty the $a array, just set $a = [], and it's ready to be filled again.
Now, one thing that might seem off-putting about this (whoa, just one? don't think so :D) is that the widget names/states might get rather long. Still, a) they are even longer in if/else form, and b) these here are alphabetically sorted so it's less likely to get utterly lost among the long strings of text.

Another hiccup is that if the <<wazzup>> macro prints a widget whose name doesn't exist, you'll get a big ugly red error message. This is easily solvable in the final product by just suppressing error messages with this piece of CSS: ".error {display:none};" This means that names that don't correspond with pieces of content just won't show instead of shining a bright red light in the player's eyes.

P.S. All of the above is just a modified approximation of some the concepts laid out in Elan Ruskin's GDC 2012 talk on Dynamically Generated Dialogue. And since in text games written content is all there is (even images are "written" content), I thought Ruskin's concepts could be brought to bear on the entirety of a project instead of just on dialogue.

Comments

  • I'd like to see it working.  I just read your post twice and I still am not sure what the end result looks like.
  • Had the feeling it would come to that :) I'm preparing a proof of concept, something very very basic, and it should be ready till the end of the weekend. In the meantime, here's another summation of the whole method:

    Instead of writing out if/else conditionals in every passage, meshed with the content they're supposed to output, I write a single widget that would concatenate any number of arguments I pass onto it and then redirect the player to the content that corresponds to them; the content itself is written separately, without the long strings of if/else-ing in between that muddle it.

    (And they really do, I know of at least two cases where a writer has come to blows with the wall, simply because he made his if/else-ing so incomprehensible to himself that he couldn't see where the problem was and why it wasn't working. A number of programmer friends of mine also have said that it's common knowledge that you just don't do if/else if you want something with a lot of state transitions in it; unless you wanna waste time and sanity.)
  • Can you explain how to do this without <<widget>> and without <<timedcycle>>? For simplicity's sake I'd like to know how to do this with stock Twine. :)

    Also, your post really should have "Sugarcube" somewhere in the title, and a mention of where to get the <<timedcycle>> macro!
  • Well, it does have Sugarcube at the start of the post itself, so noone would have to read the whole thing before learning that :) Also, I thought <<timedcycle>> was common knowledge: the original is on L's blog, where all the other custom macros are. So yeah, that's where it is, just Google "L's blog Twine timedcycle".


    As to how to do it with regular Twine... I almost feel there's no point to it without <<widget>>, but here it is:

    <<set $passagename = []>>

    Whenever you want to add a condition that would change the output, do this:

    <<set $passagename.push["condition X"]>>

    When you want to display the output, do this:

    <<display $passagename>>

    Then you just write a passage with the name that corresponds to the $passagename array.

    As I said, <<widget>> and <<timedcycle>> comprise the main engine and I don't think it's feasible to do that without them, as you would have to write a lot more code.

    As to a working example of what I'm talking about:
    :: Start

    <<silently>><<display "widgets">><</silently>>

    <<addgen $loc $pc>>

    <<show>>

    :: widgets

    <<widget desert,barbarian>>You're a barbarian, in a desert.<</widget>>
    <<widget forest,barbarian>>You're a barbarian, in a forest.<</widget>>
    <<widget plains,barbarian>>You're a barbarian, on the plains.<</widget>>
    <<widget plains,mage>>You're a mage, on the plains.<</widget>>
    <<widget desert,mage>>You're a mage, in the desert.<</widget>>
    <<widget forest,mage>>You're a mage, in the forest.<</widget>>
    <<widget forest,rogue>>You're a rogue, in the forest.<</widget>>
    <<widget desert,rogue>>You're a rogue, in the desert.<</widget>>
    <<widget plains,rogue>>You're a rogue, on the plains.<</widget>>


    <<set $loc = either("desert","forest","plains")>>
    <<set $pc = either("barbarian", "mage", "rogue")>>
    <<widget addgen>><<set $general =[]>><<set $general.push($args)>><</widget>>

    <<widget show>><<print "<<"+$general+">>">><</widget>>

    Obviously, the output texts for a real project would be much more diverse and different from one another.








  • I think I see what you're saying about it being pointless without <<widgets>>. Instead of
    <<widget a,b>>text<</widget>>
    you'd have to have an if/then statement detecting if the contents of the array were a specified combination and then printing the text (or what have you), I think. Which is what you're trying to avoid.

    Anyways, thanks. I'm curious about object-oriented programming in Twine, or libraries as in Inform, TADS, etc, and wanted to see if this would be a step in that direction. Ultimately this is all more than what I'm doing in my current game but I'm still curious about it. Chris Klimas wrote about this recently, that Twine "doesn't have a world model," which is fine. Obviously, to add a world-model to Twine, you have to augment Twine with other things. I think that's what you're trying to do here. What I'm really curious about though is how to create a world-model that allows you to "put an apple inside a knapsack and then place that in a refrigerator the player can push around" with relative ease, as it were.

    Sorry about the (perhaps just partial?) change of subject. I'm not a programmer but I've been enjoying learning the bits of programming that come with using Twine, and I enjoy trying to suss out things like this, where I do have somewhat of a grasp on what's being said.

    Have you heard of TwineQuest? I looked at some of its documentation but I can't tell if anything substantial's been added to it. Maybe (likely) it's above my head. It's supposed to be a framework for adding libraries or world-models to Twine, basically.

    PS It's not entirely clear from the original post that the widget macro is specific to Sugarcube; I only knew because I've perused the Sugarcube documentation before. And I don't think people coming straight from the Twine homepage to this forum will necessarily know about L's macros page. I do, but I didn't know that <<timedcycle>> was from there, because I don't have memorized the names of all of the available macros for Twine, Sugarcube, and from L's site, and I don't use <<timedcycle>>. I had to Google about to figure it out. That's why I recommend making those two things clearer. Just a thought. Thanks again for your post.
  • I think it would be possible to add world model to Twine with this.
    The big thing about widgets for me is that within them you can specify a piece of code or formula of just about any complexity and then just call it easily and repeatedly with a simple <<anyname>> macro. Arrays also work in that direction.

    loopernow, I suggest you find a copy of Elan Ruskin's slides from GDC 2012 (this should be enough to find it in Google, I don't remember where I got my copy from...) to see what my method is trying to emulate. The main thing for me was that Ruskin basically blew off if/else at the very beginning of his presentation, so I've tried to do the same. 
  • Interesting. I'll take a look around for that.
Sign In or Register to comment.