Solved: Stuck on making shops

Started by johanvepa, Mon 18/02/2013 22:00:35

Previous topic - Next topic

johanvepa

I have made an attempt at using money in a game, and have gotten quite far, I think. However, I stumble upon a couple of hurdles now. I hope someone out there will answer me these two simple questions.


First, bear with me for not being a tech. I have attempted to read the manual, but I must admit, the wiki on using money in the game was just about useless to me, being a newbie. I was looking for a quick "how-to" way. All this talk of strings, variables, ints and all that jazz just gets me confused. I need to see things working by example before I can wrap my mind around the theory (what on earth is an int, unless you've seen what it looks like in the script?). Happily, there have been many posts on this topic.

Thanks to TerranRich for a post from 2004 informing me of how to setup the inventory so I can count my money by looking at them in the inventory screen. And to Jordan, also from 2004, for telling me to use the global script to fix the amount of money from the beginning of the game. And to someone I don't remember for showing me a working way of deducting and adding amounts of ANY sort of inventory, without (very) complicated codings in the global script.

Anyway, I made an inventory item called iMoney, imported a sprite with a picture resembling gold coins, made another inventory item called iApples, imported a sprite for that, too, and ended up with this in my global script:


// In the global script, somewhere under
function game_start() {   
// I put this:
  player.InventoryQuantity[iMoney.ID] = 30;

// and somewhere further down I put this:
function iMoney_Look()
{
Display("You have %d golden coins!",player.InventoryQuantity[3]);
}
//since my gold coins is inventory item no. 3.

// For buying apples at the farmer's daughter (the character Hilde), I scripted this:
function cHilde_UseInv()
{
if (cEgo.ActiveInventory == inventory[3]) {  //here I use the inventory item iMoney on Hilde
if (player.InventoryQuantity[iMoney.ID] > 0) {  //and only if there's still at least one left
cEgo.Walk(265, 175, eBlock);   // Character walks up to the stand
Display ("You buy ten apples for a silver piece");
player.InventoryQuantity[iMoney.ID] -= 1;   //paying the cost
player.InventoryQuantity[iApples.ID] +=10;   //and receiving said and paid apples
UpdateInventory();  //dunno what this does, but I dare not leave it out.
}
else Display ("You don't have any money!");
}
}

// And for keeping track of same apples (inventory item 2), I have:
function iApples_Look()
{
Display("You have %d small apples from last Fall's harvest",player.InventoryQuantity[2]);
}



IT WORKS! What a feeling. This has taken me hours to set up right. Now I can do this with more shops    :)


For my question # 1:
Now, I would like very much to have my moneybag present in my inventory, even when the count is 0! At present, the iMoney sprite disappears from my inventory when it reaches 0, until I find some more. Can I have it just sit there being empty, telling me that I have 0 coins, when I look at it?


Question # 2:
Second, I would like to be able to put this script in with a dialog, so I can buy apples by choosing that topic in a dialog, with the obvious bonus of being able to choose from different wares from the same seller. I figured something like this might do it (assuming I initiate the dialog by for example clicking the iMoney inventory item on the seller):

// Dialog script file
@S  // Dialog startup entry point
narrator: Would you like to buy something?
return
@1   //choose topic: apples
{
if (player.InventoryQuantity[iMoney.ID] > 0) {
Display ("You buy ten apples for a silver piece");
player.InventoryQuantity[iMoney.ID] -= 1;
player.InventoryQuantity[iApples.ID] +=10;
UpdateInventory();
}
else Display ("You don't have any money!");
}
return
@2   //choose topic: nothing
narrator: You decide to buy nothing after all.
stop

But something is horribly wrong here. It seems I can't just adopt the same approach when it comes to dialogues. Am I totally on the wrong track here, or am I missing some small but vital detail?





Khris

First of all, if you want to put code in a post, you can use code tags. They will do all the coloring and stuff for you. Just click on the dropdown menu above the message area that says "Code" and select "AGS". This will insert code tags into your post at the cursor position. Now paste the code in between.

Int:
Spoiler

As for what an int is: it is short for integer. It's a kind of variable. A variable is something you use in code to store data.
Consider this:
Code: ags
  int a = 5;     // declare integer variable; name: a, initial value: 5
  Display("%d", a + 3);

This will display 8.
You're actually using an integer variable in your code, namely player.InventoryQuantity[Money.ID].
[close]

On to your questions:
If you decouple the amount of money from your inventory item, you can keep the moneybag.
Here's code for the very top of your GlobalScript:
Code: ags
int moniez = 30;

bool Spend(int coins) {
  if (coins > moniez) return false;
  moniez -= coins;
  return true;
}


Add this to GlobalScript.ash:
Code: ags
// money
import bool Spend(int coins);


When you want to buy something:
Code: ags
function cHilde_UseInv() {

  if (cEgo.ActiveInventory == iMoney) {
    if (Spend(1)) {   // if we can spend one coin, do so, then:
      cEgo.Walk(265, 175, eBlock);
      Display ("You buy ten apples for a silver piece");
      player.InventoryQuantity[iApples.ID] += 10;
      UpdateInventory();
    }
    else Display ("You don't have any money!");
  }
  else Display("I can't do that.");   // we used something other than iMoney on cHilde
}


The same works in dialogs, but you have to indent the code by at least one space to use regular script commands. You should use proper indentation from the start anyway, it makes your code much more readable, you'll easier spot errors, and the people who are going to help you solve your coding problems will also get a warm, fuzzy feeling in their tummies :)

Code: ags
@1   //choose topic: apples
  if (Spend(1)) {
    Display ("You buy ten apples for a silver piece");
    player.InventoryQuantity[iApples.ID] +=10;
    UpdateInventory();
  }
  else Display ("You don't have any money!");
return


Also, now that we have a variable in the global script, you have to use this:
Code: ags
function iMoney_Look() {
  String c = "coins";
  if (moniez == 1) c = "coin";
  Display("You have %d golden %s!", moniez, c);
}


PS: Why UpdateInventory()?

johanvepa

Well, now I know what "Indenting the code" means.

Thanks a lot, there's something to work on here. Been sitting up for way too long, coding the shop function at Hilde's groceries, and I am proud to report that it works!. I even typed in a few messages about how much you're carrying around if you buy this or that amount of a given thing. It's tough work typing all the code, but it is so rewarding to see things work    :)

