Sign in to follow this  
password

C++ text game help

Recommended Posts

I'm creating a text roleplaying game in c++, everything has gone quite good for a while now but i'm stuck right now. The problem is in the class "items". It's not the class itself but when I try to give every instance of the structures a value/values an error occur. Here is the code:
class user {
    class inventory {
      public:

        inventory() {
            total_items=0;
            used_items=0;
        }

        int total_items;
        int used_items;

        void add_item(int id, string name);
        void use_item();
        void show_items();
    };

  inventory inv;

  public:
    string name;

    // Player attributes and skill levels
    int hp;
    int level;
    int attack;
    int life;

    // The conclassor of the player class
    user() {
        hp=100;
        level=1;
        attack=1;
        life=1;
    }

    void inventory() {
        inv.show_items();
    }

    void pickup(int id, string item) {
        inv.add_item(id, item);
    }

    // A function that shows the player's current stats
    void stats() {
        cout << endl << endl;
        cout << "------------------------------- STATS --" << endl;
        cout << "PLAYER: " << name << endl;
        cout << "HP: " << hp << endl << endl;

        cout << "LEVEL: " << level << endl;
        cout << "ATTACK: " << attack << endl;
        cout << "LIFE: " << life << endl << endl;

        cout << "TOTAL ITEMS: " << inv.total_items << endl;
        cout << "TOTAL ITEMS USED: " << inv.used_items << endl;
        cout << "-- END STATS ---------------------------";
        cout << endl << endl;
    }

    // The function that saves the current stats in the dat file
    void save_stats() {
        ofstream save;
        save.open("save.dat");

        for (int i=0;i<25;i++) {
            cout << ".";
            Sleep(30);
        }

        cout << endl << endl;
        cout << "-- SAVE GAME ---------------------------" << endl;

        if (!save) {
            cout << "Could not save the game!" << endl;
        }

        string s_name = "PLAYER: ";
        string s_hp = "HP: ";
        string s_level = "LEVEL: ";
        string s_attack = "ATTACK: ";
        string s_life = "LIFE: ";
        string s_items = "TOTAL ITEMS: ";
        string s_used_items = "TOTAL ITEMS USED: ";

        save << s_name;
        save << name << endl;

        save << s_hp;
        save << hp << endl << endl;

        save << s_level;
        save << level << endl;

        save << s_attack;
        save << attack << endl;

        save << s_life;
        save << life << endl << endl;

        save << s_items;
        save << inv.total_items << endl;

        save << s_used_items;
        save << inv.used_items << endl << endl;

        cout << "Game saved succefully.." << endl;
        cout << "----------------------------------------";
        cout << endl << endl;

        save.close();
    }
};

class items {

// ##################################
// Item list

  // ######################
  // Weapons
      struct small_sword {
        int id;
        int damage;
        int speed;
      };

      struct medium_sword {
        int id;
        int damage;
        int speed;
      };

      struct power_sword {
        int id;
        int damage;
        int speed;
      };

      struct flash_sword {
        int id;
        int damage;
        int speed;
      };

      struct ultimate_sword {
        int id;
        int damage;
        int speed;
      };


  // #######################
  // ARMORS
      struct small_shield {
        int id;
        int armor;
      };

      struct medium_shield {
        int id;
        int armor;
      };

      struct high_shield {
        int id;
        int armor;
      };

      struct mega_shield {
        int id;
        int armor;
      };

      struct ultimate_shield {
        int id;
        int armor;
      };

  // ######################
  // General items
      struct key {
        int id;
      };

      struct healing {
        int id;
      };

