My Textbased RPG.

Pages: 12
Take a look at this:

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;

int random(int min, int max);

struct Entity
{
    string m_name;
    int m_health;
    int m_health_max;

    Entity(){}
    Entity(const char * name, int health)
    {
        m_name=name;
        m_health=m_health_max=health;
    }

};

struct Player;

struct Mob: public Entity
{
    int m_dmg_min;
    int m_dmg_max;

    Mob(const char * name, int health, int dmg_min, int dmg_max)
    {
        m_name=name;
        m_health=health;
        m_health_max=health;
        m_dmg_min=dmg_min;
        m_dmg_max=dmg_max;
    }

    void Attack(Player & target);
};

struct Player: public Entity
{
    int m_att;
    int m_def;
    int m_block;
    int m_crit;

    int m_level;
    int m_xp;

    Player(const char * name, int health, int att, int def, int block, int crit)
    {
        m_name=name;
        m_health=health;
        m_health_max=health;
        m_att=att;
        m_def=def;
        m_block=block;
        m_crit=crit;

        m_level=1;
        m_xp=0;
    }

    void Attack(Mob & target);
    void LevelUp();
};

void Battle(Player & player, Mob & mob);

int main()
{
    srand(time(0));

    Player player("player",125,10,5,15,15);

    Mob pirate("Pirate",40,5,10);
    Mob bandit("Bandit",50,10,15);
    Mob wildbear("Wildbear",60,10,20);

    Mob * mob_table[3]={&pirate,&bandit,&wildbear};

    bool fight=true;
    int mob_id;
    char c;

    while (fight)
    {
        mob_id=random(0,2);

        Battle(player,*mob_table[mob_id]);

        system("cls");

        cout << "Fight again? (y/n) ->";
        cin>>c;

        if (c=='n' || c=='N') break;
    }

    system("cls");
    cout << "Thank you for playing!" << endl;
    system("pause");
    return 0;

}

int random(int min, int max)
{
    return rand()%(max-min+1)+min;
}

void Battle(Player & player, Mob & mob)
{
    bool win;

    while (true)
    {
        system("cls");

        if (player.m_health<=0) {win=false; break;}

        cout << player.m_name << " [" << player.m_health << '/'
            << player.m_health_max << ']' << endl;

        cout << mob.m_name << " [" << mob.m_health << '/'
            << mob.m_health_max << "]\n" << endl;

        player.Attack(mob);

        if (mob.m_health<=0) {win=true; break;}

        mob.Attack(player);

        system("pause");
    }

    system("pause");
    system("cls");

    if (win)
    {
        cout << "Congrats!" << endl;

        int xp_gain=mob.m_health_max*(mob.m_dmg_min+mob.m_dmg_max)/20;
        xp_gain+=random(1,5);

        cout << "You defeated " << mob.m_name << " and you got "
            << xp_gain << " xp!" << endl;

        player.m_xp+=xp_gain;

        system("pause");

        player.LevelUp();
    }
    else
    {
        cout << "You were defeated by " << mob.m_name;
        cout << "...\nBetter luck next time..." << endl;

        system("pause");
    }

    mob.m_health=mob.m_health_max;
    player.m_health=player.m_health_max;
}

void Mob::Attack(Player & target)
{
    int block=random(1,100);

    if (block+target.m_block>100)
    {
        cout << "You block " << m_name << "'s attack!" << endl;
    }
    else
    {
        int damage=random(m_dmg_min,m_dmg_max)-target.m_def;

        if (damage<=0) damage=1;

        cout << m_name << " attacks you for " << damage << " damage!" << endl;
        target.m_health-=damage;
    }
}

void Player::Attack(Mob & target)
{
    int dmg_min=m_att;
    int dmg_max=1.5*m_att;

    int attack_type;

    while (true)
    {
        cout << "(1) Attack\n";
        cout << "(2) Thrust\n";
        cout << "(3) Leg Sweep" << endl;
        cin >> attack_type;

        if (attack_type>=1 && attack_type <=3) break;
    }

    int damage=random(dmg_min,dmg_max);

    switch (attack_type)
    {
        case 1:
        cout << "You attack ";
        break;

        case 2:
        damage+=5;
        cout << "You use Thrust on ";
        break;

        case 3:
        damage+=10;
        cout << "You use Leg Sweep on ";
    }

    cout << target.m_name << endl;

    int crit=random(1,100);
    if (crit+m_crit>100)
    {
        cout << "It's a critical hit!" << endl;
        damage*=2;
    }

    cout << "You deal " << damage << " damage!" << endl;

    target.m_health-=damage;
}

