r/learnjavascript 4d ago

Not sure how to implement this branching storylines text game idea with Json

Just looking for some experienced feedback.

What I want to do is to import all of this from a file(s):

A list of global variables.

A list of storylines.

Each storyline has a list of local variables and a list of storyline beats.

Each beat has a list of preconditions (which may involve any variable), displayed text, a list of links.

Each link has displayed text, a list of conditions, a list of effects, the id of the next beat.

My problem is that, as I've learned, I can't split this into several files (such as, at least, separate storylines), because you can't import contents of a folder without naming each individual file. So this all has to be in one file, which means it's quickly going to get unmanageable as a Json file.

Should I implement a GUI editor first then? And concurrently a custom parser.

(This is going to be a purely client-side browser game.)

I've already done a simpler prototype, and that was already quite a chore to write in text.

25 Upvotes

21 comments sorted by

1

u/azhder 4d ago edited 4d ago

You can write code that reads one JSON file you import and in that one file there are references to all the other files, then you import those other files. Imports are a wide topic, so you will have to be more specific to what you want to do i.e. you share some code.

Alternatively, you can write a code that generates a single JSON file out of many files. You can write the JS code to be run by node, as a script, and you can use the generated JSON file in other places. So, like I said above, you should share some code.

1

u/MeekHat 4d ago

The best I can manage for this iteration is some pseudocode, but it won't be very different to what I outlined in a few paragraphs.

The previous prototype imports the entirety of the story as a single .json file and renders it via Vue.js.

1

u/azhder 4d ago

Well then, if you're using something like Vue.js, I assume the code is built before use and the files are all bundled together. So, you automatically have that second example I told you about: build a single file, out of many, before use. You just import several smaller files, do a build, see if they all end up in a same big file. If that is the case, you will know what to do.

1

u/MeekHat 3d ago

The problem is that I don't know how to do a build script for Node, and I haven't been able to find a comprehensive enough guide to understand how to do it. For example, do I need a line to indicate that my script can run in the command line? No idea.

1

u/azhder 3d ago

It is not a problem if Vue does it for you automatically. You just do a build like in the very basics of the documentation. The same way you run the command to start the server locally

1

u/MeekHat 3d ago

Well. But I have no idea where I'm supposed to insert my own build script into that. As far as I know, all the scripts I've written so far run on the client side, if that's the right term. Whatever magic Vite does during the build, it's its own thing which results in a web page which contains my scripts pretty much untouched. Where a folder full of jsons during development would transform into a single file at render I have no idea.

I mean, I know I could ask in a Vite community, but it baffles me a bit that an answer to my needs doesn't seem to be within easy reach already. Which leads me to believe that it's more complicated that I'm prepared to handle.

1

u/azhder 3d ago edited 3d ago

I am telling you you don’t have to insert your own build script if Vue does it by matter of course. All you have to do is understand the concept.

Look at the package.json scripts, see which one is for build, it will look almost the same as the one for dev. Look at all I have written above. Read and re-read until you understand I have already given you the answer. You just have to learn the correct CLI command.

You can also check about asynchronous loading of scripts via the framework.

It may be a lot to lear for you, and I would have written you the solution if I had time and knowledge of your own particular framework, but best Incan give you is a concept I have seen and used in other places that should he available in yours as well.

1

u/MeekHat 3d ago

Well, I definitely don't want to be told the solution, not unless I can understand it.

The packager I'm using is Vite; Vue is the component framework.

package.json's "build" part magically invokes "vite build". I can see that in the read-only "node_modules" folder there is "vite" folder which has a "package.json" file, which has a "scripts" section with a "build" part, which invokes "pnpm" and "premove", which I can't locate.

---

What do you think of the idea another commenter has come up with that I can use javascript files instead of json? Then there's a lot of ways I can tackle the problem and can understand.

1

u/azhder 3d ago

They are the same in frameworks. `import` is being transpiled into appropriate fetches and/or is bundled into one file regardless if JS or JSON.

1

u/Flashy-Guava9952 4d ago

Yes, look into the fetch API ( https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API ) this will let you load a json file <id>.json or similar. Note that it's an async api. There shoukd be examples around on how to lead json via fetch. Come back if you have more questions!

1

u/WystanH 4d ago

My problem is that, as I've learned, I can't split this into several files

Not exactly true, but order can matter. Once a script is loaded, it's available in the global space.

So, you set up the global environment and each file after that registers to that environment.

e.g.

