SOLVED (but please comment): functions within functions, experience system

Started by johanvepa, Sat 04/05/2013 23:55:56

Previous topic - Next topic

johanvepa

I recently got an answer to this topic:

Character status screen

Well, things are going great, but I am reaching a dead end when trying to simplify some functions.

I made an experience system where, whenever you use a skill, you gain one exp. point in that particular skill. Whenever you do gain a point in a skill, it then checks with a random to see if that point gets you a raise in the skill you just used. Some skill stats are harder to raise from experience; whenever a skill is used, gain one exp., then do a check to see if three points are available.  if three points are available, do a random check to see if spending those exp points raises the stat in that skill. if three points are not available, wait until more exp points are gained in the skill.

This repeats every time the skill is used, and eventually, the skill raises. The higher the skill, though, the lower the probability that the skill is raised.


So far so good, I can get that to work.


But, then I try to make a function that collects all possible skills into one function, so I save space and avoid typing in skill raising functions for every possible skill.

In short, I do something like this:

In the global script:
Code: AGS


//skill with a certain spell, skills go from 0 to 100.
int spellBAKECAKE;
export spellBAKECAKE;

int expBAKECAKE; //experience with the same spell
export expBAKECAKE;


//here we insert the characteristics of the spell BAKECAKE into a function and add toughness for how much exp it takes to attempt to raise the stat:

function adjustskillforexp(int Characteristic, int expCharacteristic, int toughness)
{
while (expCharacteristic >= toughness)  //the higher the toughness, the more exp needed to do that random check and see if skill is raised.
  {
  expCharacteristic -= toughness; //when doing the random check, the exp is lost, regardless of the result
  if (Random(100) > Characteristic)
    {
    Characteristic += 1;  //in this case, skill raised
    }
  }
}

function adjustallskillsforexp()
{
adjustskillforexp(STRENGTH, expSTR, 2); //so STRENGTH is attempted raised whenever two exp. points in that skill are gained.
adjustskillforexp(ALERTNESS, expALE, 2);

adjustskillforexp(spellBAKECAKE, expBAKECAKE, 1); //and our spell BAKECAKE is attempted raised whenever one exp point in that skill is gained.
}




What I want to do with this is; whenever exp is gained, do the adjustallskillsforexp(), that way experience is always channeled into stats raising.


This worked until I made the two abovementioned functions. But it should work. Ought to. Really.

Can anyone see the fault?



Gilbert

The reason is that the parameters for a function are passed by reference.
For example, when this is called:
Code: AGS

adjustskillforexp(STRENGTH, expSTR, 2); //so STRENGTH is attempted raised whenever two exp. points in that 

The VALUE of the variable STRENGTH is passed to the function, but not the variable itself. So, the function just modifies a 'copy' of that variable it and will NOT affect the original variable. One way of solving this is to return the value back, like this:
Code: AGS


//skill with a certain spell, skills go from 0 to 100.
int spellBAKECAKE;
export spellBAKECAKE;

int expBAKECAKE; //experience with the same spell
export expBAKECAKE;


//here we insert the characteristics of the spell BAKECAKE into a function and add toughness for how much exp it takes to attempt to raise the stat:

function adjustskillforexp(int Characteristic, int expCharacteristic, int toughness)
{
while (expCharacteristic >= toughness)  //the higher the toughness, the more exp needed to do that random check and see if skill is raised.
  {
  expCharacteristic -= toughness; //when doing the random check, the exp is lost, regardless of the result
  if (Random(100) > Characteristic)
    {
    Characteristic += 1;  //in this case, skill raised
    }
  }
  return Characteristic; //return the modified value
}

function adjustallskillsforexp()
{
STRENGTH=adjustskillforexp(STRENGTH, expSTR, 2); //so STRENGTH is attempted raised whenever two exp. points in that skill are gained.
ALERTNESS=adjustskillforexp(ALERTNESS, expALE, 2);

spellBAKECAKE=adjustskillforexp(spellBAKECAKE, expBAKECAKE, 1); //and our spell BAKECAKE is attempted raised whenever one exp point in that skill is gained.
}




DoorKnobHandle

What iceboty said is correct, except that this is pass-by-value and NOT pass-by-reference! :p

Khris

Just wanted to say that there isn't a function within a function anywhere here (nor is it possible in AGS) :-D

Calin Leafshade


johanvepa

#5
Quote from: Iceboty V7000a on Sun 05/05/2013 13:56:45

"The VALUE of the variable STRENGTH is passed to the function, but not the variable itself. So, the function just modifies a 'copy' of that variable it and will NOT affect the original variable. "


