GML RPG dialogue question. Advice appreciated! I want to learn more.

M

Matthew Ottewell

Guest
Hi everyone! Here are my two problems, and help on either would be great.

1. The immediate problem. I would like to impact my game with certain dialogue choices. If you choose a rude dialogue path, your relationship with an NPC breaks down, and possible dialogue options filled with a whole host of good stuff disappears.

2. The more long term problem. My object creating the entire dialogue array is loading all the modifiers upon creation. This is obviously not ideal. Using an if statement with a certain statement starts a continuous loop of increased Charisma scores. I would like advice on any resource that is good at explaining GameMaker structure (create events, step events) or a well written book/resource for a programming language that is very similar to Gamemaker (and I have no clue what that language would be) For example:

global.InnkeeperYdrin[8,2]="Thank you for your help!";
global.InnkeeperYdrin[8,3]="Thank you Ydrin. I will help protect the town."; global.Charisma +=1;
global.InnkeeperYdrin[8,4]="I'll do what I can. Goodbye.";

This simply takes whatever you roll for Charisma at the start of the game and adds 1 to it before you access the array. I think I understand: this is in the create event with a global variable. Upon creation it sees this and does what you tell it to. I need to create something separate. So I tried this.

global.InnkeeperYdrinConversationGroup = 1;

This is in the same object. It keeps track of what block of conversations you are on. This prevents the immediate application of the modifier.

But if I put all of this into a separate object with the following if statement

if (global.InnkeeperYdrinConversationGroup = 8)
{ global.Charisma +=1;}

It continuously accesses the state of being on that conversation group until I switch off of it, adding I am assuming 60 charisma per second, based on the room speed.

I have tried things like the following below, but it just accesses the creation event and adds the Charisma, again, off the top. I copied the snippet below, Flag was initialized as true in the create event. This is step code.

if (global.InnkeeperYdrinConversationGroup =8) {
if (flag) {
global.Charisma +=1;
flag = false;
}
}
else {
flag = true;
}

Guidance? Advice? Thanks!
 
D

dannyjenn

Guest
1.) I'd start by drawing out a flow chart (on paper). The actual implementation could be as simple as using some if statements, but there are other ways you could do it as well. You'll probably at least need to give each NPC a friendship variable (or give the player a reputation variable, or both).

2.) Have you checked the help file? I believe it includes a good explanation of the different events and stuff, and the order in which they are performed.
The creation event is called one time, as soon as the instance is created.
The step event is called once every frame (so, about 60 times each second on the default settings).
For your particular question, I'm not entirely sure what you're trying to do but it seems like you want it to add 1 to global.Charisma every time the textbox reads "Thank you Ydrin. I will help protect the town."? I think you're on the right track with the flag idea, but I would not set flag to true in the else statement, since all that's going to do is keep toggling the flag between true and false each step (probably resulting in ~30 charisma being added each second). Rather, I'd set it to false only when the text changes to something else. But how you go about doing this depends on how your textbox system is set up.
 
2. Since we both know that creating the Charisma code via creation code isn't ideal, but have you thought of storing the information in an "ini_file" and having the game reference that instead of a myriad of "global.variables?" That way you don't have to worry about typing overly long variable names, and you'll be able to keep track of your variables while playing the game, and outside of the game. For example
Code:
/// @function write_data( )
/// @param filename
/// @param type
/// @param key
/// @param value
/*
    Writes a value to an ini_file to be used later...
*/

ini_open( argument[ 0 ] );

// Decide if this is a string or real value...
if ( argument[ 1 ] == "string" ) {
    ini_write_string( "Data", argument[ 2 ], argument[ 3 ] );
} else {
    ini_write_real( "Data", argument[ 2 ], argument[ 3 ] );
}

// This closes the file so we can open up more files
ini_close( );


// -------------------------------------------------------------------------------------

/// @function read_data( )
/// @param filename
/// @param type
/// @param key
/*
    Reads a value to an ini_file to be used later...
*/

var value;
ini_open( argument[ 0 ] );

// Decide if this is a string or real value...
if ( argument[ 1 ] == "string" ) {
    value = ini_write_string( "Data", argument[ 2 ], "Nothing" );
} else {
    value = ini_write_real( "Data", argument[ 2 ], 0 );
}

// This closes the file so we can open up more files...
ini_close( );

