Author Topic: Help with custom function regarding reloading bullets into a gun  (Read 355 times)

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: Adventure Game Studio
  1. // new module script
  2. function Reload_gun(){ //important: every bullet is 20 px wide in the GUI, which is the origin of the 20's seen below.
  3.  
  4. Empty_ammo=Maxammo-(btnAmmo.Width/20); //global var representing bullets missing from the magazine, i.e fired bullets
  5.  
  6.   if ((btnAmmo.Width!=Maxammo*20)&&(Bullets>0)){ //don't do anything if magazine is full or you don't have bullets
  7.     if (Empty_ammo>Bullets){
  8.       btnAmmo.Width=btnAmmo.Width+Bullets*20;
  9.       Bullets=0;
  10.     }
  11.     else{ //we have enough bullets to fill the magazine no matter what
  12.       Bullets-=Empty_ammo; //lose the amount of bullets missing (1-6)
  13.       btnAmmo.Width=btnAmmo.Width+Empty_ammo*20; //ammo = ammo left + ammo missing
  14.       Empty_ammo=0;
  15.       if (btnAmmo.Width>Maxammo*20){ //so you can't have more bullets than the size of your magazine
  16.         btnAmmo.Width=Maxammo*20;
  17.       }
  18.     }
  19.     aReloadgun.Play();
  20.   }
  21.   else {
  22.     aEmptygun.Play();
  23.   }
  24. }
  25.  

Gilbert

  • Local Moderator
  • * KILL* * KILL * * KILL *
    • Lifetime Achievement Award Winner
    •  
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

  • Mutated Guano Deviser
    • Best Innovation Award Winner 2009, for his modules and plugins
    •  
    • Kweepa worked on a game that was nominated for an AGS Award!
Agree totally with Gilbot7000.
I would have these globals: CurrentMagazineSize, TotalBullets, BulletsInMagazine.
Code: Adventure Game Studio
  1. int bulletsToLoad = Maths.Min(CurrentMagazineSize - BulletsInMagazine, TotalBullets);
  2. if (bulletsToLoad > 0)
  3. {
  4.   aReloadGun.Play();
  5.   TotalBullets -= bulletsToLoad;
  6.   BulletsInMagazine += bulletsToLoad;
  7.   btnAmmo.Width = 20 * BulletsInMagazine;
  8. }
  9. else
  10. {
  11.   aEmptyGun.Play();
  12. }
  13.  
I don't see where the UI for the total bullets is updated. Perhaps that doesn't update until you fire again?
« Last Edit: 29 Dec 2017, 06:37 by Kweepa »
Still waiting for Purity of the Surf II

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: Adventure Game Studio
  1.   btnAmmo.Width-=20;
  2.   Bullets_magazine--;

In my Reload function:

Code: Adventure Game Studio
  1. function Reload_gun (){
  2. //int Bullets_to_load= Maths.Min(Magazine_size - Bullets_magazine, Bullets_total); ERASED THIS BECAUSE IT DIDN'T WORK
  3.  
  4. if (Bullets_total!=0){
  5.   Bullets_to_load=Magazine_size-Bullets_magazine;
  6.  
  7.   if (Bullets_to_load > 0){
  8.     if (Bullets_to_load>Bullets_total){
  9.       Bullets_magazine=Bullets_magazine+Bullets_total;
  10.       Bullets_total=0;
  11.     }
  12.  
  13.     else
  14.     {
  15.       Bullets_total -= Bullets_to_load;
  16.       Bullets_magazine += Bullets_to_load;
  17.     }
  18.   aReloadgun.Play();
  19.   btnAmmo.Width = 20 * Bullets_magazine;
  20.   }
  21.   else{
  22.     aEmptygun.Play(); // to be replaced with a "can't reload, full gun" sound, or something
  23.   }
  24. }
  25. else
  26. {
  27.   aEmptygun.Play();
  28. }
  29. }

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: Adventure Game Studio
  1. Stats personaje [100];
  2. export personaje;

Then, in Global script.ash:
Code: Adventure Game Studio
  1. struct Stats{ //character stats
  2.   int Health;
  3.   int Damage;
  4.   int Defense;
  5.   int Luck;  
  6. };
  7.  
  8. import Stats personaje[100];
  9.  

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: Adventure Game Studio
  1.   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:
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. // new module script
  2. //what happens when you shoot someone
  3.  
  4. 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.
  5. //if you have bullets, and you hit your target:
  6. if (Bullets_magazine!=0){
  7.  
  8.   btnAmmo.Width-=20;
  9.   Bullets_magazine--;
  10.  
  11.   Score++;
  12.   Velocidad+=Velocidad2; //increasing the speed of the target after being shot, like in classic NES games.
  13.  
  14.   this.StopMoving();
  15.   //this.Teleport();
  16.   this.SetWalkSpeed(Velocidad, Velocidad);
  17.   this.Walk (walkx, walky);
  18.  
  19.   personaje[this.ID].Health-=personaje[player.ID].Damage-personaje[this.ID].Defense; //how much health is the enemy gonna lose
  20.  
  21.  
  22.   //String HPenemigotxt = String.Format("HP %s: %d", this.Name, Health[this.ID]);
  23.   String HPenemigotxt = String.Format("HP %s: %d", this.Name, personaje[this.ID].Health);
  24.   lblHPenemigo.Text=HPenemigotxt;
  25.   aGunshot.Play();
  26.   sonido.PlayQueued();
  27. }  
  28. else{ //if you have an empty clip
  29.   aEmptygun.Play();}
  30. }
« Last Edit: 29 Dec 2017, 20:11 by Egmundo Huevoz »

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
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: Adventure Game Studio
  1. import int Max(static Maths, int a, int b);
  2. import int Min(static Maths, int a, int b);
  3.  

Script body:
Code: Adventure Game Studio
  1. static int Maths::Max(int a, int b)
  2. {
  3.   if (a >= b)
  4.     return a;
  5.   return b;
  6. }
  7.  
  8. static int Maths::Min(int a, int b)
  9. {
  10.   if (a <= b)
  11.     return a;
  12.   return b;
  13. }
  14.  

Snarky

  • Global Moderator
  • Mittens Earl
  • Private Insultant
    • I can help with proof reading
    •  
    • I can help with translating
    •  
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.)
« Last Edit: 29 Dec 2017, 23:06 by Snarky »

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!