      struct full_healing {
        int id;
      };

// #################################
// End of item list

// ################################
// ITEM ATTRIBUTES
// ################################

// SWORDS
//                                   id   power  speed
small_sword smallsword            = {1,   2,     2};
medium_sword mediumsword          = {2,   4,     3};
power_sword powersword            = {3,   17,    2};
flash_sword flashsword            = {4,   3,     16};
ultimate_sword ultimatesword      = {5,   14,    11};

// ARMORS
//                                   id   armor
small_shield smallshield          = {6,   2};
medium_shield mediumshield        = {7,   4};
high_shield highshield            = {8,   6};
mega_shield megashield            = {9,   8};
ultimate_shield ultimateshield    = {10,  10};

// GENERAL ITEMS
//                                  id
key key                           = {11};
healing healing                   = {12};
full_healing fullhealing          = {13};

// ################################
// END OF ITEM ATTRIBUTES
// ################################
};

For example: small_shield smallshield = {6, 2); If I edit it to: small_shield smallshield; without giving it a value, it works, if I edit every instance of the structures. Here comes the error message, I will paste a part of it. 7 start.cpp c:\documents and settings\xxx\desktop\game\classes.h:204: ANSI C++ forbids initialization of member `smallsword' 204 classes.h making `smallsword' static 204 classes.h in-class initialization of static data member of non-integral type `items::small_sword' 205 classes.h ANSI C++ forbids initialization of member `mediumsword' 205 classes.h making `mediumsword' static 205 classes.h in-class initialization of static data member of non-integral type `items::medium_sword' etc..... This is really frustruating, mostly because it shouldn't be any error because I did the same thing, almost, in the user class on the top. Anyways there shouldn't be anything wrong. That aside, what do you think of my code style, is it a good way to build a text based c++ game or does it just plain suck? took the opportunity to ask that while i'm at it :) Thankful for help... [Edited by - password on November 4, 2005 4:30:03 AM]

Share this post


Link to post
Share on other sites
Sorry, but I cant really help the way you have your code pasted. Use this:
[ source]
//code
[ /source]
Ofcourse, remove the space in between the '[' and 's' and the '[' and '/'. I just did it for the tags to ahow, not the box.

Share this post


Link to post
Share on other sites
Its hard to tell without the indenting and because its kindof confusing. I took a quick look but you are trying to initialize the member variables inside of the class 'user'. That should really be inside the constructor if you want to init them to those stats. I am not sure it it works but you could try making them static if every item of that type has those stats. You might not able to init them directly in the class declaration though. You might have to do something like
small_shield user::smallshield = ... outside of the decalartion of the class. I am not sure if that will work with structures though. The easiest way would be probably to just init them inside of the user constructor. Hope that helps.

Cya,
vbuser

[edit] typo

Share this post


Link to post
Share on other sites
Quote:
Original post by dudedbz1
Sorry, but I cant really help the way you have your code pasted. Use this:
[ source]
//code
[ /source]
Ofcourse, remove the space in between the '[' and 's' and the '[' and '/'. I just did it for the tags to ahow, not the box.


I'll have to second that. It really really helps!!

From trying to move through your code i cant even find a main() function, so iv no idea how everything is being called.

You should definitely be splitting up everything into .h and .cpp files, with the definition for all of your classes in the .h and the implementation in the .cpp. I assume that your not doing this, but if you are then obviously thats not a problem :)

You seem to declare a few classes within other ones. Im not sure that you necessarily need to do this with how you have structured things, it just seems to confuse me a bit (then again, with using [ code] tags and proper indenting it might be clearer)

For class items, you have many similar identical structures. This is not required, you want a single structure definition and then it being instanced in different ways. For example for the structures for Swords, instead of having a structure for small_sword, medium_sword etc etc, the following would work better:


struct Sword
{
int Id;
int Damage;
int Speed;
};


and then in the function where you need to declare new swords, hold the data in different instance variables. For example:


Sword Basic_Sword;

Basic_Sword.Id = 0;
Basic_Sword.Damage = 5;
Basic_Sword.Speed = 5;

// use Basic_Sword wherever its needed....

Sword Really_Awesome_Sword;

Really_Awesome_Sword.Id = 1;
Really_Awesome_Sword.Damage = 453759436;
Really_Awesome_Sword.Speed = 5437534897694;