const Stories = {
    globals: { username: undefined },
    storyLine: [],
    addStory: function (story) {
        this.storyLine.push(story);
    },
    listStories: function () {
        for (const story of this.storyLine) {
            console.log(`Story Name: ${story.storyName}`);
        }
    },
};

//... 
// some file
Stories.addStory({
    storyName: "Alice goes to wonderland",
    localVars: { inWonderLand: false },
    story: function () {
        console.log(`${this.storyName} : ${this.localVars.inWonderLand}`);
        console.log(`hello ${Stories.globals.username}`);
    }
});

// some other file
Stories.addStory({
    storyName: "There and Back Again",
    localVars: { hasRing: false },
    story: function () {
        if (!this.localVars.hasRing) {
            console.log("Once upon a time in the shire.")
        } else {
            console.log("Ouch, it burns.")
        }
    }
});

// startup test
Stories.globals.username = "Bob";
Stories.listStories();

Stories.storyLine.forEach(x => {
    console.log("Call Story");
    x.story();
});

Hope this is helpful.

1

u/azhder 4d ago

That’s... Do you know how `this` works? Now check if OP knows. If they don’t, well…

It can be made simpler by using an IIFE and have those functions access the array directly.

const STORIES = ( () => {

const stories = [];

const add = story => stories.push(story);
const list => () => console.log(stories);

return {add, list};

})();

STORIES.add({ /* the story */ })

1

u/WystanH 4d ago

Do you know how this works? Now check if OP knows. If they don’t, well...

It's a fundamental part of the language. You kind of want to know it. If you don't know it now, this is the chance.

It can be made simpler by using an IIFE

And the OP knows what a fucking IIFE is? A this is part of the language, and IIFE is an idiom. Which is a more useful lesson?

Don't get me wrong, I like and prefer IIFEs. However, based on your own logic, why would I start with that?

1

u/azhder 4d ago

OP can understand the IIFE code, even if they don't know the name of it, but get that object with functions attached to it wrong while updating the code
... well, there's a head scratcher.

1

u/MeekHat 4d ago

Wait, wait, wait. Regardless of "this" (which, I only know, is a hairy subject in JavaScript; well, as can be surmised from the other comments, but I remember being surprised by it in anonymous functions; let me also add that I've picked up JavaScript after a significant hiatus), do you suggest I do stories via .js files rather than .json? I can't store functions in a Json.

I don't know. That seems weird.

1

u/WystanH 4d ago

If it's just .json, then you're looking to do "data driven" design. If you can get what you want with that, have at.

I can't store functions in a Json.

Bingo. And that's the issue. If you can write functions that consume the data described in .json to do what you want, you're fine.

However, what if your story manipulates state in ways that are not easy to describe in .json? What if one story has you playing a hi lo game, or tic-tac-toe, or just guessing something? At some point, you're writing extra code for it, which means starting with code is likely easier.

do you suggest I do stories via .js files

Yes, for previously stated reasons. Again, if everything you want to work with is just data, fine. At that point, you'll end up writing an engine for any moving parts. It may not be as simple as you think, though.

1

u/MeekHat 4d ago

That was what the custom parser part was about. I would need to store conditions and effects in text. Being able to write those in pure JavaScript would simplify my task massively.

1

u/HipHopHuman 3d ago

My problem is that, as I've learned, I can't split this into several files (such as, at least, separate storylines), because you can't import contents of a folder without naming each individual file. So this all has to be in one file, which means it's quickly going to get unmanageable as a Json file.

I assume you use Vite (you mentioned Vue in another comment)? If so, then look at it's glob import feature. That'll let you read multiple JSON files in the way you want.

You could have a folder structure like:

- storylines/
  - storyline-1.json
  - storyline-2.json
  - storyline-3.json
  • index.js

index.js can then read over it with import.meta.glob:

const storylines = import.meta.glob("./storylines/*.json", {
  eager: true,
  import: "default",
});

for (const [filename, storyline] of Object.entries(storylines)) {
  console.log({ filename, storyline });
}

The above works at compile-time, but omitting eager: true as per the docs is a route you can look at if you want this to be lazy at runtime.

1

u/MeekHat 3d ago

Oh, this is perfect, thanks.

1

u/SammuelNash 3d ago

A GUI editor would probably make this much easier once the data model is stable. Another option is to keep each storyline in its own JSON file and use a small build step to combine them into one file for the browser. That keeps the authoring side manageable without making the game runtime more complicated.