I'm not done yet, though. I still haven't figured out what all the "bool" stuff is about (but I'm learning as I go). And why do you call it "moniez"? Any special reason?

Khris

Nah, I just think it's a funny word :)

A bool is the most primitive type of variable. It's either true or false, literally:

Code: ags
bool iamtrue = true;
if (iamtrue) Display("You will read this.");


Bools are often used to denote progress in the game.

johanvepa

Quote
First of all, if you want to put code in a post, you can use code tags. They will do all the coloring and stuff for you. Just click on the dropdown menu above the message area that says "Code" and select "AGS". This will insert code tags into your post at the cursor position. Now paste the code in between.

/Thank you for that  ;)

/ and thank you for answering my # 2, I see it working now.

/I think I got the hang of using the "if", the "else if" and the "else" now.

/About integers and money, and looking at the bag without there being any money "in" the bag, I think I got a little more than I bargained for. Don't get me wrong, I believe I can see the general idea in what you're telling me here, and if I may attempt at explaining it to myself in layman's terms, it goes something like: Make a variable called an "int" and name it MONEY. Now make another variable called "int" and name it AMOUNT. Then make a bool ("true or false" test) based on the int AMOUNT, so I can use the bool to test if I've got enough MONEY to perform a given action based on an AMOUNT (e.g. buying stuff).

A bit more thorough than using the code I found from earlier years, yes.
But I should be able to simplify it a bit, yes? If I stick with just one integer for money, called for example MONEY, by defining the integer in the globalscript.asc, give it a gamestart value, and then allow it to vary as the game progresses, I can use it to keep track of money.
Then I need to
1) add or subtract from the integer as my character recieves or spends money
2) compare the  value of the integer "at the given time" with the value needed to make a given transaction, and
3) I can use the "%d" in a LOOK function in the inventory to call forth the "at the given time" value of the integer.

It would go something like this in the globalscript.asc:

Code: ags