// use swords wherever...



Hopefully that makes sense to you. If not then il try to clarify what im saying later :)

Share this post


Link to post
Share on other sites
Taking Tesl's suggestion, you could even go a step further with it.


class Sword
{
private:
int mID;
int mDamage;
int mSpeed;

public:
Sword( int = -1, int = 5, int = 1 );
};

Sword::Sword( int pID, int pDamage, int pSpeed )
: mID( pID ), mDamage( pDamage ), mSpeed( pSpeed )
{}



From that, you'd have a sword that is always initialized to whatever base settings you want. You could inherit from that and add whatever magical effects or abilities you want on a special sword. Of course, you could do that even better with an Ability class, but I'll hold back on that for now, heh.

That aside, I'd have to say I'm a tad confused as to why you're defining the inventory and such inside the user. If you're doing it for purposes of keeping everything neat and tidy together, I'd recommend using a namespace instead.

Share this post


Link to post
Share on other sites
Quote:

That aside, what do you think of my code style, is it a good way to build a text based c++ game or does it just plain suck? took the opportunity to ask that while i'm at it :)


I think you have the right idea but maybe a bit "wrong" structuring.
I would consider something like


class Item {
public:
int id;
};

class Sword : public Item{
public:
int speed, dmg;
};

class HeavySword : public Sword {
public:
int special_effect;
};

...

main()
{
// Keeping many swords...
vector<Item*> vec_swords;

// Or use one sword for all purposes...
Sword sword;
HeavySword heavy_sword;

...
}






If I needed more special effects in the HeavySword object
Id consider using bit fields

...

class HeavySword : public Sword {
public:
union {
struct {
unsigned int critical : 1;
unsigned int whirlwind : 1;
} effect;
unsigned int align;
};
};

...

maain()
{
HeavySword heavy_sword;

heavy_sword.effect.critical = 1;
...
}




EDIT:
This code misses everything from virtual destructors to proper testing

[Edited by - pulpfist on November 3, 2005 11:51:41 PM]

Share this post


Link to post
Share on other sites
I think you are quite confused. Nesting classes is generally something you don't want to get in to without a good reason. It appears here like you want 'small_sword', 'medium_sword' etc. to be *instances* of a simple sword class - they all have the same *kind* of data, but simply different *values* for the data members. Similarly for shields. Meanwhile, both a sword and a shield are supposed to be a *kind of* item. That relationship is not properly represented by nesting the classes, but instead by inheritance.


class Item {
// There isn't really such a thing as an unspecified 'item'; that's just an
// abstract concept that exists to let us say that swords and shields are
// kinds of items.

int id; // something that all items have.
// We want every Item that ever exists to have a different ID - that's the
// proper use for an ID value, really; there's no sense using it to represent
// the object type, because, well, the object's actual type (as understood by
// the compiler) already does that.
// This is a setup we can use to accomplish this:
static int nextIdToAssign;
public:
Item() : id(nextIdToAssign++) {}
// Since we're going to inherit from this class, we should put a
// "virtual destructor"
virtual ~Item() {} // it doesn't need to do anything for the base class.
};

// The static int line above only declared the variable (that's a weird thing
// with static class members in C++); we need to define it out here as well.

static int Item::nextIdToAssign = 0; // starting value.

class Sword : public Item {
// Here is stuff that a Sword has that other items don't.
int damage;
int speed;
// We'll make a constructor that lets us specify the damage and speed
// when we make a sword. Since a Sword "is a" Item, the data structure
// implicitly includes an id as well, which will be implicitly assigned by
// the implicitly-called Item constructor during Sword construction.
public:
Sword(int damage, int speed) : damage(damage), speed(speed) {}
};

// Now here's a convenience function that will let us create small, etc. swords,
// each of which with (naturally) a unique ID.

Sword createSmallSword() {
return Sword(2, 2);
} // and we could do similarly for other sword types.