void Player::LevelUp()
{
    while (m_xp>10*m_level)
    {
        system("cls");

        cout << "Level Up!!!" << endl;

        int stat;

        while (true)
        {
            cout << "Raise:\n";
            cout << "(1) Health " << m_health_max << "->" << m_health_max+5 << '\n';
            cout << "(2) Attack " << m_att << "->" << m_att+1 << '\n';
            cout << "(3) Defense " << m_def << "->" << m_def+1 << '\n';
            cout << "(4) Block Chance " << m_block << "->" << m_block+1 << '\n';
            cout << "(5) Critical Hit Chance " << m_crit << "->" << m_crit+1 << '\n';
            cin >> stat;

            if (stat>=1 && stat<=5) break;
        }

        switch (stat)
        {
            case 1:
            cout << "Health^^!" << endl;
            m_health_max+=5;
            break;

            case 2:
            cout << "Attack++!" << endl;
            m_att++;
            break;

            case 3:
            cout << "Defense++!" << endl;
            m_def++;
            break;

            case 4:
            cout << "Block++!" << endl;
            m_block++;
            break;

            case 5:
            cout << "Critical++!" << endl;
            m_crit++;
        }

        if (m_block==51)
        {
            m_block=50;
            cout << "Oops! Can't raise block chance more than 50%..." << endl;
            system("pause");
            continue;
        }

        if (m_crit==51)
        {
            m_crit=50;
            cout << "Oops! Can't raise critical hit chance more than 50%..." << endl;
            system("pause");
            continue;
        }

        m_xp-=10*m_level;
        m_level++;

        cout << "You are now level " << m_level << "!!!" << endl;

        system("pause");
    }
}

I like it, very clear and clean on what is going on, which is what I am working on now. I'm reading up on this ncurses library I think I may use it.

Now I am changing the combat around, it was just simple attacks but now I working on adding combos, state effects(if you ever played old school SWG, things like knock downs, dizzy, mostly injuries that effect combat efficiency). I don't believe in levels, but I do believe in a completely customized class system. No more numbers to represent a players skill. So I've adopted a popular leveling method in games, the tree system. Eventually when I am done with this combat program I'll move on to a new project that will be combined with this one, which involves creating your own class. Then of course I'll start a new one after that, an inventory program that I will also combine. I'll do this until I have a full "test" RPG that uses my ideas, some original, some not so original. I think the combo system I am working on now though will be pretty decent, different combos work better on different class combos. Also, each "mob" class will randomly choose its own custom class so every mob will be different, use different weapons, have different health and defense.

