Subtracting values of global variables-What am I doing wrong?

Started by Perpetuall, Mon 15/07/2024 08:49:40

Previous topic - Next topic

Perpetuall

Hello,
So I'm working on making an RPG-like style game in AGS, and I'm done (hopefully) with almost all the tricky parts of coding, but I have one last snag left that is overwhelming me.

I'm trying to write the battle system, which is a little bit unconventional. Instead of Health or Stamina or any of the typical stats you find in most RPGs, the characters have either Vices or Virtues. There are 30 of each, with 30 that correspond to it on the enemy team-for instance, Greed/Charity, Fear/Faith, etc.

To defeat your opponent, you need to enter specific verses or spells into the text parser. Each time one is entered, it decreases the relevant Vice or Virtue by 1. Each character has a specific number of each Vice or Virtue, and once it hits 0 the character dies. So, for instance, if you encounter a Demon with a value of 4 Fear and 3 Greed, you need to recite 4 verses about having Faith and 3 verses about having Charity to defeat it.

Anyway, I thought I figured it out, but the script is not doing anything when I trigger the 3 dialog scripts. What am I doing wrong?

Code: ags
// room script file
int Fear;

function on_event(int event, int data)
{
    if (event == eEventEnterRoomBeforeFadein)
    {
        aCantrell_manic_dance.Play();
        int ran=Random(2);
if (ran==0) cAngel2.ChangeRoom(5); Fear += 1;
if (ran==1) cEgo.ChangeRoom(5); Fear += 2;
if (ran==2) cDemon.ChangeRoom(5); Fear += 3;
    }
}

function repeatedly_execute()
{ if (Fear == 0)

  {
    cAngel2.ChangeRoom (1); cEgo.ChangeRoom (1); cDemon.ChangeRoom (1);  Display("The demon has been defeated!"); 
} 
}

And the dialog script:
Code: ags
// Dialog script file
@S  // Dialog startup entry point
Angel: &1 "In the Beginning, God created the Heaven and the Earth."
    Display("The demon writhes in agony!"); 
    GiveScore(1);
    bGenesis1V1.Visible = true; 
    Fear -= 1;
stop
(plus 3 other scripts like it)

Also, how do I get my cursor to animate or change when it's over a button, and not just over hotspots? I can't find the answer anywhere.

Thank you.

Khris

If you declare a variable in the room script, it only exists in that room.
Since the dialog line that reduces Fear doesn't throw an error, my guess is you declared Fear twice. Which means you're working with two separate variables which just happen to have the same name.

To create an actual global variable, either use the global variables pane, or export/import it.

Perpetuall

Quote from: Khris on Mon 15/07/2024 10:04:31If you declare a variable in the room script, it only exists in that room.
Since the dialog line that reduces Fear doesn't throw an error, my guess is you declared Fear twice. Which means you're working with two separate variables which just happen to have the same name.

To create an actual global variable, either use the global variables pane, or export/import it.
That is exactly what I already figured. The only problem is I already tested it and took out the line where I declare it (I already added it to the global pane), but it didn't change anything.  :-\
EDIT: Yep, just tested it again with another variable to make sure it wasn't just being wonky because I defined it in the room, and it still doesn't work for some reason.

Updated code:
Code: ags
// room script file

function on_event(int event, int data)
{
    if (event == eEventEnterRoomBeforeFadein)
    {
        aCantrell_manic_dance.Play();
        int ran=Random(2);
if (ran==0) cAngel2.ChangeRoom(5); Greed++;
if (ran==1) cEgo.ChangeRoom(5);  Greed += 2;
if (ran==2) cDemon.ChangeRoom(5); Greed += 3;
    }
}

function repeatedly_execute()
{ if (Greed == 0)

  {
    cAngel2.ChangeRoom (1); cEgo.ChangeRoom (1); cDemon.ChangeRoom (1);  Display("The demon has been defeated!"); 
} 
}

Code: ags
// Dialog script file
@S  // Dialog startup entry point
Angel: &2 "And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters."
    Display("The demon writhes in agony!"); 
    GiveScore(1);
    bGenesis1V2.Visible = true; 
    Greed--;
stop

Khris

I didn't see this before but there's a big problem with your code. C-style programming languages essentially ignore line breaks.
Which means
Code: ags
  if (ran==0) cAngel2.ChangeRoom(5); Greed++;
  if (ran==1) cEgo.ChangeRoom(5);  Greed += 2;
  if (ran==2) cDemon.ChangeRoom(5); Greed += 3;
is equivalent to
Code: ags
  if (ran==0) cAngel2.ChangeRoom(5);
  Greed++;
  if (ran==1) cEgo.ChangeRoom(5);
  Greed += 2;
  if (ran==2) cDemon.ChangeRoom(5);
  Greed += 3;

To run multiple commands conditionally, you need to wrap them in { and }:

Code: ags
  if (ran==0) { cAngel2.ChangeRoom(5); Greed++; }
  // or
  if (ran==1) {
    cEgo.ChangeRoom(5);
    Greed += 2;
  }

Perpetuall

Thank you... I fixed that. Unfortunately, it's still not working! And my friend is getting very upset at me for spending all day coding when I said I'd help clean today. I thought it'd be a simple fix  (laugh)
Life...

EDIT:
Also changed the repeatedly execute function... still didn't solve the problem
Code: ags
 // room script file