// If we wanted to make sure that *every* sword is one of those specific types,
// then we could instead change the constructor to take some enumeration value
// (e.g. we might say ''Sword mySword(Sword::SMALL)'' after setting that up),
// and look up the necessary damage/speed values from a (static) table.

// Similarly we can make other Item subclasses, and then we can have a container
// of Items in the Inventory. You will want to store them using some "smart
// pointer" wrapper. Storing Item objects in an array or other container
// direction *will not work*, because of "object slicing" - look it up - and
// raw pointers here will be a memory management nightmare.

Share this post


Link to post
Share on other sites
Sorry, I tried the [CODE] tag but it didn't work. I will update the first post now, thanks for your replies. There were many good methods you posted, can't wait to test them, will update the post if it works.

I edited the code to this:

class item {
int id;
static int nextIdToAssign;

public:
item() : id(nextIdToAssign++) {}
virtual ~item();
};

int item::nextIdToAssign = 0;

class sword : public item {
int damage;
int speed;

public:
sword(int damage, int speed) : damage(damage), speed(speed) {}
};



It didn't work with
static int item::nextIdToAssign = 0;

outside the class, it resulted in compile error but it did work with int alone like this:
int item::nextIdToAssign = 0;

It has the same effect right? I understand what you mean except the virtual destructor, what it does.

[Edited by - password on November 4, 2005 4:28:53 AM]

Share this post


Link to post
Share on other sites
thats right, same effect :p
Actually I think it was a bug even

you need virtual destructor so that when you
delete a Sword object, not only the sword part of the object is
deleted, but also the Item part. (Inhertitage)

Share this post


Link to post
Share on other sites
Yes, it worked now, atleast there are no errors at the moment. This is the first game i'm creating and I don't know so much about classes except the techniques I used in the first example. After I saw some of your pasted codes I realised how much better code you can do with classes when coding games like this. I learned a lot from you.

Do you know any good tutorial on classes used in a similar relation as this game? maybe i'm asking too much now, it could be useful because that stuff was kind of new to me that you posted, I guess I will read more about classes.

Thank you all..

Share this post


Link to post
Share on other sites
YOu might look into OMT or UML diagrams... Yeah, they look
very boring, but you can translate them 1:1 into C++ code,
and it helps you with the overview of the class-design (which
classes interact together.

I think you might want to learn the difference between when to
use inheritance, and when to use delegation.

- inheritance: "is-a", like a hammer is-a tool,
your hammer class derives properties from the tool-class
- delegation: "has-a", like a car has-an-engine
your car-class will contain a data member to another class
called "engine".

... and engine might derive from an abstract base-class (also
interface-class) "Device". Abstract like 'a device might have
some properties and methods" but you can never make an instance
of an abstract class.

Hope that helps a bit :)

Regards

Share this post


Link to post
Share on other sites

I dont have anything good atm on my favourites....

Btw you can also have protected data members, like

class Item
{

public:
(variables here is accessable to all)

private:
(variables here is only accessable to the Item class own functions)

protected:
(variables here is only accessable to the Item class functions, and functions
of types that inherits from Item)

};

Instead of placing variables like speed and damage under public (as i notoriously do in my code examples) it is more common and "correct" to
leave them under private (to protect them from the cold world out there),
and make accessor functions to them under the public section

class Sword : public Item
{
public:

int get_speed() const { return speed; }

void set_speed(int new_speed) { speed = new_speed); }

...

private:

int speed ... ...
};


EDIT:
And as kitt3n says, dont be tempted to use a "is a" realtionship
if it should be a "has a" realtionship.


[Edited by - pulpfist on November 4, 2005 6:39:50 AM]

Share this post


Link to post
Share on other sites
>Oh. Uh, you're getting way ahead of yourself, then. Trust me. Work out >something simpler first.

I think he's making a good first attempt: A _text_ rpg (not the next 3d
mmorpg).

With a text-rpg, his focus will be on datastructures, just let him mess
around with classes/structs and things like that.