function game_start() {
int MONEY = 50   //gives cEgo 50 MONEY at game start. He may choose to keep track of this integer through looking at his pouch.

//So, instead of using the integer embedded in the inventory thingamagig, I can create another integer to do the same as a "player.InventoryQuantity" can do, and this integer has no connection to the pouch except for the fact that I can keep track of the MONEY integer by coding a function that links the integer to using or looking at the pouch.

function iPOUCH_look   //assuming I have an inventory item called iPOUCH that cEgo begins the game with.
  {
  Display("You have %d silver coins!",int[MONEY]);
  }


//Now, in a given room where I might find money, I can do this:
function DeadCorpse_Interact
   int[MONEY] += 7;
   Display("You find 7 silver pieces on the corpse")


//And in the dialog tree which I use to initiate the bargain (Ego has already walked up and chosen topic "Buy 10 apples", I would have this:

 if (int[MONEY] > 0)     //We can spend one coin
   {   
   int[MONEY] -= 1;
   Display ("You buy ten apples for a silver piece");
   player.InventoryQuantity[iApples.ID] += 10;
   UpdateInventory();
   }
 else    //We cannot spend a single coin
   {
   Display ("You don't have any money!");
   }
 


WOULD THIS WORK?

Crimson Wizard

No, it won't work. There is a number of mistakes there.

First of all, you are using "int" keyword in a very wrong way. Khris already mentioned, that it is a variable type declaration. You should use it only to declare the variable, not when using it further.

For example:
Code: ags

int MONEY; // declare variable MONEY of type "integer"; initial value will be 0
int MONEY2 = 50; // declare variable, and set initial value

MONEY += 5; // add 5 to money
MONEY2 = MONEY + 10; // make some calculations, MONEY2 is now equal to MONEY plus 10


Therefore command "int[MONEY]" makes no sense. You should use just "MONEY".

The syntax "A[ B ]" is actually used when you have an array. Array is a group of variables of the same type joined together for the sake of convenience.
For example:
Code: ags

int PEOPLE_HAVE_MONEY[10]; // declare an array of 10 variables of type integer

PEOPLE_HAVE_MONEY[0] = 50; // assign value 50 to the 0th (first) variable in the group.



Other important thing is the scope of life of the variable.
To put this simply: variable exists only in the block where you declare it.
If you declare variable outside of all functions, it will exist until game end. If you declare it inside the function, it will exist until function exits.
How do you know what is variable declaration, and what is variable usage? Declaration always starts with type!
Code: ags

int MY_NUMBER; // this is declaration
MY_NUMBER = 10; // this is usage

Therefore, since you want to keep MONEY count for the rest of your game, you should move "int MONEY = 50;" outside of game_start() (just place it somewhere at the top of the script).
Or, you may declare it first, then set starting value in game_start:
Code: ags

int MONEY; // put this outside of any function

function game_start() {
  MONEY = 50; // set initial value; notice that you do not need to put "int" here.
}


johanvepa

Code: ags
[quote author=Crimson Wizard link=topic=47664.msg636446583#msg636446583 date=1361387525]
No, it won't work. There is a number of mistakes there.
[/quote]

But I got the general idea, haven't I? I'd never heard of an "integer" until a couple days ago    :)

[quote]
First of all, you are using "int" keyword in a very wrong way. Khris already mentioned, that it is a variable type declaration. You should use it [u]only to declare the variable[/u], not when using it further.

Therefore command "int[MONEY]" makes no sense. You should use just "MONEY".
[/quote]

Got it. Thanks.

[quote]
The syntax "A[ B ]" is actually used when you have an array. Array is a [u]group of variables of the same type[/u] joined together for the sake of convenience.
For example:
int PEOPLE_HAVE_MONEY[10]; // declare an array of 10 variables of type integer

PEOPLE_HAVE_MONEY[0] = 50; // assign value 50 to the 0th (first) variable in the group.
[/quote]

So what this means is: you can make lots and lots of integers? And give them similar names, yes?

[quote]
variable exists only in the block where you declare it.
If you declare variable outside of all functions, it will exist until game end. If you declare it inside the function, it will exist until function exits.
[/quote]

Thank you. That's important. So I can declare a variable in the general script (which will last for the game, and which I can vary in all rooms), a variable in a room (which will last for the room only, I cannot manipulate its value in other rooms) and a variable in a function (as you said, variable ends when function ends). This opens up quite a perspective.

[quote]
int MONEY; // put this outside of any function
[/quote]

Thanks. That cleared up a relevant issue.

[quote]
Also, in your script up there, I can see that game_start function has opening bracket { but no closing bracket }.
[/quote]
Bear with me.


Conclusively, I seem to have the right idea, only need to know more exactly what words to use and more exactly where in the script to use them, right?
And integers can be set up as I like them, as many as I like them. And an integer set in the global script is always "on", no matter what room I'm in, so I can use it to for example register the completion of a certain quest in one room by altering the integer value by a function in that room, and then have someone/something in another room react to that new value of the integer, right? I think I've got a hold of some pretty decent basics, now.

Thank you again.

Crimson Wizard

Quote from: Nanuaraq on Wed 20/02/2013 21:19:23
But I got the general idea, haven't I?
Yes.

Quote from: Nanuaraq on Wed 20/02/2013 21:19:23
So what this means is: you can make lots and lots of integers? And give them similar names, yes?
Basically, yes. It may be useful to group values of the same type. Like in that example I gave, if you want to know money savings for 100 people, you should use array (of size 100), and not 100 different variables.

Quote from: Nanuaraq on Wed 20/02/2013 21:19:23So I can declare a variable in the general script (which will last for the game, and which I can vary in all rooms), a variable in a room (which will last for the room only, I cannot manipulate its value in other rooms) and a variable in a function (as you said, variable ends when function ends).
Generally yes, but there's one thing: a variable in room script won't "last" for the room only, it will last for whole game too, but will be "visible" only in the room.
Okay, here goes more theory, but that was inevitable :).
There are two concepts: a scope of life and a scope of visibility (they may be called a bit differently sometimes, but that does not matter).

