real-time open-world text adventure

san

Member
hey!

i have a relatively ambitious idea — to make a real-time open-world text adventure. i chose GM, because in addition to the usual text-based narration and answer choices, i want to incorporate different visual and audio effects into the game, depending on the environment. i want the game time of day to be changing constantly as well.

i've watched a series of tutorials from the official GM channel and made this game where you need to fly a spaceship and shoot at asteroids. after that i've tried to intuitively push buttons and write code using the commands and language structures manual, but this is definitely not enough, because i don't have a general idea on how to use GML and GM in general, so i have some questions for experienced developers.

in my idea there are three categories of locations in the game. big locations (countries, regions), usual locations (city, settlement), small locations (building interiors, squares, street pieces). characters move between locations due to business or time of day (for example, a character may work at the market all day and go to sleep in his house at night). the player can communicate with them, and some of them give quests.

i don't have enough understanding how to build the structure of such a game.

it seemed like a good solution to load the characters and locations replies from a JSON file so i've found some tutorials and posts on the subject. it also seems logical to me that each character should be an object that has different states (so that characters use different lines depending on how the story goes). but i have no idea how to organize the rooms better:
each room is a small location?
there is a single game room, and the locations are set in an array or JSON?

how do i implement the essence of the in-game time? should it be a script?

i would be very grateful if someone could clarify how to arrange the structure for my game and try to give me as much detail as possible about the basic principles that should guide me. please ask me anything if something looks unclear or messy!

also if you know for sure that the implementation using Python, for example, for these purposes is going to be much easier, please let me know.
 
Last edited:

eimie

Member
This is an interesting problem, but especially the walking and story sensitive NPCs can make the whole thing much more complicated as it seems at a first glance. The easiest way could be to make one single room and represent the locations and the NPCs as objects. You could change locations by activating and deactivating them with a variable.

Let's consider you start the game in a wood and meet an old man who tells you what to do. Each location can be connected to four other locations and hold up to three NPC's.

That's just a suggestion how the locations could(!) be realized:

The create event for obj_wood could look so:
GML:
location_active = true;

next_location_west = obj_village;
next_location_east = obj_mountains;
next_location_north = noone;
next_location_south = noone;

npc[0] = obj_wiseMan;
npc[1] = noone;
npc[2] = noone;

text = "You awake in a dark wood and can't remember your quest nor your name. ";
text += "There is a village to the west and you can see mountains in the east. ";
if (npc[0] != noone && npc[1] == noone) text += "You see " + npc[0].name + ". ";
if (npc[0] != noone && npc[1] != noone && npc[2] == noone) text += "You see " + npc[0].name + " and " + npc[1].name;
if (npc[0] != noone && npc[1] != noone && npc[2] != noone) text += "You see " + npc[0].name + ", " + npc[1].name + " and " + npc[2].name;
The step event:
GML:
// walking
if (keyboard_check_released(ord('W'))) {
   if (next_locatio_west != noone) {
      next_location_west.location_active = true;
      location_active = false;
   }
}
else if (keyboard_check_released(ord('E'))) {
   if (next_locatio_east != noone) {
      next_location_east.location_active = true;
      location_active = false;
   }
}
else if (keyboard_check_released(ord('N'))) {
   if (next_locatio_north != noone) {
      next_location_north.location_active = true;
      location_active = false;
   }
}
else if (keyboard_check_released(ord('S'))) {
   if (next_locatio_south != noone) {
      next_location_south.location_active = true;
      location_active = false;
   }
}


//talking
else if (keyboard_check_released(ord("1"))) {
   if (npc[0] != noone) {
      talk_to(npc[0]);
   }
}
else if (keyboard_check_released(ord("2"))) {
   if (npc[1] != noone) {
      talk_to(npc[1]);
   }
}
else if (keyboard_check_released(ord("3"))) {
   if (npc[2] != noone) {
      talk_to(npc[2]);
   }
}
The talk_to function and the NPC's changing the rooms will be your biggest challenges if you want your NPC's walking around and be story-sensitive.

I would recommend to plan a tiny prototype or demo with pen and paper and then see how complicated it becomes.

Btw: you stated that you are a beginner with GameMaker, but you want to use JSON. Do you have experience in other programming languages as GML?
 

san

Member
hey, eimie! thank you very much for your response!

i appreciate you've written some code for me to show your view. i might use some of these for my first tries!

i actually had some time yesterday to take several attempts of building simple command-line-based prototypes using Python, just to understand how much work for the game engine is there. so i came up with an idea to split the whole game world into parts, like a two dimensional array, each cell of which contains one more array — so all the locations can be stored in the cells as objects. is it a suitable decision for GM?

it seems for me now that firstly i have to implement a kind of an AI system for all the NPCs walking between locations. but there has to be plenty of them, do i still should stick to the objects for each single character?

Btw: you stated that you are a beginner with GameMaker, but you want to use JSON. Do you have experience in other programming languages as GML?
eh, i have a little Java coding experience but it was all about maths and command prompt programs, i just suppose it is not that complicated so i should be good :)
 

