How to properly get thrown out of a room and comment on it

Started by Cerno, Tue 12/03/2013 23:30:25

Previous topic - Next topic

Cerno

Hello.

I hope this has not been asked a hundred times, my search did not find anything on the forums.

So I have the following situation I want to script, but I have not found a way that seems to be in good style:

We start in a "starting room"
When the player enters a certain other room, he is intercepted by another character, who says "No trespassing, get out!"
The player automatically leaves the room and comments "What a jerk!".
When the player enters the room again, the other character updates his comment to "I said, no trespassing!"
The player automatically leaves the room and comments "He seems to mean it."

Here is what I considered:

Use two global variables "jerk_talk_count" and "comment_on_jerk".
They are initialized as such:

Code: AGS
int jerk_talk_count = 0; //control how many times the jerk has talked
bool comment_on_jerk = false; //only comment on the jerk when we got thrown out of his room, not when entering the starting room by ourselves


Some mock-up code for the jerk room
Code: AGS
room_AfterFadeIn()
{
    if (jerk_talk_count == 0)
    {
        cJerk.Say("No trespassing, get out!");
        jerk_talk_count += 1;
    }
    else
    {
        cJerk.Say("I said, no trespassing.");
    }
    player.changeRoom(1);
    comment_on_jerk = true;
}


Some code for the starting room
Code: AGS
room_AfterFadeIn()
{
    if (comment_on_jerk == true)
    {
        if (jerk_talk_count <= 1)
        {
            player.Say("What a jerk!");
        }
        else
        {
            player.Say("He seems to mean it.");
        }
        comment_on_jerk = false;
    }
}


While I am pretty sure this does what I want I am unsure whether it is good style, so I would ask if there are ways to do it better.

What I don't like so much about this solution is that in a large game I would have to define a lot of these global variables for all kinds of situations that span multiple rooms (either using the GUI or exporting them from GlobalScript.asc), which would make things difficult to overview. Are there some tried-and-true ways to organize these variables? I thought I might be able to export the variables from a room.asc which would help with organization, but I wasn't able to get it to work.

Other ideas I had to encapsulate the character state information:

- Use a custom character property, but unfortunately they are read-only. Something like that would have been pretty neat
- Use character inventory items to simulate the variables (but I guess that would be pretty dumb).

Any help is appreciated.
Thanks.
123  Currently working on: Sibun - Shadow of the Septemplicon

Khris

What you line out is exactly the way these things are done. There are ways to organize your variables; a really tidy solution is using a separate script for all global variables and structs to group them. Unfortunately, AGS doesn't support static variables, so we have to instantiate the struct:
Code: ags
// script header
struct c1 {
  int jerk_talk_count;  // can't set initial value; is 0 for ints
};

import c1 Chapter1;

// main script
c1 Chapter1;
export Chapter1;


// in room code or global script
  Chapter1.jerk_talk_count = 1;


Cons:
-kinda messy to set up

Pros:
-auto-complete displays members after dot is typed
-a single import and export line make a whole bunch of variables global
-allows grouping of related variables

Cerno

Man, that was fast.

Thanks, this grouping via structs seem very reasonable.
I'll try it out and see if I can get it done.

By the way, I have a bonus question (that kept haunting me all day yesterday):

Assuming I enter the room with the jerk and instead of sending me away, he triggers a dialog where I can give one of three answers. All of them are wrong initially and he comments on my answer and then sends me away. Later in the game I may learn something which enables a fourth answer that I can use to convince the jerk to let me stay in the room.

Most of the scripting would be clear to me by now. The question I have is how to get the dialog system to work within the script. Dialog.Start() won't work since it does not execute when it's called. Would I do something like that with Dialog.DisplayOptions()? Like

Code: AGS
dostuff();
int choice = dDialog.DisplayOptions() // <--- is this triggered immediately and the next line executed when the choice is made?
if (choice == 4)
{
  player.Say("I'll stay");
}
else
{
  player.Say("I'll go.");
  player.changeRoom(2);
}


Let's assume there is a more complex dialog involved where the player answers the jerk and the jerk asks for a password and the player answers again (and so on). Would I script this using combinations of Dialog.DisplayOptions() and Character.Say() or is there a simpler/better way?

Sorry, I haven't tried this yet, got to go to work ;)
123  Currently working on: Sibun - Shadow of the Septemplicon

Khris

Yes, it would work that way.

You can also use normal script commands in dialog scripts. Just indent them by at least one space.

Cerno

So, finally some things to wrap this up for any other beginner stumbling over this, here is what I did in more detail:

In the Scripts section of the GUI, I added a script asc/ash pair which I called GameVariables.
This script is automatically available in all other rooms (no need to import like in C++/Java as I first thought).

Here is an extract:

GameVariables.asc
Code: AGS
VillageVariablesStruct VillageVariables;
export VillageVariables;


GameVariables.ash
Code: AGS
struct VillageVariablesStruct
{
  int jerkSpokenCount;
  bool commentOnJerk;
};

import VillageVariablesStruct VillageVariables;


Since I like initializing the variables where I need them (instead of initializing them in the GameVariables script), initialized them like this from within my starting room script:

