Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » RPG game help

This thread is locked; no one can reply to it. rss feed Print
RPG game help
Antone333
Member #15,545
March 2014

ok so i am making an RPG game. I am in the process of rewriting the ENTIRE game using allegro and a simple tile set that i have made myself.

but every time i as for help people always tell me one of two things with my program.

1:
they tell me that i need to use structures for my inventory/banking system

2:
they tell me to take the map arrays out of my program and put them in their own separate files

I know more or less how to use a struct but i have no idea how to use it for the purpose that i want.

People dont have to tell me that this is ugly I realize its a terrible god aweful way to make an inventory/banking system. but it was what i knew how to do.
believe me i have worked on this for a very long time and I dont know how to change it to be more efficient. so could someone please help me out.

i know most people hate it when people ask for exactly how to do it. but that is pretty much what im asking for at this point, but please explain it to me in great detail if you can.

You can find the function that actually allows you to mine and get ore at: 1555
you can find the inventory storing at: 801
you can find the ore banking at: 5152

ok my computer is being extremely slow at the moment and wont upload my attachment so you can find it here on google sites:

https://sites.google.com/site/temprpgquestion/go-here-for-the-program

Thank you all for the help in advance. i really appreciate it.

so i am working on the menu screen right now and this is what i have so far

OnlineCop
Member #7,919
October 2006
avatar

My first suggestion is to break your file apart. Don't EVER be afraid of splitting a single large file into smaller parts.

  • It makes the individual parts easier to read.

  • It lets you know when things are too tightly coupled (things that rely on each other "too much").

  • It helps you avoid distraction.

My second suggestion: Instead of telling us the line numbers you need advice with, specify the name of the function you need help on.

Line 1555: void Start_Mining()
Line 801: void Items_List()
Line 5152: (no method; just an if() block)

My third suggestion: Within your Bank_Action() function, you have long if() blocks. Break those out into their own functions. I'd love to see this 3300-line function broken down to something a little more manageable:

#SelectExpand
1void Bank_Action() 2{ 3 cout << endl << "You have: " << bank << " free bank spaces" << endl; 4 5 Show_Items_In_Storage(); 6 7 cout << endl << "Would you like to withdraw or deposite?"; 8 cout << endl << "1 deposite, 2 withdraw" << endl; 9 cin >> choice; 10 if(choice == 1) 11 { 12 Bank_Prompt_To_Deposit(); 13 } 14 else if(choice == 2) 15 { 16 Bank_Prompt_To_Withdraw(); 17 } 18}

Now within Bank_Prompt_To_Deposit(), you have its own if() blocks:

#SelectExpand
1void Bank_Prompt_To_Deposit() 2{ 3 cout << endl << "What would you like to deposite?" << endl; 4 cout << "1 ore, 2 fish, 3 wood, 4 cooked fish" << endl; 5 cin >> choice; 6 if(choice == 1) 7 { 8 Bank_Prompt_To_Deposit_Ore(); 9 } 10 else if(choice == 2) 11 { 12 Bank_Prompt_To_Deposit_Raw_Fish(); 13 } 14 else if(choice == 3) 15 { 16 Bank_Prompt_To_Deposit_Wood(); 17 } 18 else if(choice == 4) 19 { 20 Bank_Prompt_To_Deposit_Cooked_Fish(); 21 } 22}

You'd do the same for all the rest of the stuff.

Last of all, I'm not exactly sure what question you're asking.

Antone333 said:

...please explain it to me in great detail if you can...

What did you want explained?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Something that would shorten your level select code would be a function pointer array. Then all you would need to do is index the array to run the function to select the level.

Example :

#SelectExpand
1void SelectLevel1(); 2void SelectLevel2(); 3void SelectLevel3(); 4 5typedef void (*LEVEL_SELECTOR_FUNC)() 6 7LEVEL_SELCTOR_FUNC level_selector_array[] = { 8 SelectLevel1 , SelectLevel2 , SelectLevel3 9}; 10 11int level = GetUserLevelInput(); 12// run level function 13*level_selector_array[level](); 14 15 16void SelectLevel1() { 17 // whatever 18}

But what you really want is probably to load each level from a file. nvm, can't read

Antone333
Member #15,545
March 2014

@OnlineCop