At the very least it'll be a good learning experience and it's a nice
first program to mess around with in which you can realy live into (and
probably it'll be thrown away and remade with the experience gained :)

I say go for it!

Regards,
Roger

Share this post


Link to post
Share on other sites
Btw how would you guys implement a simple inventory using these new classes?
Im realy not sure how to do it nicely.

Say I make an inventory

Item *inventory[10];

Sword sword = createSmallSword();
...
inventory[0] = &sword;
...


I believe this is malicous somehow, if it works at all.

cout << inventory[0]->speed << endl;


And even if the Sword part of the object wasnt sliced, I still would need
a way to determine what type of item resides in a speciffic slot.

PS. Using a switch to do it at runtime is not very tempting.

Any suggestions?

Share this post


Link to post
Share on other sites
Here are some ideas for you to play around with.

First of all, don't use pointers in your inventory class - they
will screw you when you try to make savegames later (you can't
store pointers on disk) - use unique object-id's instead.

Notice the use of inheritance and delegation. Creatures for example
just call the inventory-save function (creature doesn't care what
it does, it just assumes the inventory class will handle it).
Inventory can save itself, without knowing anything of the objects.
It knows the id's and that's enough - it's the objectManagers
responsibility to save the actual objects (and the objectmanager
in its turn loop through all objects, but delegates the actual save
to the Objects themselves)

enum Type
{
TYPE_SWORD,
TYPE_POTION,
TYPE_ORC, // creature
TYPE_KNIGHT,
TYPE_xxx
TYPE_MAX,
};

enum Family
{ // we have one class for each family
// one family can have several Types associated
// One class for inventory items will usually suffice. Swords, Big-swords
// and magic-sword usually only look different, but their behaviour in
// general is the same.
FAMILY_INVENTORY_ITEM, // potions, swords, everything player can take
FAMILY_CREATURE, // creatures (orcs, knights, ...)
...
};


class ObjectManager
{
struct TypeShared
{ // shared information every type has
Family fam;
unsigned char inventoryWidth, inventoryHeight;
... <whatever information is shared>
};

TypeShared sharedInfo[TYPE_MAX];

Family getFamily (Type t) { return sharedInfo[t].fam;


unsigned int createObject (Type t)
{ // design pattern to use: Factory
// for simplicity we use a switch here:
Object* o = NULL;
switch (Fam)
{ case FAMILY_CREATURE: o = new Creature;
case FAMILY_ITEM: o = new Item;
}

// store a reference to o (eg inside a vector, or map)
// so we can get a pointer to the actual object when we
// have an id - let o know it's own id for comfortability.
o->setSelf ( array-index on which we stored it)

return o;
}

void deleteObject (unsigned int ID)
{ //
// remove object from our internal vector and
// then delete it.
}

void save (...)
{
// save ammount of objects
// loop through all objects
// get the family and store it - we need this in load!
// now call this objects save-method
}

void load ()
{
// load ammount of objects - we'll have to loop this often.
// load the family - we can create an empty class with it
// now call the load of the class, passing our current file-pointer
// so the object can load itself into our clean instance
}
}

class Inventory
{
// inventory references items (created by the objectManager)
unsigned int invItems[width][height];

// saving this, is as easy as saving all ID's into your savegame.
}

class Model3D
{ // vertex-buffers, which textures to use, ...

void render (position, ...) { put it on the screen at specified position}
};


class Object
{ // Object has a position in the world
Position p;
Type t; // object knows its own type
Model3D model; // 3d model

virtual void save (...) { save position & type }

Type getType (void) const { return t; }

void Render (void) { model.render(position, ...); }
}


class Creature: public Object // creature is-a object
{
Inventory inv; // creature has-a inventory

virtual void save ()
{ Object::save(...);
inv.save();
<other creature-specific saves>

}

class InventoryItem : public Object // item is-a object
{
virtual void save ()
{ Object::save (...)
<other object-specific saves>
}
}

Regards,
Roger

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this