Skip to content

Creating Story

piitex edited this page Jan 23, 2025 · 8 revisions

Construction

THIS PAGE IS UNDER CONSTRUCTION!

Stories

Stories is the foundation of the novel. It is responsible for mapping and managing the scenes. Scenes are the frames within the game. You can view more about scenes here.

Creating a Story

To create a story we need to make a new class. I will call this IntroStory for example.

import me.piitex.renjava.api.stories.Story;
public class IntroStory extends Story {

  public IntroStory(String id) {
      super(id);
  }

  @Override
  public void init() {

  }
}

Adding Scenes

The 'init' function is used to add the scenes to the Story by using the addScene() function. Note, you will not be able to call this function if the class does not extends Story.

@Override
public void init() {
  // As of right now there is no Nar character. This will be created later.
  addScene(new ImageScene("1", nar, "It's time to wake up again.", new ImageOverlay("stories/intro/introbg.png")));
}

Let's break down the ImageScene. The first parameter is the ID. Every scene needs a unique ID. The second parameter is the character. As of right now we do not have a character called nar. You can pass null to the character but this will make the scene not render a text-box. The third parameter is the dialogue of what the character said. If you passed null for character it is pointless to add dialogue. The last parameter is the ImageOverlay. This is used as the background image. If you wish to render a black screen you can pass null.

Story API

There are a lot of ways to control and manage how stories work and function. You can control specific scenes in a story and event multiple. Stories can also have global controls, such as, setting music for the entire story.

There are global story events that handle the start and end of those stories.

Story story = RenJava.getInstance().getStory("story");
story.onStart(event -> {
    // Play music when the story starts
    RenJava.getTracks().playMusic("music.mp3", true);        
})
story.onEnd(event -> {
    // When the story ends start the next one.
    Story nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
    nextStory.start();
})

Mapping Stories

All stories must be mapped to the framework in 'createStory()' function. It is not required but highly recommended to do it in chronological order.

public class MyGame extends RenJava {
    ...
    
    @Override
    public void createStory() {
        new MyFirstStory("story-id");
        new MySecondStory("second-story");
        new MyThirdStory("third-story");
    }
}

public class MyFirstStory extends Story {
    
    public MyFirstStory(String id) {
        super(id);
    }
    
    @Override
    public void init() {
        // Add scenes here.
    }
}

You can also map stories within the story constructor. This will map the next story when the first one is constructed.

public class MyFirstStory extends Story {
    
    public MyFirstStory(String id) {
        super(id);
        new MySecondStory("second-story");
    }
    
    @Override
    public void init() {
        // Add scenes here.
    }
}

public class MySecondStory extends Story {

    public MySecondStory(String id) {
        super(id);
        new MyThirdStory("third-story");
    }

    @Override
    public void init() {
        // Add scenes here.
    }
}

Branching Stories

The main aspect of VN games are the different routes a player can take, the way a story can shape itself with the players decisions. The API was shaped around this idea and mechanic. One way a player can make decisions is with the ChoiceScene

ChoiceScene choiceScene = new ChoiceScene("4");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));

// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
    // This is an action listener. The code here is called when they click a choice.
    Choice choice = event.getChoice(); // Gets the choice button they clocked.   
    if (choice.getId().equalsIgnoreCase("help")) {
        // The player selected the help choice
    } else if (choice.getID().equalsIgnoreCase("run")) {
        // The player decided to run away
    }
})

Events are the main function to shape your story. Different scenes may have specific events built in them while some may not. All scenes will have start and end events. It is imperative you pick the proper scene to shape your story.

There are a few ways you can map your story with different routes. Depending on the impact of the decision you may have to create a separate story entirely. The below is an example of dealing with different choices in the same story.

ChoiceScene choiceScene = new ChoiceScene("1");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));

// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
    // This is an action listener. The code here is called when they click a choice.
    Choice choice = event.getChoice(); // Gets the choice button they clocked.   
    if (choice.getId().equalsIgnoreCase("help")) {
        // The player selected the help choice
        
        // Lets say this decision isn't important and it only ahieves a couple of lines of unique dialogue.
        // We can skip down to that specific part of the dialogue.
        displayScene("3");
    } else if (choice.getId().equalsIgnoreCase("run")) {
        // The player decided to run away
        displayScene("2");
    }
});

addScene(new ImageScene("2", mc, "I can't risking my life here. I've put everything on the line. I must live.")
    .onEnd(event -> {
        // When this event ends we do not want it to play the dialogue that shows when they saved the women.
        event.setAutoPlayNextScene(false); // This is very important!!! Stories will automatically run the game and play the next scene. We need to disable this behavior to properly display the scenes.
        displayScene("4");
    }));

addScene(new ImageScene("3", mc, "Here, take my hand. We must hurry!")
    .onEnd(event -> {
        event.setAutoPlayNextScene(false); 
        displayScene("4");
    }));

addScene(new ImageScene("4", mc, "I got out, though barely. That was a lot closer than I ever wanted to."));

This is how you would do everything in a separate story. I highly recommend this method especially for complex and important decisions. Simple ones that only achieve a different string of dialogue are easier the other way.

ChoiceScene choiceScene = new ChoiceScene("1");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));

// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
    // This is an action listener. The code here is called when they click a choice.
    Choice choice = event.getChoice(); // Gets the choice button they clocked.   
    if (choice.getId().equalsIgnoreCase("help")) {
        // The player selected the help choice
        SaveCompanionStory saveStory = (SaveCompanionStory) RenJava.getInstance().getPlayer().getStory("save-companion");
        saveStory.start();
    } else if (choice.getId().equalsIgnoreCase("run")) {
        // The player decided to run away
        LeaveCompanionStory leaveStory = (LeaveCompanionStory) RenJava.getInstance().getPlayer().getStory("leave-companion");
        leaveStory.start();
    }
});

Chaining Stories

You can chain stories so they play after each-other. You can do this by calling the .start() function inside the scene events. Or can you do so by mapping the stories after each one.

// This is the final scene in the story. Once it ends play the next story
addScene(new ImageScene("4", mc, "I got out, though barely. That was a lot closer than I ever wanted to.")
    .onEnd(event -> {
        NextStory nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
        nextStory.start();
    }));

To map stories in a specific order.

public class MyStory extends Story {
    
    public MyStory() {
        super("my-story");
        onEnd(event -> {
            // This is called when the story has reached its end.
            // Call next story
            NextStory nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
            nextStory.start();
        });
    }
}