Wow. yeah i did not think about that. It would take care of not knowing how to use structs. i would still have to use that big long ugly list everything out but it would be so much easier just to list it once in its own function. thanks a bunch.

OnlineCop
Member #7,919
October 2006
avatar

I think that two things will help this code: the use of struct or class, and replacing redundant code with functions. Many things can be simplified, like when you have text that is identical in all places except maybe the monster name or the amount of damage taken.

One example is void Monster_1():

#SelectExpand
1 int Monster = rand()%8+1; 2 3 int Monster_Health = rand()%5+5; 4 int Monster_Attack = rand()%5+1; 5 int Monster_Defence = rand()%5+1; 6 7 if(Monster == 1) 8 { 9 //system("cls"); 10 cout << "A Rat has emerged." << endl << endl; 11 pause(2); 12 cout << "Enemy: " << endl << "Health: " << Monster_Health << endl << "Attack: " << Monster_Attack << endl << "Defence: " << Monster_Defence << endl << endl; 13 cout << "You: " << endl << "Health: " << Health << endl << "Attack: " << Attack_Level << endl << "Defence: " << Defence_Level << endl << endl; 14 pause(2); 15 cout << "You ready your weapon in the: 1 Defencive Position, 2 Offencive Position." << endl; 16 cin >> Monster; 17 if(Monster == 1) 18 { 19 cout << "You ready your weapon for the Rat to attack." << endl << endl; 20 cout << "The rat with its sharp fengs jumps at you. "; 21 Monster = Monster_Attack - Defence_Level; 22 cout << "It deals " << Monster << " damage."; 23 } 24 } 25 else if(Monster == 2) 26 { 27 // ... 28 } 29 else if(Monster == 3) 30 { 31 // ... 32 }

You can convert this into a simple function that you can reuse multiple times:

#SelectExpand
1int Fight_Monster( 2 const string &monster_name, 3 const string &monster_attack_type, 4 const int &monster_health, 5 const int &monster_attack, 6 const int &monster_defence, 7 const int &health, 8 const int &attack_level, 9 const int &defence_level 10) 11{ 12 cout << "A " << monster_name << " has emerged." << endl << endl; 13 14 cout << "Enemy: " << endl 15 << "Health: " << monster_health << endl 16 << "Attack: " << monster_attack << endl 17 << "Defence: " << monster_defence << endl 18 << endl; 19 20 cout << "You: " << endl 21 << "Health: " << health << endl 22 << "Attack: " << attack_level << endl 23 << "Defence: " << defence_level << endl 24 << endl; 25 26 cout << "You ready your weapon in the: 1 Defencive Position, 2 Offencive Position." << endl; 27 28 int AttackOrDefendResponse; 29 cin >> AttackOrDefendResponse; 30 31 int DamageTaken = 0; 32 if(AttackOrDefendResponse == 1) 33 { 34 cout << "You ready your weapon for the " << monster_name << " to attack." << endl << endl; 35 cout << monster_attack_type << endl; 36 DamageTaken = monster_attack - defence_level; 37 cout << "It deals " << DamageTaken << " damage."; 38 } 39 40 return DamageTaken; 41} 42 43void Monster_1() 44{ 45 srand(time(0)); 46 47 int Monster = rand()%8+1; 48 49 int Monster_Health = rand()%5+5; 50 int Monster_Attack = rand()%5+1; 51 int Monster_Defence = rand()%5+1; 52 53 int DamageTaken = 0; 54 if(Monster == 1) 55 { 56 DamageTaken = Fight_Monster("Rat", "The rat with its sharp fengs jumps at you.", 57 Monster_Health, Monster_Attack, Monster_Defence, 58 Health, Attack_Level, Defence_Level 59 ); 60 } 61 else if(Monster == 2) 62 { 63 DamageTaken = Fight_Monster("Spider", "The spider with its long legs and poisonous bite jumps at you.", 64 Monster_Health, Monster_Attack, Monster_Defence, 65 Health, Attack_Level, Defence_Level 66 ); 67 } 68 else if(Monster == 3) 69 { 70 DamageTaken = Fight_Monster("Snake", "The spider with its venomous fangs lunges at you.", 71 Monster_Health, Monster_Attack, Monster_Defence, 72 Health, Attack_Level, Defence_Level 73 ); 74 } 75 // ...and so on... 76 77 // Do something with 'DamageTaken'... 78}

As you can see, the text is almost the same for each battle, the logic is nearly the same, and you can simply pass in parameters to change the parts that should differ.

Next, use of struct or class. For C++, they are identical except that the former makes everything public unless you specify otherwise, and the latter makes everything private unless you specify (respectively).

Two perfect candidates for a struct are monsters and the player.

#SelectExpand
1struct S_Player 2{ 3 string Name; 4 5 int Health; 6 int Health_Level; 7 int Health_Skill; 8 int Health_Skill_M; 9 10 int Attack_Level; 11 int Attack_Skill; 12 int Attack_Skill_M; 13 14 int Defence_Level; 15 int Defence_Skill; 16 int Defence_Skill_M; 17 18 int Fishing_Level; 19 int Fishing_Skill; 20 int Fishing_Skill_M; 21 22 int Woodcutting_Level; 23 int Woodcutting_Skill; 24 int Woodcutting_Skill_M; 25 26 int Mining_Level; 27 int Mining_Skill; 28 int Mining_Skill_M; 29 30 int Firemaking_Level; 31 int Firemaking_Skill; 32 int Firemaking_Skill_M; 33 34 int Cooking_Level; 35 int Cooking_Skill; 36 int Cooking_Skill_M; 37};

This is the general structure of the Player. It doesn't contain any data (yet). It just specifies that anything that gets defined as a Player will have a Name, will have Health and Attack/Defence attributes, and so forth.

You create an instance of the player (which will contain values for all the attributes) like:

S_Player player;

Now, you pass around the player and it's much easier to understand whether "Health" belongs to monster.Health or player.Health.

If you're struggling to understand any of these, ask.

Antone333
Member #15,545
March 2014

Yeah I get both of those just fine and will for sure be adding them into my current remaking of the project. Someone along the line of that console based game told me that I should make the inventory into I think they said boolean structure. And I was like... what? And so I didn't implant that. I tried looking it up and stuff but I just could not wrap my mind around any way to take a structure such as tuna fish; and tuna.amount; and use that in my inventory system any different then how I am already using my inventory system.couldn't quite figure out how a boolean would go in there to make it different.

So where I have those big long if statements where I list everysingle item in the game and repeat for every single item in the game.
am I able to pass something like this:

If(tuna + Items list <= BankSpace)
store the item of so much amount;

I'm on my phone at the moment because I couldn't sleep so I don't really remember how that one went exactly. But is that possible to pass all the items through a function like that in an if statement? so I don't have to type so much? Because that was by far the worst part of typing that code and why I stopped working on it where i did.

Thanks a lot for all the help you guys.

OnlineCop
Member #7,919
October 2006
avatar

Yes, it's actually a good idea to pass things through functions for that reason.

I suggest that you create an enumeration:

#SelectExpand
1enum Ores 2{ 3 Copper_Ore, 4 Tin_Ore, 5 Blurite_Ore, 6 Iron_Ore, 7 Silver_Ore, 8 Elemental_Ore, 9 Gold_Ore, 10 Mithril_Ore, 11 Adamantite_Ore, 12 Runite_Ore, 13 Magic_Ore, 14 Cursed_Ore, 15 Blood_Ore, 16 Dragon_Ore, 17 Crystal_Ore, 18 19 Number_Of_Ores // 'Number' enum entries always last 20};

Then you create an array:

  int All_Ores[Number_Of_Ores];

Now instead of keeping track of individual ores, you can specify exactly ONE array, index any of the individual ores whenever you want, and counting up the total ores is as simple as iterating over the array:

  int Total_Ore_Count = 0;
  for (int i = 0; i < Number_Of_Ores; i++)
  {
    Total_Ore_Count += All_Ores[i];
  }
  cout << "You have " << Total_Ore_Count << " ores in your inventory.";

You're also able to update only the ore you care about:

  All_Ores[Mithril_Ore] += 5;

If you do that with all your other items as well (one for fish types, one for wood types), then you can get rid of that long if() block and condense it down to only a handful of lines.

Antone333
Member #15,545
March 2014

Thanks a bunch. i more or less get how to use enum but i think i could use that one.

can i have an enum within an enum

for example

enum AllItems
{
enum AllOres
{
copper, tin, so on, number of ore
};
enum AllFish
{
Trout, tuna, so on, number of fish
};
number of total items
};
/
/
/
/
/
and also could you please help me figure this out?
what i am doing is simply just getting the map to change by changing the Level X and Y value in the code itself. but when i change it like this:
from
x and y = 0
to
x = 1
y = 0

nothing happens. it still only displays the first map. but when i get rid of the else and have a double if statement then the opposite happens. only the second map is drawn to the screen.

i am doing basically the same type of code i used for my character movement in the console to move between the maps. and will have a copy of the map arrays that is left blank except for the added in character and npc. this way i can manipulate on top of the drawn map without having to redraw it. and also the only way i know how to do collision detection.

void LevelSelect()
{

int LevelSelectArray[10][10] = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30},
{31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
{41, 42, 43, 44, 45, 46, 47, 48, 49, 50},
{51, 52, 53, 54, 55, 56, 57, 58, 59, 60},
{61, 62, 63, 64, 65, 66, 67, 68, 69, 70},
{71, 72, 73, 74, 75, 76, 77, 78, 79, 80},
{81, 82, 83, 84, 85, 86, 87, 88, 89, 90},
{91, 92, 93, 94, 95, 96, 97, 98, 99, 100}
};

int LevelX;
int LevelY;

bool FirstTime = true;

if(FirstTime)
{
LevelX = 0;
LevelY = 0;
FirstTime = false;
}

if(ArrayLevelSelect[LevelX][LevelY] = 1)
{
Level1();
}
else if(ArrayLevelSelect[LevelX][LevelY] = 2)
{
Level2();
}
}
//for the example the only difference is the 5 and the 10
void Level1()
{
int Level[5][5] = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 5, 1},
{1, 1, 1, 1, 1}
};
memcpy(Map, Level, sizeof(Level));
}
void Level2()
{
int Level[5][5] = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 10, 1},
{1, 1, 1, 1, 1}
};
memcpy(Map, Level, sizeof(Level));
}

