Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: bulka_tarta on Fri 24/02/2017 18:40:03

Title: Variable in a variable?
Post by: bulka_tarta on Fri 24/02/2017 18:40:03
Hi,

This is a bit tricky to explain so hopefully you can bare with me... I wanted to set up a computer screen, where the player can look through the "database of characters" to find required information.

What I was hoping of getting:
Character Name - Name of the character
Photo - Image of the character
Balance - How much money they have
Bunch of other stats (but I want to keep it short for now)

I wanted to display this info in a ListBox. You see a list of Names on the left, and the right side of the screen shows all the other information. Ideally, player would be then able to click on "More Info" to show a page with additional information. This means, that more than one Label would need to get the same data - for example, in the original List you see "name" and "photo". When you click on "More Info" you get another screen showing "name" and "photo" but also other bits.

I currently show this info with simply changing the string in each listbox.

Code (ags) Select

if (lDatabase.SelectedIndex == 0){
    ClientID = 00;
lblName.Text = ClientName; // Here I just list all the labels that need to be changed
}


and in a another script I determine

Code (ags) Select

if (ClientID == 00){
ClientName = "John Smith;
ClientPhoto = 29;
}


This saves me having to repeat the same info for each of the Labels, however I realized that all this does, is just change the text on each label, while I need each of this things to be a variable, that can be later modified.

Ideally, I wanted to be able to give an ID to each item in the Listbox. If you click on that item (name), it gets the information of that ID's variables and shows it on the right - Name, Photo, Balance.
The only way I can think of to achieve this, was to store all the info in separate functions. For example,

Code (ags) Select

function ID00(){
ClientName = John Smith
ClientPhoto = 29
Balance = 200
}

function ID01(){
ClientName = Jonny Smithy
ClientPhoto = 30
Balance = 2
}

//etc


I'm still unsure of how I would be able to assign this info in the ListBox items thought, so a (bad?) alternative would be to create a separate button for each name in the list (custom list with lots of buttons?). You click on the button and it changes all the Labels on the right.
In other words, I guess it's like making an "inventory item" (which is not an actual inventory item) that has various stats - dmg, durability, lvl etc all of which can be changed in-game.

I've read in the manual about the Arrays, but I don't see how I can make them work - If I have an Array e.g. String Name[]; how can I determine what this Array "holds" (name, photo, balance)? Do I just make an Array for each info bit separate, and just make sure to keep the track of each number "on paper"?

I have searched the forums, but I can't find anything that I could successfully apply to my scenario. Any kind of help or a pointer in right direction would be greatly appreciated!



EDIT:
I just remembered why I wanted to use the different functions.
I was planning on creating a different function for each of the Items in the ListBox and later call these functions with
Code (ags) Select
if (lDatabase.SelectedIndex == 0){
function ID01();
}