function on_event(int event, int data)
{
    if (event == eEventEnterRoomBeforeFadein)
    {
        aCantrell_manic_dance.Play();
        int ran=Random(2);
 if (ran==0) { Greed++; cAngel2.ChangeRoom(5); }
 if (ran==1) { Greed += 2; Demon2.ChangeRoom(5); }
 if (ran==2) { Greed += 3; cDemon.ChangeRoom(5); }
    }
}

function room_RepExec()
{ if (Greed == 0)  {
    cAngel2.ChangeRoom (1); cEgo.ChangeRoom (1); cDemon.ChangeRoom (1);  Display("The demon has been defeated!"); 
} 
} 

Khris

So to get this straight:

- When the room in entered, you randomly set Greed to 1, 2 or 3.
- Next you start dialogs that reduce Greed by 1 multiple times
- But when Greed goes down to 0, you're not getting the line about the demon being defeated?

The first thing you need to check is that your room_RepExec is linked to the room event*, since unlike on_event, it actually needs to be linked or AGS will simply ignore it.

In the long run however you should use a function to reduce the variable which then also checks if it reached 0, because this check doesn't have to run 40 times per second. A related issue with your current code is that if you fix it, AGS will now keep displaying the message, leaving you unable to do anything else.

(* if this turns out to be the issue here, we need to think of a way to clarify and really emphasize this mechanism in the manual, given how often it still stumps beginners)

FortressCaulfield

#6
This might have something to do with it:

    cEgo.ChangeRoom(5);
    Greed += 2;

Anything after ego changeroom will be ignored in a room script because you're not in that room any more. This could lead to Greed being less than zero if the +2 is ignored, so saying verses just makes it go more negative. You might also change the check in rep-exec from == 0 to < 1, just as a safety.

When I run into this sort of issue I add a ton of display commands so I can see the value at every step and figure out what part of the process is misfiring so you know if it's not subtracting or just not hitting zero or if some other call is incrementing it somewhere else.

"I can hear you! My ears do more than excrete toxic mucus, you know!"

-Hall of Heroes Docent, Accrual Twist of Fate

Crimson Wizard

#7
Quote from: FortressCaulfield on Mon 15/07/2024 17:45:01This might have something to do with it:

    cEgo.ChangeRoom(5);
    Greed += 2;

Anything after ego changeroom will be ignored in a room script because you're not in that room any more.

When called for player ChangeRoom is not executed right away, but scheduled to be executed after the script finishes.
See a note in: https://adventuregamestudio.github.io/ags-manual/Character.html#characterchangeroom

But for the purpose of clarity of the code, it's definitely best to have all actions done before ChangeRoom.

Perpetuall

Quote from: Khris on Mon 15/07/2024 15:03:16So to get this straight:

- When the room in entered, you randomly set Greed to 1, 2 or 3.
- Next you start dialogs that reduce Greed by 1 multiple times
- But when Greed goes down to 0, you're not getting the line about the demon being defeated?

The first thing you need to check is that your room_RepExec is linked to the room event*, since unlike on_event, it actually needs to be linked or AGS will simply ignore it.

In the long run however you should use a function to reduce the variable which then also checks if it reached 0, because this check doesn't have to run 40 times per second. A related issue with your current code is that if you fix it, AGS will now keep displaying the message, leaving you unable to do anything else.

(* if this turns out to be the issue here, we need to think of a way to clarify and really emphasize this mechanism in the manual, given how often it still stumps beginners)

Duh!  (roll)
Wow, sorry, that was dumb of me. I guess I just was so used to doing everything in Global Script that I didn't think about having to link it in the room editor... nor the fact that it'd run indefinitely. I need more sleep.
Oh well. Thanks for being patient with me and helping me work out all the many bugs in my script.
I fixed it now, I used DoOnceOnly, and it works fine. I was also a little bit unsure of what function to put it under, do you think there is a better function I could put the check in?
I think the reduction itself would have to be triggered by the dialog script somehow to make the battle work as intended, but if there's a less clunky way to do this that I'm overlooking, that would certainly make things simpler...

Khris

I would do it like this:

Code: ags
// header

import int Fear;
import function FearChange(int by);

// global script

int Fear; export Fear;

function FearChange(int by) {
  Fear += by;
  if (Fear == 0) CallRoomScript(1); // this instead of rep_exe / DoOnceOnly
}

In your dialogs:
Code: ags
  FearChange(-1); // Fear goes down by 1

In your room script:
Code: ags
function room_Load() { // linked to room event!
  Fear = Random(2) + 1; // 1, 2 or 3
  switch(Fear) {
  case 1:
    // set up battle
    break;
  case 2:
    // etc.
  }
}

function on_call(int param) {
  if (param == 1) {
    // Fear reached 0 or less
    // stuff happens
  }
}


Perpetuall

Thanks, I'll try that out.
One more thing: How do I get the cursor to change when it's over a button?

Khris

Do you already have code that's handling this? Or are you using an @OVERHOTSPOT@ label?
In both cases you need something like
Code: ags
  GUIControl* gc = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
  if (gc != null && gc.AsButton != null) lblHotspot.Text = gc.AsButton.Name;
  else lblHotspot.Text = Game.GetLocationName(mouse.x, mouse.y);
inside repeatedly_execute.

SMF spam blocked by CleanTalk