Thomas Fjellstrom
Member #476
June 2000
avatar

Nope. Enums are not nestable.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Antone333
Member #15,545
March 2014

ok so they are not nestable. but what if i did something like this?

enum all ore
{
copper, number of ores
}

enum all fish
{
tuna, number of fish
}

enum all items
{
Number of ores, number of fish
}

Thomas Fjellstrom
Member #476
June 2000
avatar

You can probably do that. though the last enum may not be needed (I'm not sure of the purpose for that).

Just note that each enum starts from 0 if you don't give it a starting value.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

OnlineCop
Member #7,919
October 2006
avatar

The last enum is not needed: correct. HOWEVER, it just "happens" to be the total number of all the items in the enum, so you don't have to try to determine the enum's size at runtime (or remember all the places that you hardcoded the size) in case you need to add any more enum values.

Logically, you want to separate your "fish" from your "ores". Wood doesn't feed you, so won't make sense to have inside of a massive enum or array that contains food-based items. In the same vein, weapons aren't armor.

It makes a LOT more sense to me to have multiple for() loops, one after the other, than to have some massive array of "everything in the game". My mind just doesn't work like that.

Antone333 said:

if(ArrayLevelSelect[LevelX][LevelY] = 1)

You are assigning the value of 1 into ArrayLevelSelect[LevelX][LevelY] here. You probably meant == (equality).

Next, onto the subject of your maps.

I have always been of the mindset that all levels in a map should potentially be different sizes: the memory can be allocated when you need it, and freed when you're done with it.

Some maps are the "interiors" of a large house, so they can be smaller. Other maps are vast oceans with a special hidden island right in the middle of them, and can be much larger.

When you create a level, there should be a class that is specifically dedicated to all aspects of its generation and tear-down. You should pass in the level number you want loaded, and get a level back. You should pretend that you have NO idea what's going on "behind the curtain" when you request a level from it.

This class might have the array in memory. It might load it in from a file. It might parse a Tiled-generated XML file. That's the point: it should be a mystery to everything else so you can change it at any time in the future, and as long as "give me level X" always returns a predictable map structure, how it works should always be a black box.

Last of all, DON'T (did you see all the emphasis I put there?) create function calls for levels and things that "will come". Don't copy-and-paste 70 "placeholder" levels when you really only have about 7-8 truly defined. Don't do placeholder fish/wood/weapons/armor. Don't create "dummy" monsters that you'll revisit "someday".

It might make you much less overwhelmed if you drop your file down from the nearly 10,000 lines to about 5,000. Strip away ALL pre-"defined" stuff: get rid of levels 9-70. Get rid of all enemies but two: the rat, and maybe the spider. Get rid of all types of fish. You can leave some of the fish/wood/monster "types" in comments somewhere so you remember them later, but get them out of your current codebase.

Once you have all that, post your new source file results, and we'll move on to breaking them apart into individual files.

Your Monsters should be in one or more files (either a massive monster.cpp file, or individual monster_rat.cpp/monster_spider.cpp files). Your Items should be in one or more files (items.cpp or item_wood.cpp/item_fish.cpp, etc.). Etcetera, etcetera.

Thomas Fjellstrom
Member #476
June 2000
avatar

OnlineCop, while I agree that separating the item types could be more logical, its a lot more work to manage. Using a single item namespace allows for more efficient loading and saving as you don't have to figure out the category of the item first, and no item ids will ever overlap.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

pkrcel
Member #14,001
February 2012

I think that the superfluous enum in Tomasu view was this:

Antone333 said:

enum all items
{
Number of ores, number of fish
}

The "last item in an enum is the one that enumerates the enum itself" is quite handy :P

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

LennyLen
Member #5,313
December 2004
avatar

pkrcel said:

I think that the superfluous enum in Tomasu view was this:

Not only is it superfluous, it also won't work as intended, since it will give the number of ores as 0, the number of fish as 1, etc...

pkrcel
Member #14,001
February 2012

Well in case it is strongly typed I guess ti would be only superfluous, but of course in this case......er now that I come to think of it, would it be even possible?

The names should clash as multiple definition? (too rusty to remember >:()

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

OnlineCop
Member #7,919
October 2006
avatar

Thomas, while I agree with this, I'm trying to work with his code on his programming level.

If I were talking to LennyLen, Arthur or Trent, I'd be suggesting more advanced methods. I feel that these topics wouldn't be appropriate for where he's at now, so I'm doing my best to suggest things that will help him get this project far enough along that he can see some significant success.

Thomas Fjellstrom
Member #476
June 2000
avatar

OnlineCop said:

Thomas, while I agree with this, I'm trying to work with his code on his programming level.

I honestly think throwing more code at the problem just makes it harder :P an item is an item, no matter the category. I think that is pretty darn simple ;)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Antone333
Member #15,545
March 2014

@OnlineCop

Oh. wow. duh. haha == i know that. thats a silly mistake.

ok so my maps work well now and i have loaded 2 in.
I have also drawn in my player in a layer on top of the tilemap like this:

1 wall 2 grass 0 player

11111
12221
12221
12221
11111

11111
10221
12221
12221
11111

in the second array the only thing that gets drawn is the player and everything else is blank. i did this so the player can move freely ontop of the map without changing the map itself.

so a couple of things.

first of all, I know i am going to get crap for having all those huge arrays in my program itself. i get it. they should be in different files. and i am also seeing the result of it when the game is loading in the maps. it takes a minute. and this is only 2 maps!

but i dont know how to use an array in a txt file like that. i have tried but i only know how to do simple variables and store them in an array using fstream. so if someone could help me out with that i would be grateful.

and also, i am trying to figure out how to at this time make the player move. I am going for the same type of movement i have in my console game that i will repost. but i am pretty sure i will have to make the game redraw every time and i thought i had that working but it doesnt work so i will post my current project aswell

and a final question comes when i create my inventory equipment and my incomplete date time thing. when you create them by pressing the "buttons" on the toolbar display it takes a couple of clicks before the new display shows up. and at line 373 of Game() i thought that would close the window. but for some reason it doesnt and i dont know why.
/
/
/
/
/
for the console game go to Line 1164 GameMechanics() to see how the player moves when down is pressed

for the Allegro 5 game go to line 470 PlayerMovement() to see how i thought the player should move when down is pressed.
just above there at 444 of Game() is where i draw the two tilemaps
and PlayerMovement() is passed through Game() at line 321
/
/
/
/
/
/
Thank you all so much for the help!

Go to: