Hey guys. I wanted to share some progress of mine. I made my own language so that I could easily write NPCs in my game. I wanted to share it in hopes that I can give others ideas for how to do something similar, as it is truly pretty helpful.
This is a tutorial on how to do it. The download link for a practice engine is included, as well as multiple example files
Overview:
Quote
The point of Ocode is to make NPCs know what to say and when to say it. Ocode also allows NPCs to check for quest status, give quests, and complete quests.The system works on a progression of what I call tags.
Continue reading now...
Tags:
QuoteDisplay More
A tag is like a go-to, or a reference point. Each tag is followed by an open bracket '{' and then some lines of code, and eventually a closing bracket '}'. There are almost always multiple tags in an NPC.This is what a snippet of a tag looks like:
Let's analyze the elements:
AAA = Tag Name. There are a few different things you can put here, we will go over that later. It tells the NPC what it's generally going to be doing (conversation? giving a quest? etc)
BBB = Tag Number. This is like a subgroup of the tag. Think of this as like a page number in a book.
XXX = Command. Now we've gotten to the inside of the system where commands exist. Different commands go here that tell the NPC what to say, give the player his/her options of dialogue, check quests, give quests, and complete quests.
yy & zz = Arguments. When you specify a command, you must supply some arguments. These simply tell the command what to do but more specifically.Side note: Every NPC will always look for the #PreCheck_0 tag first.
Progression of the NPC:
Quote
Basically the way this system works, is that the NPC will go to a certain tag, and display what is within that tag, and then will look for another tag when the time comes. Just remember, the NPC will always look for the #PreCheck_0 tag first. Now let's get into some examples.
Example NPC:
QuoteDisplay More
Entire Code:Code Display More#PreCheck_0 { checkQuest(33) ifNotStarted(#Convo,0) ifInProgress(#Convo,100) ifComplete(#PreCheck,1000) } #Convo_0 { NPC_Say(Hello player. I will test how friendly you are... How are you today?) Player_A_Say(I'm good how are you?) Player_A_Send(#Convo,1) Player_B_Say(Don't talk to me) Player_B_Send(#Convo,5) } //If player picks option A, this window is displayed #Convo_1 { NPC_Say(You are friendly.) Player_A_Say(Yes I am nice.) Player_A_Send(#PromptQuest,33) } //Otherwise if player picks option B, this window is displayed #Convo_5 { NPC_Say(You are not friendly!) Player_A_Say(Yes I am evil.) Player_A_Send(npc.end) } //In a PromptQuest dialogue, the Player_A_Send is now the accept button, and the B is the deny. #PromptQuest_33 { NPC_Say(Would you like to help me with this quest?) Player_A_Say(Yes!) Player_A_Send(#Convo,10,#Convo,11) Player_B_Say(No...) Player_B_Send(#Convo,15) } //Player accepts and has fulfilled requirements #Convo_10 { NPC_Say(You accepted my quest.) Player_A_Say(Yes I did accept it.) Player_A_Send(npc.end) } //Player accepts and has not fulfilled requirements #Convo_11 { NPC_Say(You cannot accept this because you are not strong enough.) Player_A_Say(I will come back when the time is right.) Player_A_Send(npc.end) } //Player Denies #Convo_15 { NPC_Say(You did not accept my quest.) Player_A_Say(No I did not accept it.) Player_A_Send(npc.end) } //In Progress #Convo_100 { NPC_Say(Hope you haven't gotten into too much trouble since we last spoke.) Player_A_Say(I am doing well.) Player_A_Send(#CompleteQuest,33) Player_B_Say(Goodbye.) Player_B_Send(npc.end) } //In a PromptQuest dialogue, the Player_A_Send is now the accept button, and the B is the deny. #CompleteQuest_33 { NPC_Say(Would you like to complete the quest?) Player_A_Say(Yes!) Player_A_Send(#Convo,110,#Convo,115) Player_B_Say(No...) Player_B_Send(#Convo,115) } //Player Denies #Convo_115 { NPC_Say(You did NOT finish this quest.) Player_A_Say(Okay I will finish then come back.) Player_A_Send(npc.end) } //Player Denies #Convo_110 { NPC_Say(You did finish this quest) Player_A_Say(I'll take my rewards and leave now.) Player_A_Send(npc.end) }
So let's analyze this bit by bit. As always the first tag is #PreCheck_0. Now I mentioned before that each tag name does something different, and the precheck tag's purpose is to check some requirements before figuring out what to say to the player. For example, if a player hasn't started a quest, we want the NPC to start the conversation that leads up to the quest. If the player is in progress with a quest, we want the NPC to start the conversation that leads up to the checking of the quest. And finally if the player has completed the quest, we want the NPC to know that and respond accordingly, and maybe even offer another quest.
So the first line within the tag reads checkQuest(33). Complicated right? Not really. This literally sends a request to the server and the server will check whatever quest is in parentheses (in this case 33) and respond with one of 3 things.... (1) the user hasn't started this quest, (2) the user is in progress, and (3) the user has completed the quest.
Now take a look at the next 3 lines. Each one corresponds to one of these 3 responses....
Within each of these 3 commands, there is another tag and tag number. So let's pretend the user HAS NOT STARTED quest #33. The NPC will do whatever is inside the ifNotStarted(#Convo,0) command, and ignore the other commands. Specifically, it tells the NPC to go to tag '#Convo' on number '0'.
side note: if you do not wish to check a quest at the beginning, you can specify -1 in the checkQuest command and the NPC will then look for #Convo_0
Now if we scroll down a bit we will find a tag with those exact parameters...#Convo_0 {
The purpose of the Convo tag is to (1) display NPC dialogue, (2) player dialogue, and like all tags, (3) to control where the dialogue goes next.
Let's look within this tag to learn about the new commands we will find.
CodeNPC_Say(Hello player. I will test how friendly you are... How are you today?) Player_A_Say(I'm good how are you?) Player_A_Send(#Convo,1) Player_B_Say(Don't talk to me) Player_B_Send(#Convo,5)
The first command is NPC_Say and the argument--which is the term for the contents inside the parentheses--is a line of text that the NPC will say. It's that simple.
The next line has the command Player_A_Say. This is like the above, in that it is a line of text but this time it is what the player will say. But the player can have two options of what to say, so that's what the "A" is for.
Next we have the command Player_A_Send. The arguments are (#Convo,1), which is exactly like the parameters for the ifNotStarted command we played with before. So if the player clicks option A when in game, then the NPC will "SEND" the player to whatever tag is within option A.
Option B is the following two lines and do the exact same thing except it allows the conversation to go in multiple directions.So let's pretend the player picks option A. We will follow the tag to, as it states, #Convo, and 1.
Looking at the NPC code, this is the next tag.
The entire thing reads:
Code#Convo_1 { NPC_Say(You are friendly.) Player_A_Say(Yes I am nice.) Player_A_Send(#PromptQuest,33) }
Most of this is a repeat of what we already learned. The player is only given one option here, just for the purpose of showing that it is possible to give the player only one choice.
But this time instead of going to another Convo tag it will go to a PromptQuest tag. Let's learn about that.
Code#PromptQuest_33 { NPC_Say(Would you like to help me with this quest?) Player_A_Say(Yes!) Player_A_Send(#Convo,10,#Convo,11) Player_B_Say(No...) Player_B_Send(#Convo,15) }
For the most part, you can make the tag numbers whatever you want. But in a PromptQuest tag, you MUST make the tag number the number of the quest that you want to prompt. Additionally, a PromptQuest will make player dialogue buttons A and B turn green and red respectively (normally they are both gray) resembling an accept/decline for a quest. That's the purpose of a PromptQuest, to give the player the ability to accept a quest.
You'll notice that choice B looks the same.... this is what the NPC will follow when the player clicks choice B or decline.
Which takes the player to this tag:
Code#Convo_15 { NPC_Say(You did not accept my quest.) Player_A_Say(No I did not accept it.) Player_A_Send(npc.end) }
And the only new thing here is the npc.end part of choice A, which obviously terminates the NPC conversation.
But let's go back to our example of the PromptQuest_33 one more time. What happens if the player picks choice A?
You'll notice that choice A (the send command) has two arguments (#Convo,10,#Convo,11), when we've only seen one in the past.
The reason for this is because the player may choose to accept a quest, but may not be able to actually take the quest because of level restrictions, or unfulfilled pre-reqs. So the first argument #Convo,10 will take control IF THE PLAYER ACCEPTS THE QUEST AND IS ABLE TO DO SO.
The second argument #Convo,11 will take control IF THE PLAYER ACCEPTS THE QUEST YET IS NOT ABLE TO DO SO.
The rest of the progression should be self explanatory at this point.
But really quick, let's go over what would happen if the player went through the progression that we just did, and accepted the quest. The server would now store the information pertaining to this acceptance.
So now let's pretend the player clicks the same NPC once more.
This time, the NPC would still go to the #PreCheck_0 { tag, as it did before. But now since the player has accepted quest 33 and is in progress with it, this is the line that would be interpreted: ifInProgress(#Convo,100). So now let's look at #Convo_100...
Code#Convo_100 { NPC_Say(Hope you haven't gotten into too much trouble since we last spoke.) Player_A_Say(I am doing well.) Player_A_Send(#CompleteQuest,33) Player_B_Say(Goodbye.) Player_B_Send(npc.end) }
Most of these we have seen before. Option B is an end command, and option A sends the player to a tag. But we haven't dealt with a #CompleteQuest tag yet.
Here's the CompleteQuest Tag
Code#CompleteQuest_33 { NPC_Say(Would you like to complete the quest?) Player_A_Say(Yes!) Player_A_Send(#Convo,110,#Convo,115) Player_B_Say(No...) Player_B_Send(#Convo,115) }
This acts exactly like the PromptQuest tag. The tag number MUST be the ID of the quest to be completed. And likewise, the two options, A and B, will turn green and red respectively for completing the quest. Also, you'll notice that option A (for the send) has two arguments (#Convo,110,#Convo,115). Like the way PromptQuest works, CompleteQuest needs to account for the possibility that a player may want to complete a quest, but does not meet the requirements (i.e. missing an item for completion). So the first set of arguments (#Convo,110) will activate when the player CAN complete the quest, and the second set of arguments (#Convo,115) activates when the player wants to complete the quest but IS NOT able to.
The rest has already been covered. Happy NPC coding
Cheat Sheet:
QuoteDisplay More
---Tags---
#PreCheck = Purely for guiding the first portion of the NPC and its decision of where to go initially. Here you can check quests and their status. This tag is driven by the checkQuest command. If you specify a quest within the command (anything greater than -1) it will perform a quest check. If you specify a -1, the NPC will go directly to #Convo_0 tag.#Convo = Basic conversation.
#PromptQuest = For prompting a quest. The player buttons change from gray to green and red for accept and decline.
#CompleteQuest = For completing a quest.
---Commands---
checkQuest(x) = for checking a quest. Only used during PreCheck tag, where x is the quest ID.
ifNotStarted(x,y) = after a quest check is performed, if the quest has not been started, this will be executed. x is the tag name and y is the tag number.Can only be used within a PreCheck tag.
ifInProgress(x,y) = after a quest check is performed, if the quest has been started but is not complete, this will be executed. x is the tag name and y is the tag number.Can only be used within a PreCheck tag.
ifComplete(x,y) = after a quest check is performed, if the quest has been started and is complete, this will be executed. x is the tag name and y is the tag number. Can only be used within a PreCheck tag.NPC_Say(x) = Defines what the NPC says within a Convo tag. x is the NPC's dialogue.
Player_A_Say(x) = Defines what the player says within a Convo tag (option 1). x is the player's dialogue.
Player_A_Send(x,y) = Defines where the NPC will progress to when the player picks option A. x is the tag name, y is the tag number.
Player_B_Say(x) = Defines what the player says within a Convo tag (option 2). x is the player's dialogue.
Player_B_Send(x,y) = Defines where the NPC will progress to when the player picks option B. x is the tag name, y is the tag number.---Arguments---
Arguments can either be tags or tag numbers, an the only other exception is npc.end, which can be added in place of any tag name. npc.end will terminate the NPC process.---Comments---
Any line that begins with two forward slashes (//) will be completely ignored by the interpreter.
ex: //This line makes no impact even if it has a tag like #Convo_0---Variables---
%PLAYER% = using this anywhere in the code tells the NPC to display the current user's name in place of this.
DOWNLOAD:
Please login to see this link.
Examples:
Lion's Wizard example
//Author: Lion
//Date: 6/28/2015
#PreCheck_0 {
checkQuest(33)
ifNotStarted(#Convo,0)
ifInProgress(#Convo,100)
ifComplete(#PreCheck,1000)
}
//This displays when quest not started
#Convo_0 {
NPC_Say(Welcome to Onyx Legion, %PLAYER%!)
Player_A_Say(Who are you? Where am I? Wha.. *Who.. I.. %PLAYER%)
Player_A_Send(#Convo,1)
Player_B_Say(Nevermind, you smell..)
Player_B_Send(#Convo,5)
}
//This displays with option A
#Convo_1 {
NPC_Say(Enough babbling! I am the Great Wizard A wizard who is.. er.. great! You are here in this very spot, standing, talking to a strange wizard who smells like he recently defecated within his trousers.)
Player_A_Say(My name is Lion)
Player_A_Send(#PromptQuest,33)
Player_B_Say(You what now?)
Player_B_Send(#PromptQuest,34)
}
//This displays with option B
#Convo_5 {
NPC_Say(Alright, have a nice day, aventurer!)
Player_A_Say(You too.)
Player_A_Send(npc.end)
Player_B_Say(Go away, old man.)
Player_B_Send(npc.end)
}
//In a PromptQuest dialogue, the Player_A_Send is now the accept button, and the B is the deny.
#PromptQuest_33 {
NPC_Say(Welcome, Lion! Would you care to help an old man out? I need my meds! Collect five wild flowers and I'll reward you with something special.)
Player_A_Say(Sure, I'll help you out, grandpa!)
Player_A_Send(#Convo,10,#Convo,11)
Player_B_Say(No thanks, stinky.)
Player_B_Send(#Convo,15)
}
//In a PromptQuest dialogue, the Player_A_Send is now the accept button, and the B is the deny.
#PromptQuest_34 {
NPC_Say(Nevermind that. I heard someone around here thinks he's a wizard.. What a psycho! If you find me five wild flowers, I'll reward you with something special.)
Player_A_Say(You're the.. Wait, rewards?! I mean, sure, I'll help!)
Player_A_Send(#Convo,10,#Convo,11)
Player_B_Say(Nah, I have better things to do, stinky.)
Player_B_Send(#Convo,15)
}
//Player accepts and has fulfilled requirements
#Convo_10 {
NPC_Say(Thanks, bring me five wild flowers for your reward.)
Player_A_Say(Right away, gramps!)
Player_A_Send(npc.end)
Player_B_Say(Don't rush me.)
Player_B_Send(npc.end)
}
//Player accepts and has not fulfilled requirements
#Convo_11 {
NPC_Say(You're too weak to help me out, come back later.)
Player_A_Say(I will come back, thanks!)
Player_A_Send(npc.end)
Player_B_Say(Too weak to pick flowers? I'm not coming back, stinky!)
Player_B_Send(npc.end)
}
//Player Denies
#Convo_15 {
NPC_Say(Your loss!)
Player_A_Say(Sorry, other stuff to do!)
Player_A_Send(npc.end)
Player_B_Say(Yah, cause I really wanted your smelly treasure..)
Player_B_Send(npc.end)
}
//In Progress
#Convo_100 {
NPC_Say(How's it going?)
Player_A_Say(Good, I have your flowers!)
Player_A_Send(#CompleteQuest,33)
Player_B_Say(Not done yet, wizz off!)
Player_B_Send(npc.end)
}
//In a PromptQuest dialogue, the Player_A_Send is now the accept button, and the B is the deny.
#CompleteQuest_33 {
NPC_Say(You have all my flowers?!)
Player_A_Say(Yep, here they are!)
Player_A_Send(#Convo,110,#Convo,115)
Player_B_Say(No, stop being so needy.)
Player_B_Send(npc.end)
}
//Player Denies
#Convo_115 {
NPC_Say(You're not done yet, I said I need five!)
Player_A_Say(Alright, coming right up!)
Player_A_Send(npc.end)
Player_B_Say(Ugh, I'll be back .. ungrateful old man!)
Player_B_Send(npc.end)
}
//Player Denies
#Convo_110 {
NPC_Say(Wow, thanks so much! As promised... here's your reward.)
Player_A_Say(Thanks, I can't wait to open it!)
Player_A_Send(npc.end)
Player_B_Say(Thanks for nothing, smelly.)
Player_B_Send(npc.end)
}
Display More
An NPC without quests
//Author: Hydracol
//Date: 6/28/2015
//You can use a -1 to skip the quest check. This is useful for simple NPCs, as you will see.
//Side note, technically without a PreCheck for a quest, the NPC can never give a quest or else the user will never be able to reach the dialogue.
#PreCheck_0 {
checkQuest(-1)
}
//When -1 is supplied for checkQuest, the NPC looks directly for #Convo_0
#Convo_0 {
NPC_Say(Hi %PLAYER% I'm a one dimensional NPC.)
Player_A_Say(So you'll never give me a quest or anything?)
Player_A_Send(#Convo,1)
Player_B_Say(I'm one dimensional as well....)
Player_B_Send(#Convo,5)
}
//This displays with option A
#Convo_1 {
NPC_Say(No I won't!!!)
Player_A_Say(Alright then I'll see ya later.)
Player_A_Send(npc.end)
Player_B_Say(Fine then.)
Player_B_Send(npc.end)
}
//This displays with option B
#Convo_5 {
NPC_Say(Alright, have a nice day, adventurer!)
Player_A_Say(You too.)
Player_A_Send(npc.end)
Player_B_Say(Go away, old man.)
Player_B_Send(npc.end)
}
Display More