// Finally, Return the value so we can use it later
return value;
Note: Data can technically be anything, but I'm keeping it as "Data" just for that reason.

Now to use these scripts, you would need to use
Code:
ydrin_charisma = read_data( "ydrin_slot_0", "real", "Charisma" );

// Let's just say you said something nice...
var val = ydrin_charisma + 1;

write_data( "Ydrin_slot_0", "real", "Charisma",  val  );
This way, you could keep everything organized. . . I would still do what dannyjenn said and definitely keep a flowchart of some sort to stay organized.
Another thing is that you would have to be sure to use "read_data" for every time you would need get data from an object, and don't forget to use "write_data" to update it afterwards.
 
M

Matthew Ottewell

Guest
1.) I'd start by drawing out a flow chart (on paper). The actual implementation could be as simple as using some if statements, but there are other ways you could do it as well. You'll probably at least need to give each NPC a friendship variable (or give the player a reputation variable, or both).

2.) Have you checked the help file? I believe it includes a good explanation of the different events and stuff, and the order in which they are performed.
The creation event is called one time, as soon as the instance is created.
The step event is called once every frame (so, about 60 times each second on the default settings).
For your particular question, I'm not entirely sure what you're trying to do but it seems like you want it to add 1 to global.Charisma every time the textbox reads "Thank you Ydrin. I will help protect the town."? I think you're on the right track with the flag idea, but I would not set flag to true in the else statement, since all that's going to do is keep toggling the flag between true and false each step (probably resulting in ~30 charisma being added each second). Rather, I'd set it to false only when the text changes to something else. But how you go about doing this depends on how your textbox system is set up.
I have the entire conversation tree mapped out. I have reputation variables for each NPC mapped out as well.

I use the game docs 24/7. Nothing in the documentation explained arrays in a manner that I understand.

For example: global.InnkeeperYdrin[8,2]="Thank you for your help!";

From what I've learned, I've initialized a 2d array with a string value. It's holding a variable, namely a conversation option, and I can also initialize it to increase a game modifier (like Charisma). When it is created it will modify the attribute automatically, which is obviously what I don't want to do.

What I want to do is determine whether or not that particular array has been accessed. How I did that was with the following:

***
if (point_in_rectangle(window_mouse_get_x(),window_mouse_get_y(),460,800,652,864) && mouse_check_button_pressed(mb_left && global.Conversation > 0))
{
switch global.Conversation
{
case 1: global.MayorConversationGroup=global.Mayor[global.MayorConversationGroup,4];instance_destroy(osprite_Textbox);break;
case 2: global.RangerConversationGroup=global.Ranger[global.RangerConversationGroup,4]instance_destroy(osprite_Textbox);break;
case 3: global.InnkeeperYdrinConversationGroup=global.InnkeeperYdrin[global.InnkeeperYdrinConversationGroup,5]instance_destroy(osprite_Textbox);break;
}
}
***

Essentially the conversation group variable tells me where I am in the conversation. When I move to block 8 of the 2d array, as so:

global.InnkeeperYdrin[8,1]="Talk to Ayrokin first. He is usually busy at the Ranger's outpost at the southwest corner of town. The other Ranger Csasi is probably with the Mayor tackling the problem.";
global.InnkeeperYdrin[8,2]="Thank you for your help!";
global.InnkeeperYdrin[8,3]="Thank you Ydrin. I will help protect the town.";
global.InnkeeperYdrin[8,4]="I'll do what I can. Goodbye.";
global.InnkeeperYdrin[8,5]="11";
global.InnkeeperYdrin[8,6]="11";
global.InnkeeperYdrin[8,7]="11";

I have global.InnkeeperYdrinConversationGroup updated to the value 8. Now that I have that value, I want Charisma to increment one point. The problem is this: once the variable is updated to 8, it keeps on being updated to 8, triggering the increments (whether it is based on room speed) and making it not work.

So to clarify, my simple question is "how do i fix this particular problem?" and my bigger question is "how can i learn how functions interact with the various components of GameMaker in a more global way?" I feel like I'm just learning piecemeal and I don't understand the structure at all.
 
M

Matthew Ottewell

Guest
2. Since we both know that creating the Charisma code via creation code isn't ideal, but have you thought of storing the information in an "ini_file" and having the game reference that instead of a myriad of "global.variables?" That way you don't have to worry about typing overly long variable names, and you'll be able to keep track of your variables while playing the game, and outside of the game. For example
Code:
/// @function write_data( )
/// @param filename
/// @param type
/// @param key
/// @param value
/*
    Writes a value to an ini_file to be used later...
*/