Lot of work to do. Thanks for you help, I went from a simple little 90 line program to almost 500 now, all working as intended(maybe a few bugs I can't really see yet).
Sounds promising... Can't wait to test it! :)
So looking at your example kind of made me mad, it's so simple and I was trying to complicate things. First of all, I should have finished the last two chapters of this book I've been learning from, which would be class inheritance and abstract classes. Because now I feel like I need to take what I have learned and rewrite it. I think my main problem is I didn't understand constructors so I was writing HUGE functions that just got way too big and confusing. Now that I see how you did it, I understand it a whole lot better. I was also forgetting that maybe I should make the player himself a class. I also need to decide when writing a function is necessary, because throughout my whole program it was pretty much the opposite :/. So I'm going to reference what you did and rewrite the whole thing myself.

Although, I did learn a lot of new things programming my monster wall of text.

One thing though, why do you use struct ? I didn't learn about that, is that compiler specific or does that have a different use than just class?
The only difference between a struct and a class is the default access modifier. It's private for class and public for struct.

So,

1
2
3
4
struct TypeName
{
    //...
};

is the same as

1
2
3
4
5
class TypeName
{
    public:
    //...
};
I'm also puzzled by this line Entity(){}. I don't understand the curly brackets with nothing between them.

Edit: I also don't understand this
1
2
3
struct Player;

struct Mob: public Entity


Is this just another way of inheritance? You declare a struct then declare another, and after that you define the first one.
Last edited on
I'm also puzzled by this line Entity(){}. I don't understand the curly brackets with nothing between them.

Did you try to comment it out and compile it? Did it compile? My guess is it didn't... Did this puzzle you even more? :D Ok, let's clarify things a bit...

Suppose you have a base class:

1
2
3
4
5
6
7
8
class Base
{
    public:
    int bval;

    Base() {}
    Base(int v) {bval=v;}
};

and a derived class:

1
2
3
4
5
6
7
class Derived: public Base
{
    public:
    int dval;

    Derived(int v1, int v2) {bval=v1; dval=v2;}
};

Whenever you create a Derived object, the Base default constructor is also called. If you don't provide any constructor to your class, you compiler provides one that does nothing (Base() {}). However, here I provide a constructor for Base (Base(int v);) so the compiler doesn't automatically give me the default constructor. But I need it, because it will be called whenever I instantiate a Derived object. That's why I write it myself. But, now that I look at it again, I'd say that this is bad design... I could have done it like this and save me some lines of code:

1
2
3
4
5
6
7
class Base
{
    public:
    int bval;

    Base(int v) {bval=v;}
};

1
2
3
4
5
6
7
8
9
class Derived: public Base
{
    public:
    int dval;

    //make the derived constructor
    //call my base constructor, not the default ;)
    Derived(int v1, int v2):Base(v1) {dval=v2;}
};

The above, applied to the game would look like this:

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
struct Entity
{
    string m_name;
    int m_health;
    int m_health_max;

    Entity(const char * name, int health)
    {
        m_name=name;
        m_health=m_health_max=health;
    }

};

struct Player;

struct Mob: public Entity
{
    int m_dmg_min;
    int m_dmg_max;

    Mob(const char * name, int health, int dmg_min, int dmg_max):
    Entity(name,health)
    {
        m_dmg_min=dmg_min;
        m_dmg_max=dmg_max;
    }

    void Attack(Player & target);
};

struct Player: public Entity
{
    int m_att;
    int m_def;
    int m_block;
    int m_crit;

    int m_level;
    int m_xp;

    Player(const char * name, int health, int att, int def, int block, int crit):
    Entity(name,health)
    {
        m_att=att;
        m_def=def;
        m_block=block;
        m_crit=crit;

        m_level=1;
        m_xp=0;
    }

    void Attack(Mob & target);
    void LevelUp();
};


EDIT:

Edit: I also don't understand this
1
2
3
struct Player;

struct Mob: public Entity

Is this just another way of inheritance? You declare a struct then declare another, and after that you define the first one.


struct Player; is called a forward declaration of the Player struct. It is necessary because the compiler must know that Player is a valid type when he encounters the line: void Attack(Player & target);. It has nothing to do with inheritance.
Last edited on
Sheeesh.. This is some great stuff.. Will be nice to see the end result :)
Yeah, that's what I think too! :) Hey, Adeon, would you mind changing the title of the topic to something more representative, like "my text rpg" or something (be creative ;) ), so that more people are lured into our discussion here?
Yeah.. That's what kept me from finding it in the first place :)
Title changed. Now I am running into a problem changing state effects over to the new code. To be honest with you, I stole some of the code you wrote above, but before I did anything I read through the entire thing and made sense of it all, which was why I asked questions on what you did and why. The problem came when I added the knockdown state effect. In the other program the KD state effect had a bool variable to represent it as active or unactive on the targeted mob or player. This was followed by if statements declaring
1
2
3
4
 if (KDstate==true) 
{
     cout << m_name << " is knocked down and can't attack." << endl;
}


Now in this new code, I have no idea how I would make this work. Do I add something to both the mob and player class to determine when it is active so I can take one of the class functions and use it to skip the mobs attack because it is knocked down? Say I make int knockdownChance; for the player class and I add it to the stats with knockdownChance=kd so that way I can add the chance as 10. How would I get that to work with Leg Sweep? 10% chance to knock down if leg sweep is used, and the class mob cannot attack when knockdown is active, but has, say, a 30% chance of standing up and resuming normal attacks. It's driving me crazy, classes really confuse the hell out of me and it doesn't help that the tutorials I read about them don't cover everything. So when you use things I don't know about I have no idea how you got certain things to work. Like when you write void Attack(Mob & target); What is target? Is that just in the STL or did you write something before that to declare what target is?

Topic archived. No new replies allowed.
Pages: 12