Now THAT explains a lot. So, the integers put in the paranthesis after the function name is acting as numbers, not affecting the integer placed there.

The  way I see the code you altered, it will return a value which I can use to alter the BAKECAKE and any other integers, using a collect-all function with this inside:

Code: AGS

BAKECAKE = adjustskillforexp(BAKECAKE, expBAKECAKE, toughness)
//and so on with all the different skills


What my function did was create a value based on the STRENGTH integer (int this case, STRENGTH + 1), but not to return anything!

Like writing "2+2" but no "=".

And therefore, my adjustallskillsforexp function should instead define that the BAKECAKE integer be set to the returned result of the function adjustskillforexp using the value of BAKECAKE as a variable.

But then again, something's amiss. I'm trying to adjust both the integer BAKECAKE and the integer expBAKECAKE at the same time here. Increasing the integer BAKECAKE must be followed by a subtraction from the integer expBAKECAKE, since I spend the experience points to attempt the chance at raising the skill. If the experience points stay where they are, I can keep on attempting to raise the stats and never spend a single exp point in doing so which could have my game in an endless loop? Or would the value of expCharacteristic still count down inside the function? Anyway, I would be "recycling" the exp points, meaning next time I check if I have enough exp points, I would still retain those from the first check.

So my function needs to return TWO values. To my knowledge, that isn't possible, yes? Since I can't tell AGS by use of a function to initiate another function using a certain integer as variable, the only way I can do what I want is this, right?:

Code: AGS


function CheckSTRENGTHGainForExp(toughness)
{
while (expSTRENGTH >= toughness)
  {
  expSTRENGTH -= toughness;
  if (Random(100) > STRENGTH)
    {
    STRENGTH += 1;
    }
  }
}

function CheckALERTNESSGainForExp(toughness)
{
while (expALERTNESS >= toughness)
  {
  expALERTNESS -= toughness;
  if (Random(100) > ALERTNESS)
    {
    ALERTNESS += 1;
    }
  }
}

function CheckBAKECAKEGainForExp(toughness)
{
while (expBAKECAKE >= toughness)
  {
  expBAKECAKE -= toughness;
  if (Random(100) > BAKECAKE)
    {
    BAKECAKE += 1;
    }
  }
}

//and so on and so forth until I'm through all of the SKILLS and expSKILLS integers I got attached to my character.

//And then, once I use my skill in casting the spell BAKECAKE, I can, having cast the spell, put this in near the end of the spellcasting function:

CheckBAKECAKEGainForExp(1); //meaning you need 1 exp point to attempt to raise the stat

//Or, I could gather them all together in this function:

function adjustallskillsforexp();
{
CheckSTRENGTHGainForExp(2);
CheckALERTNESSGainForExp(2);
CheckBAKECAKEGainForExp(1); //this spell isn't so hard to raise as the other skills.
}



And this would probably work, since the altering of the integers BAKECAKE and expBAKECAKE are not dependent upon a function returning a value, it is done directly in the function. It wouldn't look good, tough, it would be cluttered and tough to alter afterwards. But I can't see a way around it, otherwise. What do you think?

Oh, and Khris, I was sorta aware that I wasn't doing a "function within a function", since that would mean defining a function withint another function, yes? Bear with me for being imprecise, as I know you have before.

johanvepa

I can report that the above stated works.

Comments are welcome.

Ryan Timothy B

Quote from: Nanuaraq on Sun 05/05/2013 21:11:08
Oh, and Khris, I was sorta aware that I wasn't doing a "function within a function", since that would mean defining a function withint another function, yes? Bear with me for being imprecise, as I know you have before.
You named the thread "functions within functions". Which would be this:

Code: ags

function bakePie() {
  function eatPie() {
  }
}


Which is possible in a "real" programming language, just not AGS.

johanvepa

No wait, I got it now. Doing the "return"-thingamagig really changed things.

Will report back once I've had a chance to test it.

johanvepa

There. I re-scripted the code for adjusting Ego's strength attribute and his skill with the spell BAKECAKE.



Code: AGS


//integers on experience demand for characteristics

int DemandForSTRENGTH;
export DemandForSTRENGTH;

int DemandForBAKECAKE;
export DemandForBAKECAKE;



//adjust characteristics for experience, with modifier for the experience demand integers (how tough it is to raise)

function AdjustCharacteristicForExp(int Characteristic,  int expCharacteristic,  int demand)
{
while (expCharacteristic >= demand)
  {
  expCharacteristic -= demand;
  if (Random(100) > Characteristic)
    {
    Characteristic ++;
    }
  }
return Characteristic;
}

