Folks - Creating a HTML5 live (~MMO) RPG with quests and real-time events

[This is a technical post about implementing some complicated RPG mechanics in a game engine]

Folks is a game I made during my last year at ISART Digital.
It is a social game meant to produce an experience of peaceful living on a little village, in the kind of the Animal Crossing series.
The pitch is "Move to your new village, and create bonds with your new neighboors. Explore your village and discover all its secrets: magic plants and trees, mysterious events and fun characters!"
It is developped in HTML5 for Facebook.
Logo Folks We were two developers, two designers, two artists and one sound designer. We had around one day per month of official group-work on this project for the year.

This post is mostly about the technical challenges encountered during the development. I will probably make another separated post about the game in itself.

Folks is one of the most challenging games I ever made, and I learned really a lot. On the technical side we had to make a full game engine (given the HTML5 state and how our game was, it was better do start from scratch).
Many of the game component we developped were things we never made before and had no clue how to make them.

So let's start with the basics:

  • Pipeline data to convert from assets produced by our 3D artists to the game in isometric 2D
  • Rendering engine for 2D including multiple layers (for example trees on floor with fruits on them and butterflies )
  • Animation engine. We're very proud of it as it was able to animate mostly anything including our characters, from simple json files
  • Real-time server and client gaming: The game map is dynamic and is saved during gameplay, events can be launched to and by the server anytime
  • Facebook communication: Players are able to get to maps or their friends through Facebook and modify them.
  • Quest branching and player progression
  • Dialog API
  • Scripting API for special game events
  • Vectorial map generation (which was abandonned later because it produced ugly stuff, so we came back to tiled maps)

Folks Screenshot

Scripting system

Considering our heavy needs of controling the world by quest/event/dialog interaction, we needed a robust scripting system. We implemented hooks in every needed behaviour of the game, making us able to call special behaviours at will (moving a PNJ to a location from example) or to detect special events (Player picking up a fruit on the ground).
With these hooks, we designed a simple scripting system so the designers could write scripts for their quests/events/dialogs. It looks like that:

sequence();  
movePNJToPlayer(pnjName: "julie");  
launchDialog(pnj: "julie", dialog:"julie_qst_03_03_launch");

waitAction(type : "gameobjectshovel", object : "ground");  
normal();

givePlayerReward(reward: ["apple x 3", "money x 1000"]);  
unlockTool(tool : "shovel");  
movePNJ(pnj: "julie", location: "previousLocation");  

The sequence and normal are the only programming-minded instructions.
The first is meant to execute each instruction one after each other, which means, waiting for each action to complete before launching the next (for the launchDialog it is waiting for the end of the dialog), so it is used for a sequence of actions.
The second is meant to execute every instructions at the same time, no matter they are finished or not. It's used when many things happen at once. In this case, giving a reward and unlocking a new tool.

With these two special instructions plus the hooks in game behaviours, we were able to try and create very complex events and quest behaviour, so we are happy with the result.

Live event system

One of the keys of the game design was the real-time calendar. To keep players coming to the game, we wanted to build a lot of events associated to the real-life calendar. For example: Sunday fishing contest, full-moon marathon, or more simply a NPC going to the bar every evening.

To do that, we implemented a socket communication between client and server. The server continuously runs a loop to check if some world events are to be launched/finished. Every time that happens, it sends an event to all connected clients with the script of the event. Then the script is executed on the client and so the event is launched. If a player connects, then his client receives the information of all currently running event scripts and they are launched too.

We didn't have the time to create game events for our demo, but we made a few tests like for example making a NPC run arround the map every minute. It worked quite well.

Quest System

The players were able to participate in quests, which followed the classical MMORPG mechanics: a PNJ has some quests, with conditions to unlock them. You take a quest, finish its steps one by one, and then receive a reward.
Folks Quest diary screenshot

Quest Life-cycle

The first thing we did here was to design the general life-cycle of a quest. Inspired by the Android app behaviour, we started with a simple life-cycle

1) Activate: This step unlocks the quest and makes it active
2) Start: This step launches the quest and makes it running
3) The player does stuff
4) Complete: The last step of the quest is finished, and thus the quest is Complete
5) The player validates the end of the quest, making it Finished

Quest life-cycle schema Every step is a function with before/after hooks so we can script arround it.
The activate function would just make the quest active. This would for example make the famous "exclamation mark" appearing on a NPC and the starting dialog be available.
Then the start function, launched in a script, would make the quest running and start its objective.
At this moment all the quest objectives are initialized and pushed to the quest diary (more on that later). Most of the time this happens after a dialog, but a quest could also be launched by cutting a tree or whatsoever
Once the player fas finished the last objective, the Complete function is called, marking the quest complete and unlocking its ending.
Once the player does the last ending action (validating quest as we say on MMOs) the action is marked as finished, its rewards are sent to the player, and it is deleted from the current quest diary.

Quest Objectives

While the quest is running, the player has a list of objectives to complete. Here's how it happens:

1) The quest fetches the current objective
2) The objective goes the same life-cycle as the quest, with each step containing many hooks that can run scripts written by the designer, defining what happens during the quest
3) when the objective is finished, the quest fetches the next objective
4) If no objective is found, the quest is complete

The quests are stocked in json files containing the description of the quest, its objectives and the associated scripts. example file (game tutorial)

Putting it together

Now, keeping track of the quests done is quite easy.
See, each quest has only two things to save: Its state, and its objectives.
Each objectives is a sort of subquest, that also has only its state to save.
That means that server side, you just have a quest object and its corresponding objective objects, to which you update the state each time the player changes something.

We did it this way:
A big "quest manager" class that keeps track of all the quests and take care of activating/desactivating them. Each time a quest or an objective changes, this quest manager knows it. It calls the main methods of each objectives, launches the potential scripts to launch, etc etc.
When it does change, it sends an event to the server, which will update this player's quests list.

Our mongo models for the quest just look like that:

var QuestSchema = new mongoose.Schema ({  
    id : String,
    step : {type : Number, default : 0}, // Current objective
    state : {type : String, default : "unactive"}, // Unactive, active, running, completed
    states : [{type : String, default : "unactive"}]
});

As you can see, we didn't even make an objective model, as it is quite simple. There is just an array of "states" which are each objective states, and a "step" which is the current step (active objective).
Pretty simple and efficient I think, this does the trick.

Game editors: The difference between a finished game and a prototype

So that stuff is pretty cool, but in the end we weren't able to make any rich quests or event. Why? Because we didn't have the time to develop powerfull editors.

Obviously developping all these systems take a lot of time, and considering the small number of working hours we had on this project and being only two devs, we just didn't have the time to do that, so we skip it.

The thing is, it would have been awesome to create quests with a GUI editor. I can imagine how easily it would work. It would only require a few days of work to create every editors needed for these systems, but these are days we didn't have, and the system was always changing as we better understood the exact needs of our game.
Consequently, our designers weren't able to use all these systems, and the only quests we were able to make are demo quests for the presentation of the game written by ourselves. But we were still able to verify that the system if efficient and powerfull, and that with good editors it would be easy to produce a lot of content.

Conclusion stuff

There is a lot of things I could write about just on the technical side of this project, but I won't have the time to do them all. Here I wrote about the RPG side, and how to actually create a nice and powerfull engine for designing complex quests.
If you want me to ellaborate on other things among the topic listed in the intro, please leave a comment :)


comments powered by Disqus