Original Post

I’ve decided that since I have absolutely no experience whatsoever playing RPGs, that I am not a good candidate of programming one, so instead, I’m going to work on a fighting game (while I also have no experience playing a fighting game, I’d figure it’d be a lot easier programming one than an RPG.) So here’s my latest brainchild: Insect Combat. Right now, it’s just a warning screen and a title screen. The only two characters I’ve thought of so far are Gi-Ant and Behe-Moth. Any other punny names you guys can think of would also be appreciated.

Attachments:
399 Replies

Oh, also, HorvatM actually mentioned WaitForVIP (twice, but not really what it does. That was exactly the solution you needed, though.

The six VB demo games also use vbWaitFrame(0), which does pretty much the same thing. I’m kind of surprised you didn’t find this earlier – maybe because it’s the VB with its limited processing power. On a PC you would have found out pretty much immediately that your game runs at a limitless FPS, which can’t just be solved by saying “slow it down with this counter please”. Especially since PC processors are all different speeds. πŸ™‚

Edit: sorry all the tabs turned into double spaces, by the way. Hope you can live with that. 😑

  • This reply was modified 9 years, 6 months ago by DaVince.

I can’t figure out how to solve the problem that is posed by using the same code twice in fighter selection code. I tried and can’t come up with anything.
The compiler program doesn’t like it when you call ints in voids. It would rather you form all ints, unsigned chars, etc. before main.

I see, that’s interesting, because there’s no need at all for those functions to be ints. They don’t return an integer value…. They don’t return any value at all (which is why I changed the functions to type void in the first place).

…I just tried declaring the voids before int main, and it’s not complaining at all?

void initgame();
void preintro();
void intropart();
void titlescreenpart();
void charselect();
void prefight();
void fightscreen();

Don’t forget to use the right type, I guess? If your functions are all voids, it’d be weird to declare them as ints at the beginning of your code…

  • This reply was modified 9 years, 6 months ago by DaVince.

VirtualChris wrote:
I can’t figure out how to solve the problem that is posed by using the same code twice in fighter selection code. I tried and can’t come up with anything.

The key thing is the variables “opponent” and “fighter”.

The thing is, the variables “opponent” and “fighter” really only contain a number that represents the current fighter and opponent throughout the game. That’s completely fine. The main thing we need to do is to detach those from the actual character select function.

Why? Well, it’s because we want this one function to be able to handle multiple different situations. One has everything to do with the fighter, and nothing to do with the opponent. The other one has everything to do with the opponent, and nothing to do with the fighter. So that’s why we need to make the functionality itself more generic – usable over more situations.

Now, the best way to do that is by replacing any occurrence of fighter in your character select code with a different variable. For example, a local variable called selected_character:

//inside the selection screen function:
int selected_character = 1;

Next, you make the function charselect return this value. So the void charselect() we have right now becomes this:

int charselect() {
  int selected_character = 1;

  //...All the character selection code that determines the
  //final value of selected_character...

  //And at the very end, when the function should be closed:
  return selected_character;
}

int charselect() indicates we’re going to make the function charselect return a number to us. And “return selected_character” actually gives us that number.

Now you call the function charselect() twice and store the result of the character select screen into the appropriate variables:

fighter = charselect();  //The value that is returned will be stored in fighter
opponent = charselect();  //Same, except it's stored in opponent

Of course you don’t run that code in charselect() itself; that would make it open itself infinitely recursively. You could put it in main():

case STATE_PREFIGHT:
  fighter = charselect();
  opponent = charselect();
  prefight();
break;

And that’s the basics of that. And the cool thing is, if you ever have even more characters on the screen, it’s a cinch to just add a third variable that calls characterselect(). πŸ™‚

Now the only thing left to do is ensure that the character you selected isn’t selectable again. You can do that by adding an argument to the function declaration:

int charselect(int hidecharacter) {
  //In your code, use the value of int hidecharacter to hide 
  //the specific character you don't want to appear in the list
}

And during the function call:

fighter = charselect(-1);  //All bugs to be visible, so any value outside 1-8 works
opponent = charselect(fighter); //We want the character the fighter selected to not be visible

PS. It’s a good idea to also give variables like changefighter, fighterxsight and fightery more generic names, like changecharacter, characterxsight, charactery. And make those local variables (that is, variables declared inside the function) since that function is the only place they’ll be used.

Despite the lot of stupid, needless warnings, I have finally got the thing to work OK after three or so hours of painful trial and error. Most of the work came from the fact that I don’t want the fighter and opponent to be the same insect. So that took a whole lot of time to sort that out. So I think I did, and if you see any weird things on the character selection screens, please tell me. You can get the latest ROM here:
http://www.atari2600land.com/insecticide/

Nice work, glad it functions. I still have suggestions though! πŸ™‚

Most of the work came from the fact that I don’t want the fighter and opponent to be the same insect. So that took a whole lot of time to sort that out.

Well, about that, I actually explained how you can solve that by using function arguments:

int charselect(int hidecharacter) {
  //In your code, use the value of int hidecharacter to hide
  //the specific character you don't want to appear in the list
}

And during the function call:

fighter = charselect(-1);  //All bugs visible
opponent = charselect(fighter); //The bug the player selected is not visible

This way, you can get rid of the remaining use of the “fighter” variable in the function (like if ((fighter==showchar)) showchar++;).

You can even give the function several arguments, for example to set the default character to show:

//The charselect function:
int charselect(int startingcharacter, int hidecharacter) {
  showchar = startingcharacter;
}


//Inside main():
fighter = charselect(1, -1);
//1 means start by showing bug 1,
//-1 means don't hide any from the possible options

opponent = charselect(fighter+1, fighter)
//fighter+1 means start by showing the bug right after the one the player selected,
//fighter means hide the bug the player selected from the possible options

Function arguments are super useful and will save you a few headaches, I think. πŸ™‚

VirtualChris wrote:
Despite the lot of stupid, needless warnings

Did my suggestion above work to fix these? Over here they did. You just put that stuff right above int main(). (Oh, and void charselect(); has become int charselect(int startingcharacter, int hidecharacter); if you implement my above code).

OK, I did what you told me to and the warnings disappeared. So now what I want to do is try to make AI for this game. I tried to before (using the variable opponentmovetimer) but it didn’t work out very well. Here is what I did with that variable:

      if (opponentmovetimer==3) opppausepunch=0, animframe7=1, dontflip2=0, opppunch=0, oppjump=0, opponentspecial=1;
      if (opponentmovetimer==2) opppausepunch=0, animframe7=1, dontflip2=0, oppjump=0, opponentspecial=0, opppunch=1;
      if (opponentmovetimer==1) opppausepunch=0, animframe7=1, dontflip2=0, opppunch=0, opponentspecial=0, oppjump=1;

This basically says that if opponentmove timer=3 then do the special move. If it’s 2 then punch and if it’s 1 then jump. Somewhere else in the code it says if it’s greater than three go back to one. I have opponentmovetimer to increase by one and then increase once more if L or R on the left directional pad is pressed. Does anyone have any suggestions on this?

The way you have set it up, it sounds like the pattern would be very predictable: press L or R, and you will most guaranteed see the enemy jump, then punch, then try a special.

Here’s my tip: randomize it, and randomize it based on a timer (that counts up by 1 each frame) and your character’s proximity to the opponent. Actually, have two randomized timers: one determining the enemy’s movement, and the other their actions.

For the movement timer:
Once it reaches its max value, it is time to perform a movement action. Then do a check how close/far the opponent is from the player.
– If they’re further than, say, 200 pixels away, then always move the enemy closer by, say, 100 pixels.
– If they’re between 100 and 200 pixels, randomly decide if they should be moving a random amount forward, do nothing, or move backward a tiny bit (you can influence the chance this happens by comparing their HP, too).
– If they’re closer than that, randomly decide whether they should move forward a random amount, do nothing, move backward a (larger) random amount, or jump.

For the action timer:
This timer needs to be a little bit shorter (so moves are attempted more often), but still at random intervals. When it’s time to decide on an action:

– Check whether the player is in, say, a 50 px proximity. If they are, attempt one of the moves (or do nothing). This way, might try to attack even when it’s not close enough this way, but it’ll just look as if the enemy attempted and failed at an attack, which gives you, the player, a chance to retaliate.

Outside of both:
– If the enemy is jumping and near you, it could randomly try to do its special move right afterward.

You can add to this, and add more conditions that specifically change the behavior based on which bug it is, how aggressive/cowardly they are, if they’re almost out of HP, etc.

The issue
Now, there is one big problem. All of the logic above relies on randomization. That is, the kind of logic that looks like this:

int random_number = (select a random number between 0 and 10);
if (random_number < 5) {
  //Perform action of type 1
}
else {
  //Perform action of type 2
}

The big problem is that the VB doesn't seem to have a function to create such a random number! There's no rand() function (like in traditional C) to rely on to give you a random number. That's going to complicate things. I've found a potential random number generator you could use but I need to test it first. In the meantime, think about the fighting logic like the above a bit more, try to expand on it, and carefully consider how you would start implementing it. πŸ˜‰

Edit: and now we have a solution. πŸ™‚
http://www.planetvb.com/modules/newbb/viewtopic.php?topic_id=3723&post_id=30500#forumpost30500

  • This reply was modified 9 years, 6 months ago by DaVince.

I downloaded the random number function and it is working nicely. I also changed a few things to try and get rid of the flashing stuff between screen changes. You can always download the most current version here:
http://www.atari2600land.com/insecticide/

I wish this site would let me have a signature so I don’t have to do that every few pages.

I tried to add punching sound effects but they wouldn’t play! The Virtual Boy does have more than 2 sound channels, right? What am I doing wrong?

    if (punch==1) punchtimer2++;
	if ((punch==1) && (punchtimer2==1) && (punchtimer==1)) PlaySound(youpunch);
    if ((punch==1) && (punchtimer2>2)) punchtimer2=0, punchtimer++;
    if ((punch==1) && (fighterxsight>opponentxminus) && (fighterxsight4)) punch=0, punchtimer=0, opponenthitcounter++;
    if (opponenthitcounter==2) opponenthitcounter=0, endhealthbaropponent--, vbTextOut(3, endhealthbaropponent-1, 1, " ");

I guess the Soviet sound engine allows for two channels only. I wanted to put in sfx for both characters, and the VB itself can handle 4 sounds, but I guess it’s not possible. Nothing much added, I did add the ability to block with the shoulder buttons. But I’m stuck on what to do next. Any suggestions on what you want to see in the game?

The engine should be able to play up to 5 sounds at once (see kMaxSounds in su11sfx.h). You are probably somehow calling PlaySound on the same sound more than once, which just uses up extra channels for no reason.

I noticed at the very beginning, before anything else is called, it’s supposed to play three notes at the same time and only plays 2.

HorvatM wrote:
The engine should be able to play up to 5 sounds at once (see kMaxSounds in su11sfx.h). You are probably somehow calling PlaySound on the same sound more than once, which just uses up extra channels for no reason.

If I change that number to 8, it allows a third note to be played.

VirtualChris wrote:

    if (punch==1) punchtimer2++;
	if ((punch==1) && (punchtimer2==1) && (punchtimer==1)) PlaySound(youpunch);
    if ((punch==1) && (punchtimer2>2)) punchtimer2=0, punchtimer++;
    if ((punch==1) && (fighterxsight>opponentxminus) && (fighterxsight4)) punch=0, punchtimer=0, opponenthitcounter++;
    if (opponenthitcounter==2) opponenthitcounter=0, endhealthbaropponent--, vbTextOut(3, endhealthbaropponent-1, 1, " ");
if (punch == 1)
{
  punchtimer2++;
  if ((punchtimer2 == 1) && (punchtimer == 1))
    PlaySound(youpunch);
  else if (punchtimer2 > 2)
  {
    punchtimer2 = 0;
    punchtimer++;
  }
  if ((fighterxsight > opponentxminus) && (fighterxsight < opponentxplus) && (punchtimer > 4))
  {
    punch = 0;
    punchtimer = 0;
    opponenthitcounter++;
  }
}

if (opponenthitcounter == 2)
{
  opponenthitcounter = 0;
  endhealthbaropponent--;
  vbTextOut(3, endhealthbaropponent-1, 1, " ");
}

Now, isn’t that much easier to read (and, thus, maintain)? It also runs faster, because the “punch == 1” test is only performed once.
Assuming the ‘{‘ and ‘}’ keys on your keyboard work, you should start giving them something to do πŸ˜‰
Give the semicolon and enter keys some love, too, and we might just make a C programmer out of you, yet! :thumpup:

Work on this began several years ago when I was just getting into C and learning about it. That’s why much of the code is (apparently) unreadable and looks the way it does. I don’t want to change it because it works and I may break something if I attempt to. Since work begun on this, I have learned stuff about C I would have never known. I want to work on this project again, but I don’t know what to do next. Does anyone have any suggestions on what they would like to see in this game? I doubt I will get it finished in time for August 2015, but perhaps August 2020 for the 25th anniversary? I would have liked this to be my tribute to the VB, making the ultimate best possible game I could possibly ever make for any system I know how to code on.

VirtualChris wrote:
Work on this began several years ago when I was just getting into C and learning about it. That’s why much of the code is (apparently) unreadable and looks the way it does. I don’t want to change it because it works and I may break something if I attempt to. Since work begun on this, I have learned stuff about C I would have never known. I want to work on this project again, but I don’t know what to do next.

The one thing I can suggest from experience: don’t do a rewrite. Slowly go through your code that already works and massage it into a more readable form, making sure that you don’t remove the working logic before ensuring that your changes are functionally equivalent.

See: Netscape for what happens when you DO do a rewrite :).