//having adjusted characteristics levels,  now to do away with the spent experience
function AdjustExpAfterAdjustingCharacteristic(int expCharacteristic,  int demand)
{
while (expCharacteristic >= demand)
  {
  expCharacteristic -= demand;
  }
return expCharacteristic;
}

//Handy adjust-all when gaining experience
function AdjustAllCharacteristicsAndExp()
{
STRENGTH = AdjustCharacteristicForExp(STRENGTH,  expSTRENGTH,  DemandForSTRENGTH);
expSTRENGTH = AdjustExpAfterAdjustingCharacteristic(expSTRENGTH,  DemandForSTRENGTH);

BAKECAKE = AdjustCharacteristicForExp(BAKECAKE,  expBAKECAKE,  DemandForBAKECAKE);
expBAKECAKE = AdjustExpAfterAdjustingCharacteristic(expBAKECAKE,  DemandForBAKECAKE);


SETMAXSTATISTICS(); //function that applies the updated characteristics to the maximum health and mana integers
}

function game_start() {   

DemandForSTRENGTH = 2;
DemandForINTELLIGENCE = 2;
DemandForMAGICUSE = 2;
DemandForOPEN = 1;
}




I can report that the above works very well. Scripting the rest of the characteristics will be much more straightforward. Thank you for the pointer on using variables with my functions and the return effect.


Crimson Wizard

Nanuaraq, I strongly recommend you to explicitly define your function's type depending on what value you are returning. What you wrote may work with integers, but it may cause problems with other types (not sure that will compile). Also it will prevent possible confusions in the future.

What I mean: instead of "function AdjustCharacteristicForExp" write "int AdjustCharacteristicForExp". The word before function name is function return-type.
By default AGS allows to return integer if you start function declarations with just "function" but I find this pretty non-obvious. Probably it was left for backwards compatibility.

johanvepa

Quote from: Crimson Wizard on Mon 06/05/2013 21:18:58
Nanuaraq, I strongly recommend you to explicitly define your function's type depending on what value you are returning. What you wrote may work with integers, but it may cause problems with other types (not sure that will compile). Also it will prevent possible confusions in the future.

What I mean: instead of "function AdjustCharacteristicForExp" write "int AdjustCharacteristicForExp". The word before function name is function return-type.
By default AGS allows to return integer if you start function declarations with just "function" but I find this pretty non-obvious. Probably it was left for backwards compatibility.

So a function can be defined as an integer? Or rather, an integer may return a value created by a function?

I'm not entirely sure what the catch is, but if I get what you're saying, a function that has "return" in it somewhere always returns something, and that something should be defined as either integer or some other thing.

By the way, what others than integers could the return-type be? I ask because I have not seen anything like this in the manual (or perhaps I didn't look thoroughly enough).

And thank you.

Crimson Wizard

Quote from: Nanuaraq on Mon 06/05/2013 21:57:54
So a function can be defined as an integer?
You define function's return type by that. Return-type may be int, float, String, pointer to AGS types, like Character* for instance. That will let you return value of corresponding type from function.

For example:
Code: ags

Character *GetSomeCharacter()
{
   return character[0];
}

This will always return a pointer to first character in the list (with Id = 0).

Quote from: Nanuaraq on Mon 06/05/2013 21:57:54
Or rather, an integer may return a value created by a function?
No, and this makes no sense actually. Not sure what you call "integer" here - a type or variable - but neither can "return" anything.

geork

@Nanuaraq
Also, you can have no return type by declaring the return type to be void, so:
Code: AGS
void DoSomeStuff(){
    Display("Doing things");
    //NO RETURN STATEMENT!
}

I'd recommend this if you have a function that doesn't return anything.

johanvepa

Thank you, Crimson. These past few months have been highly educational.

EDIT: Solved it.

Crimson Wizard

You do not declare function parameter types properly, it should be:
Code: ags

function castspell(int spellname); // I guess spellname is integer?


The parameters should be declared like:
(type name, type name, type name).

johanvepa

Quote from: Crimson Wizard on Mon 06/05/2013 22:47:51
You do not declare function parameter types properly, it should be:
Code: ags

function castspell(int spellname); // I guess spellname is integer?


The parameters should be declared like:
(type name, type name, type name).


Damn, you beat me to it. And I was so happy because I saw it myself.

Khris

Quote from: Crimson Wizard on Mon 06/05/2013 21:18:58By default AGS allows to return integer if you start function declarations with just "function" but I find this pretty non-obvious. Probably it was left for backwards compatibility.
I happened to read the wikipedia entry about C today, and I think the convention that "function" is basically a substitute for "int" was adopted to AGS Script from C.

johanvepa

Thank you everyone for your help and comments. Things are going very well.

SMF spam blocked by CleanTalk