Help with custom function regarding reloading bullets into a gun

Started by Egmundo Huevoz, Fri 29/12/2017 04:04:38

Previous topic - Next topic

Egmundo Huevoz

Hello! I'm having trouble with a "reloading bullets" script that I made. Some info before we start:
-I call this function from "on_key_press", by pressing R.
-I track the bullets with a GUI that has a button (btnAmmo), which has a picture 120 pixels wide, with 6 bullets with 20 px wide each. That works perfectly.
-I have another function for shooting, which also works perfectly, and deducts 1 from the global var "Bullets", which tracks my remaining bullets outside of the gun. The shooting function also deducts 20px from btnAmmo. If the button is 0 px wide, then the gun is empty.

The problem is with a function that manages reloading bullets into the gun. The max amount of bullets is 6, tracked by the global var "Maxammo" (I plan to make other guns with more bullets, but for now, it's always 6). I track the missing bullets from the magazine with the global var Empty_ammo (ie, if you shot 4 bullets, Empty_ammo should be =2). It works, but with a "delay", so to speak. I shoot some bullets, let's say 3, for this example, and I reload. My maganize refills (as shown by the GUI), but my remaining bullet count doesn't change (as shown by another GUI). When I reload again, no matter if I shot 1, 6 bullets, or anything inbetween, I lose 3 bullets (the amount I reloaded previously). And so on, it seems like the reloading is out of synch.

Any help will be deeply appreciated. I tried for hours to fix this, but I can't do it on my own.

Code: ags
// new module script
function Reload_gun(){ //important: every bullet is 20 px wide in the GUI, which is the origin of the 20's seen below.

Empty_ammo=Maxammo-(btnAmmo.Width/20); //global var representing bullets missing from the magazine, i.e fired bullets

  if ((btnAmmo.Width!=Maxammo*20)&&(Bullets>0)){ //don't do anything if magazine is full or you don't have bullets
    if (Empty_ammo>Bullets){
      btnAmmo.Width=btnAmmo.Width+Bullets*20;
      Bullets=0;
    }
    else{ //we have enough bullets to fill the magazine no matter what
      Bullets-=Empty_ammo; //lose the amount of bullets missing (1-6)
      btnAmmo.Width=btnAmmo.Width+Empty_ammo*20; //ammo = ammo left + ammo missing
      Empty_ammo=0;
      if (btnAmmo.Width>Maxammo*20){ //so you can't have more bullets than the size of your magazine
        btnAmmo.Width=Maxammo*20; 
      }
    }
    aReloadgun.Play();
  }
  else {
    aEmptygun.Play();
  }
}

Gilbert

Haven't really ventured into the codes, but I wonder why don't you use another variable (something like Current_ammo) to track the number of remaining bullets in the gun, rather than relying on computing it each time from the width of a UI button?

Usual implementation is to keep your game stats in variables, and then display these stats in a UI, not the other way round (like in your current implementation that you have to look up a stat by working backwards from checking what is displayed).

This could save you a lot of troubles and make the codes much more readable, as you don't need frequent those "*20" and "/20" to convert numbers to and from UI objects. My bet the problem is either fault in those conversions, order of the codes or incorrect "if" conditions.

Kweepa

Agree totally with Gilbot7000.
I would have these globals: CurrentMagazineSize, TotalBullets, BulletsInMagazine.
Code: ags

int bulletsToLoad = Maths.Min(CurrentMagazineSize - BulletsInMagazine, TotalBullets);
if (bulletsToLoad > 0)
{
  aReloadGun.Play();
  TotalBullets -= bulletsToLoad;
  BulletsInMagazine += bulletsToLoad;
  btnAmmo.Width = 20 * BulletsInMagazine;
}
else
{
  aEmptyGun.Play();
}

I don't see where the UI for the total bullets is updated. Perhaps that doesn't update until you fire again?
Still waiting for Purity of the Surf II

Egmundo Huevoz

Gilbert, you're right, I feel silly now. I should just track everything with variables. The code was even worse before, so I can't complain. I'm progressing (laugh)
Kweepa, I replaced my code with yours, it seems better. Then only problem is that "Min" is not a member of "Maths". I understand what you mean by that line, so I'm gonna replace it with something else and see if it works.

EDIT: Got it working! :D

In my custom Shoot and Miss functions:
Code: ags

  btnAmmo.Width-=20;
  Bullets_magazine--;


In my Reload function:

Code: ags
function Reload_gun (){
//int Bullets_to_load= Maths.Min(Magazine_size - Bullets_magazine, Bullets_total); ERASED THIS BECAUSE IT DIDN'T WORK

if (Bullets_total!=0){
  Bullets_to_load=Magazine_size-Bullets_magazine;

  if (Bullets_to_load > 0){
    if (Bullets_to_load>Bullets_total){
      Bullets_magazine=Bullets_magazine+Bullets_total;
      Bullets_total=0;
    }

    else 
    {
      Bullets_total -= Bullets_to_load;
      Bullets_magazine += Bullets_to_load;
    }
  aReloadgun.Play();
  btnAmmo.Width = 20 * Bullets_magazine;
  }
  else{
    aEmptygun.Play(); // to be replaced with a "can't reload, full gun" sound, or something
  }
}
else
{
  aEmptygun.Play();
}
}


BTW, just one minor problem I have left. I don't know if it merits making another post, so I'll just ask it here, maybe it's easy for you, more experimented AGSers. All of these functions (Shoot, etc) are in globalscript. I want to make separate modules to avoid the clutter and also find them easily. But I tried to do it with the Shoot function, and I get an "undefined token" error, although the variables in question are defined in the global script, exported, and then imported in the global header.

At the top of Global Script.asc:

Code: ags
Stats personaje [100];
export personaje;


Then, in Global script.ash:
Code: ags

struct Stats{ //character stats
  int Health;
  int Damage;
  int Defense;
  int Luck;  
};

import Stats personaje[100];


Then I make a module, called Shoot, where I put a lot of things, but the thing that sends me an error is this:
Code: ags
  personaje[this.ID].Health-=personaje[player.ID].Damage-personaje[this.ID].Defense; //how much health is the enemy gonna lose


The error in question is this:
Quote"Shoot.asc(19): Error (line 19): Undefined token 'personaje' "

Apparently it's not a coding error, because it works perfectly as long as I put the Shoot function in the global script instead of its own module.

The full Shoot function, in case it's relevant:
Spoiler
Code: ags

// new module script
//what happens when you shoot someone

function Shoot (this Character*, AudioClip* sonido, int Velocidad2){ //extender function. usage: cCharacter.Shoot(aSound, X), where X is the amount of speed increase in the character's movement after being shot.
//if you have bullets, and you hit your target:
if (Bullets_magazine!=0){
  
  btnAmmo.Width-=20;
  Bullets_magazine--;
  
  Score++;
  Velocidad+=Velocidad2; //increasing the speed of the target after being shot, like in classic NES games.
  
  this.StopMoving();
  //this.Teleport();
  this.SetWalkSpeed(Velocidad, Velocidad);
  this.Walk (walkx, walky);
  
  personaje[this.ID].Health-=personaje[player.ID].Damage-personaje[this.ID].Defense; //how much health is the enemy gonna lose
  
  
  //String HPenemigotxt = String.Format("HP %s: %d", this.Name, Health[this.ID]);
  String HPenemigotxt = String.Format("HP %s: %d", this.Name, personaje[this.ID].Health);
  lblHPenemigo.Text=HPenemigotxt;
  aGunshot.Play();
  sonido.PlayQueued();
}  
else{ //if you have an empty clip
  aEmptygun.Play();}
}
[close]

Crimson Wizard

Regarding Min and Max, these were suggested to be added to Maths long ago. But you may use this module code that creates extension for Maths:

Script header:
Code: ags

import int Max(static Maths, int a, int b);
import int Min(static Maths, int a, int b);


Script body:
Code: ags

static int Maths::Max(int a, int b)
{
  if (a >= b)
    return a;
  return b;
}

static int Maths::Min(int a, int b)
{
  if (a <= b)
    return a;
  return b;
}

Snarky

The problem with the personaje[] array is that AGS only reads the code from top to bottom, and the Shoot module is above GlobalScript (all modules are always above GlobalScript). So when you try to use it in that script, AGS hasn't got to the line where you declare it yet, and therefore doesn't know what you're talking about.

The solution is simply to move the declaration and export of personaje[] to the Shoot script, and the Stats struct declaration and import statement to its header. (Or, if you don't think it logically belongs in the same module, you can create a separate module, move it above Shoot, and place the code there.)

Egmundo Huevoz

I already knew about the reading order, but I thought that by just importing it in the global script header was enough. I did as you said, and it worked perfectly, thanks a lot! (laugh) I'm going on a trip in a few hours, it's nice to go with a working code, lol. Happy new year!

SMF spam blocked by CleanTalk