That way I could store different variables for each of the Characters, but having so many functions seems like an overkill (I'm thinking of increasing the characters in the ListBox to 100 :P).
Title: Re: Variable in a variable?
Post by: Khris on Mon 27/02/2017 12:30:14
Arrays are what you need. I'd put them in a separate script.

Header:import String clientName[100];
import int clientPhoto[100];
...

Main script:
String clientName[100];
int clientPhoto[100];
// ...
export clientName, clientPhoto;

// convenience function to set initial data
void SetClient(int i, String name, int portraitSlot, int balance, ...) {
  clientName[i] = name;
  clientPhoto[i] = portraitSlot;
  // ...
}

// actually set data when game begins
void game_start() {
  SetClient(0, "John Smith", 29, ...);
  SetClient(1, "Johnny Smithy", 30, ...);
  // ...
}



You can add a function that sets your GUI elements based on the ID:
void SetGUIToClient(int i) {
  lblName.Text = clientName[i];
  btnPortrait.NormalGraphic = clientPhoto[i];
  // ...
}


Just replace the script's header with this: import void SetGUIToClient(int i);then use the function anywhere:
  SetGUIToClient(lDatabase.SelectedIndex);
Title: Re: Variable in a variable?
Post by: dayowlron on Mon 27/02/2017 12:53:33
If these names are all characters in your game you can use the custom properties instead of arrays, but either one is the way you want to go.
Title: Re: Variable in a variable?
Post by: Snarky on Mon 27/02/2017 14:18:01
This is almost a textbook example for when you might want to use a struct. A struct allows you to collect a bunch of related data:

Code (ags) Select
#define MAX_CLIENTS 24 // How many clients there can be in the game

// The data for each client
struct Client
{
  String name;
  int    photo;
  int    balance;
};

// An array of all your clients
Client clients[MAX_CLIENTS];
int clientCount=0;

bool addClient(String name, int portraitSlot, int balance)
{
  if(clientCount+1 >= MAX_CLIENTS)    // We've run out of array space, can't add any more
    return false;
  clients[clientCount].name = name;
  clients[clientCount].photo = portraitSlot;
  clients[clientCount].balance = balance;

  clientCount++;
}

void setupClients()
{
  addClient("John Smith", 29, 200);
  addClient("Johnny Smith", 30, 2);
  // ...
}


Now you can just look up clients[i].name, .photo and .balance to get the data for that client. (If you put the code in a module there's a little more to export the data and functions, which you can look up in the manual.)
Title: Re: Variable in a variable?
Post by: Khris on Mon 27/02/2017 15:09:02
My post sort of evolved while I was writing it; it started out as a short post about declaring and using Arrays, but I probably should've switched to structs at some point  :-D
Title: Re: Variable in a variable?
Post by: bulka_tarta on Sat 04/03/2017 11:52:11
Hey, thanks for the replies everyone.

Structs are the way I wanted to go with, however I don't see (or understand?) the advantage of using them (at least in my scenario).

What I have set up at the moment is something similar to Khris' suggestion. I have a bunch of Arrays, however I don't call them in as a function like this:
Code (ags) Select
SetClient(0, "John Smith", 29, ...);
  SetClient(1, "Johnny Smithy", 30, ...);
  // ...


but I rather declare each array separately, and then call it at the game_start:
Code (ags) Select

function ClientInfo()
{
//Client Details
clientName[0] = "Smith John";
clientPhoto[0] = 28;

clientName[1] = "Bob the Builder";
clientPhoto[1] = 29;

//etc...


This is because I have quite a lot of info to add for each of the characters, and having a function with all the info in one line would be really difficult to read and manage for me later on.

Originally I hoped that structs would allow my to do something like this:
Code (ags) Select
Client[0]{
Name;
Photo;
Balance;
}

Client[1]{
Name;
Photo;
Balance;
}

//etc...

So basically with no need to name each variable with "[1], [2], [3]", but rather just state the number at the top of the struct. I looked at the manual and it seems like this isn't quite how the structs work and that you still need to call each variable separately (weapon[0], weapon[1] etc...), so I may just stick to what I have at the moment. As I mentioned, it would be much easier to add and edit info if I had a line by line structure.
Title: Re: Variable in a variable?
Post by: Crimson Wizard on Sat 04/03/2017 11:56:24
In AGS you can setup structs like:
Code (ags) Select

//Client Details
client[0].Name = "Smith John";
client[0].Photo = 28;

client[1].Name = "Bob the Builder";
client[1].Photo = 29;


Also, if your main concern is having text in one line, you can actually call function like this:
Code (ags) Select

SetClient(0,
          "John Smith",
          29,
          ...);


You can even have several functions, if your struct has too many parameters, e.g. SetClientMainParams, SetClientAdditionalParams, etc.
Title: Re: Variable in a variable?
Post by: bulka_tarta on Sat 04/03/2017 12:43:48
Hey thanks,

So what would be a difference between having a struct like this:
Code (ags) Select
//Client Details
client[0].Name = "Smith John";
client[0].Photo = 28;

client[1].Name = "Bob the Builder";
client[1].Photo = 29;


and just set of arrays like this:
Code (ags) Select
//Client Details
clientName[0] = "Smith John";
clientPhoto[0] = 28;

clientName[1] = "Bob the Builder";
clientPhoto[1] = 29;


For me (someone new to programming) it seems like it's achieving the same thing. Is there anything performance-related that I should be concerned about?

As for calling the function, I wasn't aware that you can do this, thanks!
Title: Re: Variable in a variable?
Post by: Snarky on Sat 04/03/2017 13:00:44
There's not a whole lot of difference in this particular example. The benefit of structs is that they allow you to define that "all this information goes together". So let's say that you later find out that you need another list of clients (maybe a different database on another computer). If you have all the info scattered in separate arrays, you'll have to make a new array for each field (clientName2[], clientPhoto2[], clientBalance2[], ...), but if you have it set up in a struct, you just need a single new array of that struct.

Structs are also quite powerful in other ways, but those things may not come up in this case. So just think of them as a slightly neater way to do basically the same thing. Keeping things neat is quite important: by having a struct, for example, it becomes very clear what values are defined for each client, while keeping it in separate arrays can get confusing (because you'll have to study the code more in depth to see how the arrays relate to each other).
Title: Re: Variable in a variable?
Post by: Crimson Wizard on Sat 04/03/2017 13:03:45
EDIT: well, Snarky was first :)

To be completely honest, there are bigger advantages of using an array of structs over several arrays in a real programming languages, but AGS script has limitations that negate these advantages.
For example, AGS cannot pass regular struct in function, as a single parameter, and cannot copy one struct into another (you need to assign each variable yourself).
On the other hand, if you create managed structs, you can pass them into and out of functions, but managed structs are a bit more complicated to work with (you need to learn how to create and pass them by pointer first).

Anyway, primary advantage is that by defining a struct, which depicts an object, you can easily extend these objects by adding more variables into them, but also create more of those objects if you later need that.

For example, if you have a struct consisting of many variables, and you need an array of those objects, all you do is:
Code (ags) Select

Client myArr[100];

If you later need a separate array of similar objects for some reason, you simply do same thing again:
Code (ags) Select

Client anotherArr[50];

And if for some reason you need just a single object of such type somewhere, you do just:

Client someClientAlone;


With client parameters split into separate variables/arrays you won't be able to do that as easily.
Title: Re: Variable in a variable?
Post by: bulka_tarta on Sat 04/03/2017 13:54:38
Awesome, thanks for the explanations!

In that case I will have a go at putting my arrays into structs.



Thanks again!
Title: Re: Variable in a variable?
Post by: Snarky on Sat 04/03/2017 15:27:35
To be clear, you shouldn't be putting the arrays into structs. You should be putting the variables in a struct, and then making an array of that struct.
Title: Re: Variable in a variable?
Post by: bulka_tarta on Sat 04/03/2017 15:53:52
Yes, I just caught myself on that too!

When I try putting this stuff together I get an error though "Failed to save room room1.crm; details below
Clients.ash(-10): Error (line unknown): ';' expected (cannot define body of imported function)".

How can you have "line -10"? My .ash file for the script is below.

I set the identifier to #define MAX_CLIENTS 2 (just for testing).
Since I want this to be in a separate script, I did some searching and I figured that you need to set up a struct in header of the script.
Code (ags) Select
//.ash file
#define MAX_CLIENTS 2

struct Client {
  String name;
  String address;
  int clientPhoto;
};

import Client clients[MAX_CLIENTS];
import function setupClients();


Code (ags) Select

//.asc file
Client clients[MAX_CLIENTS];
int clientCount=0;

bool addClient(String name, String address)
{
  if(clientCount+1 >= MAX_CLIENTS)    // We've run out of array space, can't add any more
    return false;
  clients[clientCount].name = name;
  clients[clientCount].address = address;

  clientCount++;
}

void setupClients()
{
  addClient("John Smith", "Burger Street");
  addClient("Johnny Smith", "Ketchup is not down");
}



EDIT: I was missing ; at the end of "import bool addClient(String name, String address)" which gave the negative number in the error. I fixed that, however I get an error which I originally wanted to mention, before I got that "line -10". It says "Size of identifier does not match prototype" on the "void setupClients()" line. I can't figure out how, since I declare 2 max clients and add exactly that number of clients to set up...
Title: Re: Variable in a variable?
Post by: Cassiebsg on Sat 04/03/2017 16:26:26
Seems to me like you are defining a void setupClients() in the script, but importing a function setupClients();... They need to be exactly the same. ;)
Title: Re: Variable in a variable?
Post by: bulka_tarta on Sat 04/03/2017 16:43:08
I think I need a little break, haha. I went over these lines so many times, I could no longer spot the difference.

I have set up the structs like they should be (well, hopefully) and everything works just fine!
Thank you!