1
\$\begingroup\$

Building forward my text adventure i stumbled upon how to actually drive the actions that can be caused by dialogs. I wrote the dialogues and the logic around handling them already, but i'm missing a key about how to turn a dialogue into an 'engine' specific action (like spawn item, drop, buy, sell).

My game is absolutely based on JSON therefore even dialogues are stored there:

enter image description here

An Option is what a player can choose by typing one of the defined Keywords for currently displayed dialogue.

Response is what a player sees when option is chosen

Goto is where the dialogue goes next after an option is chosen

Script is nothing more fancy than a list of strings with integers that get mapped into a std::map<std::string,int> and later passed for the execution. Think of it as of flags.

Let's look at the example below - where you could buy something from inn keeper.

enter image description here

In above situation, you can talk to the keeper and choose:

  • "1" to buy item "1"
  • "2" to buy item "2"
  • "back" to get back to previous dialogue (with a goto)

Let's say i want to choose option "1" and it boils down to the design now.

How would you turn that "1" into a real engine action of buying an item?

What i did.

As you might have seen i have defined a Script within the option of the dialogue, this would be just a map (string to int) of attributes passed to an interpreter.

enter image description here

Then the interpreter executes that based on 'action_type' - which is defined in json, mandatory and describes type of action like here:

enter image description here

enter image description here

Some questions then:

1) Is it the valid approach to write my own interpreter - maybe i can use it somewhere else? or i should start thinking about external scripts - which actually require me to focus on adding library and learning it, instead of doing the game itself.

2) Which class should be really responsible for actually executing the script with the data passed from dialogue.

Dialog class itself ?

std::string CDialogue::Process(const std::vector<std::string>& rvArguments)

Npc class handling talk action ?

void CEntityNPC::HandleTalk(CEntity *pFromWho, const std::vector<std::string>& rvArguments)

Command class that handles talk command?

void CCommandTalk::Execute(CEntity *pPlayer,const std::vector<std::string>& rvArguments)

3) Maybe you can think of a different way to handle this situation? Basically a translation of a text(json) data into a game logic.

\$\endgroup\$
  • \$\begingroup\$ No time for a longer discussion atm, but for now: I think you're picking at a general tension between being able to author code-free content files that keep the "game" separate from the "engine", and being able to readily customize behavior anywhere. Either may be appropriate depending on the scale/scope of your project. Can you ballpark? (numbers of rooms/npcs/equipment? small with a well-contained story, or sprawling with many? etc.) \$\endgroup\$ – abathur Sep 10 '18 at 14:56
  • \$\begingroup\$ @abathur Hi, thanks for your reply. I decided to start small, but i already have plans to expand more and more. So to ballpark, i assume i will have around 100 Rooms, and NPC's rarely on the go - but definitely in core locations towns (shops, taverns, banks, ... ). For the equipment, it's a classic non-fantasy world i work on, so you have basic weapons with 3 levels i.e : Basic Steel Sword, Medium Steel Sword ,Advanced Steel Sword. A lot of entities however you can pick up and harvest / craft into something - like trees -> chopped to wood -> ... \$\endgroup\$ – PeeS Sep 10 '18 at 18:57
2
\$\begingroup\$

Brief context: I work on a fairly large LDMud with mostly (but not entirely) hard-coded objects subclassed from a few primary blueprints. I spend some of my time trying to improve our long-term maintainability by refactoring common patterns out of the "content" objects and into the core.

  1. You probably don't want to write an interpreter at this scale unless you want to learn how to write an interpreter. I'm not exactly sure what you mean when you say external scripts here, and I'm also skeptical that your scale would make it worth picking up a library unless it's reasonably small/simple/focused.

    I would spend some time pondering what you like about JSON, why you want to structure the game around it, and (briefly) getting a sense of what text-game libraries might be real options for you. If you embed executable scripts in JSON, code and content will no longer be separate, and you'll lose all of the advantages of code in the process.

  2. I'm not sure there's a silver-bullet answer here, but I'd probably try to abstract command-handling out into parts so that a number of different entities could handle the command, and then start out with doing it on the NPC. It seems like trying to do all of this work in a dialogue or command class could lead to a lot of complexity accumulating in one place. On the NPC class, you'd be able to have a Vendor or ShopKeeper subclass, for example, if the dialogue needs of the tavern-keeper are sharply divergent from the town drunk.

    I think this gives you a little wiggle room to focus on implementing instances of this functionality in the short run, until you have a better idea of whether it can all collapse into a single common pattern.

Consider (just pseudocode/concepts here):

  • Any user input goes through a command router that checks the first word against a temporary command mapping and passes it on to the correct object/function.

  • Individual objects register their intent to handle a given command word, adding it to the temporary command mapping.

  • Give every object you suspect might eventually care a chance to register commands. Make a dedicated method for this setup/init work, and this method a call on each object you think might eventually want to register a command in a well-defined order. This isn't too sophisticated. A decent order is: everything the player is holding, everything in the room with them, the room itself, and a general command handler. Think something like register_command(name, json_command_description).

  • If anything changes, clear this temporary command map and give everyone a new chance to register.

  • When the user actually enters a command, the handler might do something like: registered_object->command_name(user_input, json_command_description) or registered_object->do_command(name, user_input, json_command_description)

This standard process for registering and handling commands will free you up to move any specific command around fairly easily, so you can write the command on the most specific object it makes sense on and move it towards the core (generalizing, parameterizing, and pushing messages/paramters out into JSON) later. It also means the JSON command descriptions can differ circumstantially in the beginning, so that common structural patterns emerge from real use.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your comment. From what you are saying around handling dialogue actions - i should go with the already existing solution like AngelScript (or anything else). So it means, that when i have a dialogue with an option that should do a real engine action (like create an object in players backpack) - this should be handled by the scripting engine, without me coding the interpreter. But how do you actually go from: registered_object->do_command(name, user_input, json_command_description) To an engine specific action ? You would still need to define script varialbes in json right? \$\endgroup\$ – PeeS Sep 12 '18 at 8:06
  • \$\begingroup\$ Not sure I'm following/answering appropriately. It makes sense to store values in JSON that affect how a command runs. Actually embedding any executable code/logic/script in the JSON seems like a bad idea, though. Eventually, whether in C++ or in a script, you'll need code that performs the appropriate engine specific action(s) (even if all of the messages, options, and outputs are dictated by values stored in JSON). Those commands might be in a switch statement, separate functions/methods, or even distinct classes. Does that help? We may be talking past each other :) \$\endgroup\$ – abathur Sep 13 '18 at 5:07
  • \$\begingroup\$ Yup, all clear ;) Thx mate! \$\endgroup\$ – PeeS Sep 14 '18 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.