Heathenlamb

Member
Look up the The Hobbit text game from 1982 ... it was a real time time text adventure. Was widely regarded as an amazing feat at the time. It will get you going in the right direction.
 

eimie

Member
You're welcome. The idea with the two dimensional array will work for sure and save the four direction variables.

But you should consider that there should be a rule if the player or the NPCs can walk in every direction or not. In my example the player can only walk to west or east, but not to south or north because there is no location referenced. You can imagine the location references as pointers and the whole system as a kind of two-dimensional linked list (computer scientists: please don't kill me for this picture). This can replace the array and allows you to create long one dimensional paths or complex map structures like mazes pretty easy. But if you want free movement in all directions, your array approach is simpler and therefore better, of course. :)

I have the impression that you have sufficient programming skills for this project. It will depend above all on good pen and paper work. I wish you every success and I'm excited to see how it turns out! Studying existing games as Heathenlamb stated is also a good idea.

If you still have questions, don't hesitate to ask.
 

san

Member
i am very glad you guys responded me!

i think i'm gonna take some time to think everything through and if something goes wrong come again here :)
 

san

Member
so here i am with my first struggle.

i figured everything out on paper and started with the open-world: a 2d array decision was thrown out, and instead it is easier and more efficient to build the world on nodes. it was exactly the same path of thought like Lowang from this thread had. and there is an answer on his question so i copied it and tried to make a little prototype.

GML:
enum location {
    Blackwood,
    Field,
    Lake
}

locs[location.Blackwood] = {
    locName: "Blackwood",
    locText: "You are in a wood.",
    locCharacters: obj_ch_Sohna,
    nextLocs: [location.Field]
}

locs[location.Field] = {
    locName: "Field",
    locText: "You are in a field.",
    locCharacters: -1,
    nextLocs: [location.Blackwood,location.Lake]
}

locs[location.Lake] = {
    locName: "Lake",
    locText: "You are on a lake.",
    locCharacters: -1,
    nextLocs: [location.Field]
}

currentLoc = locs[location.Blackwood];
and in the Draw Event:
GML:
draw_set_colour(c_white);
draw_set_font(fnt_font);

draw_text(20,30,string(currentLoc.locText));
draw_text(20,60,"You can go to "+string(currentLoc.nextLocs.locName));
the second draw_text of course won't work now, but i hope you would understand my intentions — to store links to adjacent locations inside each location so i could put the names of these on the screen.

so how do i do that?
 

TailBit

Member
Maybe something like this
GML:
var str = "You can go to ";
var len = array_length(currentLoc.locs)
for(var i=0;i<len;i++){
    str += locs[ currentLoc.locs[i] ].locName;
    if(i<len-2) str+=", " else
    if(i<len-1) str+=" and " else str+= "."
}

draw_text(20,60,str)
I would try make a editor for this, and just drop the enums.

What if two places had the same event blocking the path?
GML:
locs[LOCATION.FOREST] = {
    locName: "forest", locText: "", locCharacters: [],
    paths: [PATH.FOREST_CITY]
}

locs[LOCATION.CITY] = {
    locName: "city", locText: "", locCharacters: [],
    nextLocs: [PATH.FOREST_CITY]
}

paths[PATH.FOREST_CITY] = {
    link: [LOCATION.FOREST,LOCATION.CITY],
    event: EVENT.BOULDER,
    open: "the boulder is hovering .. some magic is in the works",
    closed: "there is a boulder blocking the way",
    opening: "the mage swipe his wand and the boulder lifts off",
    closing: "suddenly the magic holding it up fades and the boulder falls back into place"
}

event[EVENT.BOULDER] = 0;
 
Last edited:

san

Member
thank you so much TailBit!

it is such a great idea to put the events to the count here. i was thinking to add them as variables and choose between different instances of the same location, but this is much better.
 

TailBit

Member
Well, I was considering
nextLocs: [LOCATION.A, [LOCATION.B, EVENT.BOULDER] ]
But then you would need to write the same event in the opposite location

I have been trying to plan out a small world like this for some time, I wanted to give NPCs a list of events they could ignore when following the room nodes, but have them trigger it when they do pass them ..

EDIT: I do not recommend having currentLoc be the struct itself instead of the array index, I would had put the struct shortcut in a separate variable
GML:
var p,next,str = "You can go to ";
var here = locs[currentLoc];
var len = array_length(here.locs)

for(var i=0;i<len;i++){
    p = here.locs[i];

    next = p.link[p.link[0]==currentLoc];

    str += locs[ next ].locName;

    if(i<len-2) str+=", " else
    if(i<len-1) str+=" and " else str+= "."
}

draw_text(20,60,str)
I rewrote the code in case you change to using paths .. note that I use currentLoc as if it contained a location enum here.

But you could have curLoc be the location and currentLoc be the struct
 
Last edited:
Top