ini_open( argument[ 0 ] );

// Decide if this is a string or real value...
if ( argument[ 1 ] == "string" ) {
    ini_write_string( "Data", argument[ 2 ], argument[ 3 ] );
} else {
    ini_write_real( "Data", argument[ 2 ], argument[ 3 ] );
}

// This closes the file so we can open up more files
ini_close( );


// -------------------------------------------------------------------------------------

/// @function read_data( )
/// @param filename
/// @param type
/// @param key
/*
    Reads a value to an ini_file to be used later...
*/

var value;
ini_open( argument[ 0 ] );

// Decide if this is a string or real value...
if ( argument[ 1 ] == "string" ) {
    value = ini_write_string( "Data", argument[ 2 ], "Nothing" );
} else {
    value = ini_write_real( "Data", argument[ 2 ], 0 );
}

// This closes the file so we can open up more files...
ini_close( );

// Finally, Return the value so we can use it later
return value;
Note: Data can technically be anything, but I'm keeping it as "Data" just for that reason.

Now to use these scripts, you would need to use
Code:
ydrin_charisma = read_data( "ydrin_slot_0", "real", "Charisma" );

// Let's just say you said something nice...
var val = ydrin_charisma + 1;

write_data( "Ydrin_slot_0", "real", "Charisma",  val  );
This way, you could keep everything organized. . . I would still do what dannyjenn said and definitely keep a flowchart of some sort to stay organized.
Another thing is that you would have to be sure to use "read_data" for every time you would need get data from an object, and don't forget to use "write_data" to update it afterwards.

Oh wow. Thank you for taking the time to respond. I've never done anything like you suggested before. I would definitely like to learn more about that and try things like that out. It feels quite advanced. But for sake of argument, in your last part where you wrote: "//Let's just say you said something nice..." Rather than that being comment, how would you determine that part of the array had been accessed? I'm willing to give that a shot in a separate project just to see if I can get the basics of it.
 
I'm really sorry for the late respond, had to step out. But let's say we have 2 possible options right . . .
NOTE: for this example I'm going to use strings since I don't know how your dialouge works. . .

Code:
/// @function demo_script( )
/// Demo program ... Call this script in Create event...


// NOTE: Please put this on an object's create event in
// an empty project for testing... This is just a demo.
// Filename & Key settings
var fname = "Jenny_Charisma.ini", key = "charisma";


// This set's it to zero, just for testing...
write_data( fname, "real", key, 0 );

// Lol, why did i use "Jenny?"
show_message( "Hey, I\'m Jenny!" );

show_message( "They\'re been monsters lurking from the cave!" );

var str = get_string( "Will you help us?", "1 for YES, 0 for NO" );

// This is for the while loop, do NOT use this, this is just a test...

    
// We've got a recognizable input, let's us it!
if ( str == "1" ) {
    // Charisma go up!
        
    var val = read_data( fname, "real", key );
    
    write_data( fname, "real", key, val + 1 );
    
} else if ( str == "0" ) {
        
    var val = read_data( fname, "real", key );
        
    write_data( fname, "real", key, val - 1 );
}
    


// Grab the charisma
var charisma = read_data( fname, "real", key );

// Test the charisma...
if ( charisma > 0 ) {
    show_message( "Thank you so much, you\'re the best!" );
    
    show_message( "Charisma: " + string( charisma ) );
} else {
    show_message( "Oh, I\'m sorry for bothering you, what was I thinking?" );
    
    show_message( "Charisma: " + string( charisma ) );
}
Another thing I forgot to mention is that I didn't do the "read_data" script correctly. Here's the right code...
Code:
/// @function read_data( )
/// @param filename
/// @param type
/// @param key
/*
    Reads a value to an ini_file to be used later...
*/

var value;
ini_open( argument[ 0 ] );

// Decide if this is a string or real value...
if ( argument[ 1 ] == "string" ) {
    value = ini_read_string( "Data", argument[ 2 ], "Nothing" );
} else {
    value = ini_read_real( "Data", argument[ 2 ], 0 );
}

// This closes the file so we can open up more files...
ini_close( );

// Finally, Return the value so we can use it later
return value;
 
Top