DialogueScript
by Runite
CREATE FULL DIALOGUES FOR YOUR SERVER IN MINUTES, EVEN SECONDS.
Check this shit out:
- Everything is handled via a text file cleverly called dialogue.txt (you can change this if you want).
- Because it is handled in a text file, you can update your dialogue LIVE - even if your server is still running.
- It's never been easier to create and READ your dialogue - You simply put a symbol or two in front of what you want and everything else is handled for you!.
- Line breaks are created automatically, so you don't need to figure out where to split your sentences.
- Variables such as @name (replaced by the player's name) are INSANELY easy to add, with a couple of examples already there.
- FULLY supports NPC chats, player chats, options (as many in a row as you want), and simple statements (no chat heads).
- Customizable (with a few already added) COMMAND SYSTEM, which allows you to open shops, interfaces, send messages, and anything else you need FROM THE DIALOGUE.TXT FILE.
- SO MUCH MORE!
This was created for a RuneFusion 1.0.5 source, but with a few tiny fixes it'll work for literally anything.
* Please keep in mind this is just the release immediately after all of the functionalities had been added. It still needs some fixing up ;P
THE TUTORIAL
1. Create this file (you will need to change the package and what not to work with your own server):
Code:
/*
* DialogueScript by Runite
* version 1.0
*
* Completely, 100% written from scratch. Yup.
*
* The easiest, quickest, and most efficient way to handle dialogue for your server.
*/
package server.model.players;
import server.util.*;
import java.io.*;
import java.util.*;
public class DialogueScript {
private Client c;
public DialogueScript(Client Client) {
this.c = Client;
}
public String getDialogue(int npc) {
try {
BufferedReader br = new BufferedReader(new FileReader("./data/dialogue.txt"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
boolean record = false;
while (line != "[END DIALOGUES]") {
if(line.startsWith("NPCID:")) {
String[] info = line.split(" ");
if(Integer.parseInt(info[1]) == npc) {
c.currentDialogueNPC = npc;
record = true;
}
}
line = br.readLine();
if(record) {
sb.append(line);
sb.append("\n");
if((line.equals("[end]")) && record) {
break;
}
}
}
return sb.toString();
} finally {
br.close();
}
} catch(Exception e) {
e.printStackTrace();
}
return "";
}
public void endDialogue() {
c.currentDialogueStage = 0;
c.currentDialogueNPC = 0;
c.currentDialogueOption = 0;
c.dialoguePaths = new ArrayList<Integer>();
}
public void initiateNPCDialogue(int npc) {
c.currentDialogueStage = 1;
doNPCDialogue(npc);
}
public void setDialogueOption(int opt) {
if(c.currentDialogueOption <= 0) {
c.currentDialogueOption = opt;
}
}
public boolean doNPCDialogue(int npc) {
String script = getDialogue(npc);
String[] line = script.split("\n");
String dialogue = "", currentLine = "";
String[] formattedDialogue = new String[5];
int lineNumber = 0, optionNumber = 0;
for(int i=0; i<line.length; i++) {
if(line[i].startsWith("> ") || line[i].startsWith("< ") || line[i].startsWith("! ") || line[i].startsWith("? ") || line[i].startsWith("?-- ") || line[i].startsWith("{ ") || line[i].startsWith("(")) {
boolean correctPath = true, countLine = true;
if(line[i].startsWith("(")) {
if(!c.dialoguePaths.isEmpty()) {
String paths = line[i].replaceAll("\\(|\\).*", "");
String[] pathOptions = paths.split(", ");
for(int p=0; p<pathOptions.length; p++) {
if(c.dialoguePaths.get(p).intValue() == Integer.parseInt(pathOptions[p])) {
countLine = false;
if(p+1 == pathOptions.length) {
if(c.dialoguePaths.size() == pathOptions.length) {
countLine = true;
}
line[i] = line[i].substring(line[i].indexOf(")")+2);
break;
}
continue;
} else {
correctPath = false;
break;
}
}
}
} else {
if(!c.dialoguePaths.isEmpty()) {
countLine = false;
}
}
if(line[i].startsWith("?-- ")) {
if(correctPath && countLine) {
optionNumber++;
if(c.currentDialogueOption > 0 && optionNumber == c.currentDialogueOption) {
currentLine = line[i].substring(4);
break;
}
}
} else {
if(correctPath) {
optionNumber = 0;
lineNumber++;
if(lineNumber == c.currentDialogueStage) {
currentLine = line[i];
break;
}
}
}
} else
if(line[i].equals("[end]")) {
endDialogue();
return false;
} else {
continue;
}
}
if(currentLine != null) {
dialogue = currentLine.substring(2);
String[] breaks = dialogue.split(" :: ");
if(c.currentDialogueOption > 0) {
c.dialoguePaths.add(c.currentDialogueOption);
c.currentDialogueOption = 0;
}
if(currentLine.startsWith("? ")) {
formattedDialogue[4] = Integer.toString(breaks.length);
for(int b=0; b<breaks.length; b++) {
if(b < 4) {
formattedDialogue[b] = breaks[b];
}
}
} else
if(currentLine.startsWith("{ ")) {
String[] commands = dialogue.split("; ");
for(int x=0; x<commands.length; x++) {
String cmd = commands[x];
String mod = (cmd.substring(cmd.indexOf("(")+1, cmd.indexOf(")")) != null ? cmd.substring(cmd.indexOf("(")+1, cmd.indexOf(")")) : "");
if(cmd.startsWith("sendMessage")) {
try {
c.sendMessage(mod);
} catch(Exception e) {
e.printStackTrace();
}
}
if(cmd.startsWith("openShop")) {
try {
c.isShopping = true;
c.getShops().openShop(Integer.parseInt(mod));
endDialogue();
} catch(Exception e) {
e.printStackTrace();
}
}
if(cmd.startsWith("showInterface")) {
try {
if(Integer.parseInt(mod) == 14670) {
c.getCrafting().showTanningInterface();
} else {
c.getPA().showInterface(Integer.parseInt(mod));
}
endDialogue();
} catch(Exception e) {
e.printStackTrace();
}
}
if(cmd.startsWith("endDialogue")) {
endDialogue();
formattedDialogue = null;
return false;
}
}
} else {
if(breaks.length < 2) {
String[] words = dialogue.split(" ");
int chars = 0, charBreak = 0, wordCount = 0, prevWordCount = 0, lineCount = 0;
for(int w=0; w<words.length; w++) {
int wl = words[w].length();
if((chars+wordCount < 50 && lineCount < 3) || lineCount == 3) {
chars += wl;
wordCount += 1;
}
if((chars+wordCount >= 50 && lineCount < 3) || (lineCount == 3 && w+1 == words.length) || w+1 == words.length) {
for(int c=0; c<4; c++) {
if(formattedDialogue[c] == null) {
int stopPoint = charBreak+chars+(wordCount-1)+prevWordCount;
if(stopPoint > dialogue.length()) {
stopPoint = dialogue.length();
}
formattedDialogue[c] = dialogue.substring(charBreak+prevWordCount, stopPoint);
prevWordCount += wordCount;
wordCount = 0;
lineCount += 1;
break;
}
}
charBreak += chars;
chars = 0;
if(w+1 == words.length) {
formattedDialogue[4] = Integer.toString(((charBreak < 200 ? charBreak : 199)/50)+1);
break;
}
}
}
} else {
formattedDialogue[4] = Integer.toString(breaks.length);
for(int b=0; b<breaks.length; b++) {
if(b < 4) {
formattedDialogue[b] = breaks[b];
}
}
}
}
if(formattedDialogue != null) {
if(currentLine.startsWith("> ")) {
sendNPCChat(formattedDialogue, npc);
} else
if(currentLine.startsWith("< ")) {
sendPlayerChat(formattedDialogue);
} else
if(currentLine.startsWith("! ")) {
sendLine(formattedDialogue);
} else
if(currentLine.startsWith("? ")) {
sendOption(formattedDialogue);
}
if(optionNumber <= 0) {
c.currentDialogueStage++;
}
return true;
}
}
return false;
}
public Integer tryParse(String s) { // not mine :(
Integer retVal;
try {
retVal = Integer.parseInt(s);
} catch (NumberFormatException nfe) {
retVal = -1;
}
return retVal;
}
public String replaceVars(String s) {
String s1 = s;
s1 = s1.replace("@name", c.playerName);
s1 = s1.replace("@attack", Integer.toString(c.playerAttack)); // example
return s1;
}
public void sendLine(String[] s) {
int f = 0;
int length = Integer.parseInt(s[4]);
switch(length) {
case 1: f = 356; break;
case 2: f = 359; break;
case 3: f = 363; break;
case 4: f = 368; break;
}
for(int i=0; i<length; i++) {
if(i <= 3) {
c.getPA().sendFrame126(replaceVars(s[i]), (f+1)+i);
}
}
c.getPA().sendFrame164(f);
}
public void sendOption(String[] s) {
int f = 0;
int length = Integer.parseInt(s[4]);
switch(length) {
case 2: f = 2459; break;
case 3: f = 2469; break;
case 4: f = 2480; break;
case 5: f = 2492; break;
}
for(int i=0; i<length; i++) {
if(i <= 4) {
c.getPA().sendFrame126(replaceVars(s[i]), (f+2)+i);
}
}
c.getPA().sendFrame164(f);
}
public void sendPlayerChat(String[] s) {
int f = 0;
int length = Integer.parseInt(s[4]);
switch(length) {
case 1: f = 969; break;
case 2: f = 974; break;
case 3: f = 980; break;
case 4: f = 987; break;
}
c.getPA().sendFrame200(f, 591);
c.getPA().sendFrame126(Misc.formatPlayerName(c.playerName), f+1);
for(int i=0; i<length; i++) {
if(i <= 3) {
c.getPA().sendFrame126(replaceVars(s[i]), (f+2)+i);
}
}
c.getPA().sendFrame185(f);
c.getPA().sendFrame164(f-1);
}
public String npcName(int npc) {
switch(npc) {
case 0: return "Hans";
case 1: return "Man";
case 494: return "Banker";
case 550: return "Lowe";
case 554: return "Fancy-dress shop owner";
case 804: return "Tanner";
default: return "n/a";
}
// return yourNPCNameMethodHere(npc); // remove/comment out above if you have a method for getting NPC names
}
public void sendNPCChat(String[] s, int npc) {
String name = npcName(npc);
int f = 0;
int length = Integer.parseInt(s[4]);
switch(length) {
case 1: f = 4888; break;
case 2: f = 4888; break;
case 3: f = 4894; break;
case 4: f = 4901; break;
}
c.getPA().sendFrame200(f, 591);
c.getPA().sendFrame126(name, f+1);
for(int i=0; i<length; i++) {
if(i <= 3) {
if(length > 1) {
c.getPA().sendFrame126(replaceVars(s[i]), (f+2)+i);
} else {
c.getPA().sendFrame126(replaceVars(s[i]), (f+2)+i);
c.getPA().sendFrame126("", (f+2)+i+1);
}
}
}
c.getPA().sendFrame75(npc, f);
c.getPA().sendFrame164(f-1);
}
}
2. Add these in your player class:
Code:
public int currentDialogueStage = 0, currentDialogueNPC = 0, currentDialogueOption = 0;
public ArrayList<Integer> dialoguePaths = new ArrayList<Integer>();
3. Throw this stuff in your client class, or wherever necessary:
Code:
private DialogueScript dialogueScript = new DialogueScript(this);
public DialogueScript dialogueScript() {
return dialogueScript;
}
4. This is for when you click an NPC to open the dialogue:
Code:
c.dialogueScript().initiateNPCDialogue(NPC_ID);
5. This is for when you click one of the lines in the option interface (option number 1, 2, 3, 4 respectively):
Code:
c.getDialogue2().setDialogueOption(OPTION_NUMBER);
6. And HERE'S an example of dialogue.txt:
Code:
// Sample dialogue.txt, pointless and stupid, just to show off how to use it and its functionality ;D <3 Runite
NPCID: 550 (Lowe)
< Hey Lowe.
> Fuck off, @name.
< Why?
> You tell me.
? Because you're mean. :: I smell bad. :: You're trying to get it in. :: May I see your wares?
?-- < Because you're mean?
?-- < Well, I mean I try to shower regularly, but is it because I smell bad?
?-- < Silly Lowe, you're trying to get it in, aren't you?
?-- < Can I just see your shop?
(1) > Yup.
(1) < Why are you mean?
(1) > You tell me.
(1) ? I don't know. :: No.
(1) ?-- < I have no idea, Lowe.
(1) ?-- < Nope, I don't want to play this stupid game with you anymore.
(1, 1) > You suck.
(1, 1) < Stupid Lowe life. Hehe...
(1, 1) ? Lol :: Lol2
(1, 1) ?-- < LOL
(1, 1) ?-- < LOL2
(1, 1, 1) > ...
(1, 1, 1) { openShop(9); }
(1, 1, 2) > ... ...
(1, 2) > Fine.
(1, 2) < Fine.
(2) > No.
(2) < Okay, good!
(2) ? Yo? :: Swag
(2) ?-- < YO LOWE!
(2) ?-- < Swag 420 blaze it.
(2, 1) > LOL!!!!! Or, LOWEL. HAH!
(2, 2) > No, fag.
(3) > Shh! No. Well, yes.
(3) ! Lowe looks embarassed and turns away.
(4) > Ugh, fine.
(4) { openShop(9); sendMessage(Lowe reluctantly shows you his wares.); }
< Does this work still?
> This shit :: manually breaks :: yo.
[end]
NPCID: 804 (Tanner)
> Hello there! :: How may I assist you?
? Can you tan my hides? :: I'd like to see your wares.
?-- < Can you tan my hides for me?
?-- < I'd like to see your wares.
(1) > Certainly! :: However, I do charge a small fee.
(1) { showInterface(14670); }
(2) { openShop(3); }
[end]
[END DIALOGUES]
HOW TO USE IT
Here's a super duper quick crash course on how to create your own dialogue.txt the right way, and also a few other little tweaks you can make.
'NPCID: ID' - This begins all dialogue, and determines the NPC you are speaking to.
'[end]' - Placed at the very end of each dialogue, signifying that the dialogue is finished.
'[END DIALOGUES]' - Placed at the very end of the
dialogue.txt to say that there is no more dialogue beyond that point.
'<' - Putting this before your text will make the NPC speak.
'>' - Putting this before your text will make your player speak.
'?' - Putting this before your text will signify an option needs to be selected.
'!' - Putting this before your text will show lines of text without chat heads.
'{' - Putting this before your text will mark the line as being used for commands, such as
openShop(SHOP_ID).
'::' - Putting this between text will either: 1) Break up options after the '?' or 2) Manually break dialogue, which will disregard the auto-breaking completely for that line.
'?--' - Putting this before your text will signify the response for an option. The option number it responds to is respective to the order of options after the '?' and between the '::'s. (NOTE: You will still need to use the '<', '>', '?', or '!' symbols after the '?--')
';' - This is to be placed directly after commands (on a line started with '{') to separate them, i.e.
{ openShop(1); sendMessage(Opened shop 1); }.
'(#)', '(#, #)', etc. - Putting this at the beginning of a line (where # are the corresponding option numbers, in order) will determine which option number the line goes with.
Once you're comfortable creating the actual dialogues, you can tweak the DialogueScript class in pretty much any way you like. Changing most parts will screw it up though, but feel free to look at these parts:
- Search if(currentLine.startsWith("{ ")) { - Look right past here and you'll find where your commands are.
- Search public String replaceVars(String s) - This method contains the variables that you can use in the dialogue.
- Search public String npcName(int npc) - This method contains the NPC names that correspond to NPC IDs. Chances are you have a method to handle this elsewhere, so edit however you need to.
There it is! DialogueScript, brought to you 100% by yours truly. If you have any comments, concerns, or questions, PLEASE feel free to post. I'll try my best to answer anything.
I hope you guys enjoy it and find some use out of it! I'm hoping this is a big release for everyone.
Credits: Me and some guy from Stack Overflow who made the tryParse method (which isn't much LOL).