The scope of life defines how long variable exist. The variable put outside all functions exists as long as program (game) exists. So, regardless of what script you put it in, whether Global, or room, or custom module, it will still exist till the very end. But the variable, declared in function, exists till function end. For example:
Code: ags

int global_var;
function MyFunc()
{
   int local_var;
   local_var += 10;
   global_var += 10;
}

The global_var exists all the time, and every time you start this function, it will be increased by 10, eventually reaching very high numbers. But local_var exists only during one pass through the function, it will be erased as soon as function ends. And when the function is called for the second time, it will be 0 again.

The scope of visibility defines where variable is visible, and therefore - may be used. For example, variables in Global script are visible in Global script and also may be made visible to all rooms. While variables in rooms are visible only to corresponding room scripts. However, although room variables are not visible in other rooms, they are NOT erased, when you leave the room, they stay and keep their last value. When you return to the room, all the room variables are still the same as you left them.

johanvepa

The above totally makes sense. Thanks, Crimson.

One more slight problem, which may or may not have to do with the scope of visibility:

For testing, I created an object called GroundMoney, ie. "money lying on the ground", and put this into the room script:

Code: ags

function oGroundmoney_Interact()
 {
 MONEY += 10;
 Display("You find 10 silver pieces on the ground.");
 UpdateInventory();
 }


but I get an error when saving, stating that theres an error in the line where MONEY is present, error message " Undefined token 'MONEY' "
Have I forgot to somehow make the MONEY integer from the global script become visible to the room script? And more importantly, how do I do that?

It worked when I used this:
Code: ags

player.InventoryQuantity[iMoney.ID] += 10;


Khris

Since you have declared MONEY in the global script, it isn't visible in room scripts without adding a few lines.
After the declaration, export the variable. You need only the name, not the type, since AGS already knows what you're talking about:

Code: ags
int MONEY = 50;
export MONEY;


The second step is to import the variable, you need to do this in the Globalscript's header, GlobalScript.ash. This time we'll need the type though:

Code: ags
import int MONEY;


This allows you to read and change MONEY in any room script.

Since this is so useful and beginners have a lot of problems with this, AGS got the Global Variables pane added to its project tree. So instead of declaring, exporting and importing the variable in script, you add it in there, setting its type, name and initial value. AGS will do the rest for you.

johanvepa

Thanks, Khris, for those last pointers.

I've got a bit to work on now. Can't wait to see it work   :)

johanvepa

It DOES work.

Yay!   :)

So, create an integer for MONEY, export it then import it through globalscript.ash, create an inventory item named POUCH, use the scripts to set and raise/lower money amount in specific functions and use the lookinventory command to keep track of the MONEY integer.

It's that easy   :)

SMF spam blocked by CleanTalk