That said, the commas (manual sequence points) as a substitute for semicolon (statement termination) in the original second-to-last line do amuse me for some reason.

1. Being a beginner is nothing to be ashamed of, but staying one, for fear of “breaking things”, most certainly is!
2. What cr1901 said (except the comma part; that’s not amusing at all :-P).
3. You don’t have to change it, I did it for you. You only have to copy/paste.
4. If you keep your old code in there as a comment, you can compare the two while you implement #2 above.
5. If mine does “break something” (which it won’t*), you can go back to your commented version (and please also post a reply/correction, so I can improve my programming skills).

*it’s not bragging if it’s true πŸ˜‰

RunnerPack wrote:
4. If you keep your old code in there as a comment, you can compare the two while you implement #2 above.

subversion systems are great to have a histroy of your code. Basically, you commit your code to the subversion every time you add something and have a working state. The system keeps track of your changes and you can always go back to any version. You can even see what you changed when which makes tracking a bug back to it’s cause very easy.

If you want something easy for yourself svn will do the job.
You can use WinSVN for the server and tortoisesvn as a client. If you want to have many branches and coordinate your work with others git is the way to go.

I was working on this again. My FlashBoy was acting a little wonky, I don’t know why. The Virtual Boy would power off a few times in the middle of a fight. It wasn’t a battery issue as I had it plugged into the wall. But after I altered the code a little, it worked a little bit better after I loaded the program on it. I don’t know if it’s my program, the FlashBoy, the Virtual Boy, or all three. Anyway, if you want to try out the newest version for yourself (please do especially if you have a FlashBoy), at http://www.atari2600land.com/insecticide/

 

Write a reply

You must be logged in to reply to this topic.