Code: AGS
function room_FirstLoad()
{
  VillageVariables.jerkSpokenCount = 0;
  VillageVariables.commentOnJerk = false;
}


I am not certain whether this is the "official" way to do it, I also tried initializing them outside of any function but that gave an error (I guess that is only possible in global scripts). I guess that makes sense, since defining it outside any function in the room script would raise the question when exactly the initialization takes place. Did I get that right? Anyway, initializing the variables when the room loads for the first time works for me.

A possible problem with that: From the manual I gather that room_FirstLoad() is executed after fade in, so initializing my variables here might become problematic when I want to use them inside room_Load() which is executed before fade in. Any solution to that problem? Or am I doing it wrong altogether?

Anyway, for me this solution does what I want it to do.

The rest I implemented along the outlines given above. It seems to do the trick nicely.

One general question: Does anyone know whether there is a tutorial on cross-room scripting (like the use case I described here?). I don't know if I missed it but if it does not exist, I would volunteer to write one (obviously I would need a peer review from someone experienced), so is there a need for something like that?

I would like to address multi-state scripting as well. The part that I am currently scripting has

- a multi-room intro cutscene
- then a playable part where only parts of the rooms are accessible
- then another multi-room cutscene
- then a playable part where all of the rooms are accessible
- then a multi-room outro cutscene

I think this setup (possibly simplified) would make for some nice tutorial material, containing ideas about how to organize the code to be more manageable for larger games.
123  Currently working on: Sibun - Shadow of the Septemplicon

MiteWiseacreLives!

Is it just me (beginner-guy) or are you making this way crazy complicated when a couple of Global variables and a few if statements in the dialogue would suffice? .. Maybe you just want to get your head wrapped around Structs in AGS? which is something I should start messing around with myself.
I get what Khris is saying about organizing variables and auto complete... long term benefits?? (the built in global variable tool is pretty clean though, IMO).

Cerno

What I like about the structs is the level of organization you can achieve with them.

So I have one situation right now that needs three variables to handle things correctly. Assuming I have 30 of these situations in the game (I assume there will be more than that). This would lead to (at least) 100 variables in total, spread over five acts of the game. Since some of these situations might be similar I would have to be careful about naming to not confuse them. So I guess something like Act1_jerk_talked would be sensible. But then I could as well have a struct called Act1 containing the jerk_talked information. It seems much cleaner to me.

Also I have an object-oriented programming background so using encapsulation like that is very natural for me (yeah, I know structs have nothing to do with OO programming, but encapsulation is a key paradigm there).

So doing it that way feels right and in no way complicated (at least to me).
Also, I would like to avoid the Global Variables section of the Gui since it separates the place of initialization from the place of use, which is a potential source for errors (for me). I use it for global constants that I do not want to change during the game, something like "SHOW_CUTSCENES" for debugging purposes.

Maybe I don't get what you mean, would you give an example how you would do it that is less overblown?
123  Currently working on: Sibun - Shadow of the Septemplicon

MiteWiseacreLives!

Basically your first example, except make those variables global so you can run checks in the dialogue and other rooms. This would look and work fine for me, but my background is very basic (Q_basic that I used to play with as a kid actually, and a high school programming classes.. so very basic). Was digging a little to for my own learning, wondering if I should be experimenting with this stuff or if it's practical for my uses in the future.

Cerno

I am not sure I understand.
My first example states that the variables are global and checking them in the dialogue and other room was what I proposed, right?
123  Currently working on: Sibun - Shadow of the Septemplicon

Khris

Quote from: Cerno on Wed 13/03/2013 19:34:19I am not certain whether this is the "official" way to do it, I also tried initializing them outside of any function but that gave an error (I guess that is only possible in global scripts). I guess that makes sense, since defining it outside any function in the room script would raise the question when exactly the initialization takes place. Did I get that right? Anyway, initializing the variables when the room loads for the first time works for me.

You can only set initial values for primitive types (that aren't struct members), by stating the value in the declaration. Something like "int hello = 5;" is fine outside functions, whether in room scripts or not.
The thing is, you weren't trying to initialize the variables, you were just setting them to a value for the first time yourself, which will only work inside functions.
There are two ways to deal with this:
either use the variables in a way that the initial value (ints: 0, bools: false, Strings: null) doesn't have to be changed at the beginning of the game,
or set them at the very start of the game by adding a game_start() function to your variable script.

Cerno

Okay, thanks.

The thing is, I dislike using auto-initialized variables (considered bad style in c++).
Also I would prefer to set my variables close to where I actually use them (seems to be less error-prone to me).

Since that rules out these two ways of doing it, I guess I'll stick to the way I am currently doing it and hope that I won't ever need to set a variable before the room has faded in for the first time. What are the chances anyway? ;)

Edit: Actually, I'm getting some doubts about that. Using the game_start() function might be the cleaner solution after all. Anyway, I'll stick to my approach for now and see how it plays out, but I am getting the feeling that it won't make me happy in the end. :-\
123  Currently working on: Sibun - Shadow of the Septemplicon

Khris


SMF spam blocked by CleanTalk