views:

112

answers:

3

Hey,

I have a few classes set up for a game, with XMapObject as the base, and XEntity, XEnviron, and XItem inheriting it.

MapObjects have a number of flags, one of them being MAPOBJECT_SOLID. My problem is that XEntity is the only class that correctly detects MAPOBJECT_SOLID. Both Items are Environs are always considered solid by the game, regardless of the flag's state. What is important is that Environs and Item should almost never be solid.

Each class has a very basic preliminary constructor, just initializing all varibles to zero or NULL. During the CreateX() phase, Objects are linked into the map, set into a linked linked list.

Both XItem and XEnviron are a tad sloppy. They are both new, and in the middle or my debugging attempts.

Here are the relevent code samples:

XMapObject:

#define MAPOBJECT_ACTIVE 1
#define MAPOBJECT_RENDER 2
#define MAPOBJECT_SOLID 4

class XMapObject : public XObject
{
    public:
    Uint8 MapObjectType,Location[2],MapObjectFlags;

    XMapObject *NextMapObject,*PrevMapObject;

    XMapObject();

    void CreateMapObject(Uint8 MapObjectType);
    void SpawnMapObject(Uint8 MapObjectLocation[2]);
    void RemoveMapObject();
    void DeleteMapObject();

    void MapObjectSetLocation(Uint8 Y,Uint8 X);
    void MapObjectMapLink();
    void MapObjectMapUnlink();
};

XMapObject::XMapObject()
{
    MapObjectType = 0;
    Location[0] = 0;
    Location[1] = 1;
    NextMapObject = NULL;
    PrevMapObject = NULL;
}

void XMapObject::CreateMapObject(Uint8 Type)
{
    MapObjectType = Type;
}

void XMapObject::SpawnMapObject(Uint8 MapObjectLocation[2])
{
    if(!(MapObjectFlags & MAPOBJECT_ACTIVE)) { MapObjectFlags += MAPOBJECT_ACTIVE; }

    Location[0] = MapObjectLocation[0];
    Location[1] = MapObjectLocation[1];

    MapObjectMapLink();
}

XEntity:

XEntity *StartEntity = NULL,*EndEntity = NULL;

class XEntity : public XMapObject
{
    public:
    Uint8 Health,EntityFlags;
    float Speed,Time;
    XEntity *NextEntity,*PrevEntity;
    XItem *IventoryList;

    XEntity();

    void CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2]);
    void DeleteEntity();

    void EntityLink();
    void EntityUnlink();

    Uint8 MoveEntity(Uint8 YOffset,Uint8 XOffset);
};

XEntity::XEntity()
{
    Health = 0;
    Speed = 0;
    Time = 1.0;
    EntityFlags = 0;
    NextEntity = NULL;
    PrevEntity = NULL;
    IventoryList = NULL;
}

void XEntity::CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2])
{
    CreateMapObject(EntityType);
    SpawnMapObject(EntityLocation);

    if(!(MapObjectFlags & MAPOBJECT_SOLID) { MapObjectFlags += MAPOBJECT_SOLID; }
    EntityFlags = ENTITY_CLIPPING;
    Time = 1.0;
    Speed = 1.0;

    EntityLink();
}

void XEntity::EntityLink()
{
    if(StartEntity == NULL)
    {
        StartEntity = this;
        PrevEntity = NULL;
        NextEntity = NULL;
    }
    else
    {
        EndEntity->NextEntity = this;
    }

    EndEntity = this;   
}

XEnviron:

class XEnviron : public XMapObject
{
    public:
    Uint8 Effect,TimeOut;

    void CreateEnviron(Uint8 Type,Uint8 Y,Uint8 X,Uint8 TimeOut);
};

void XEnviron::CreateEnviron(Uint8 EnvironType,Uint8 Y,Uint8 X,Uint8 TimeOut)
{
    CreateMapObject(EnvironType);

    Location[0] = Y;
    Location[1] = X;

    SpawnMapObject(Location);

    XTile *Tile = GetTile(Y,X);
    Tile->Environ = this;

    MapObjectFlags = MAPOBJECT_ACTIVE + MAPOBJECT_SOLID;
    printf("%i\n",MapObjectFlags);
}

XItem:

class XItem : public XMapObject
{
    public:
    void CreateItem(Uint8 Type,Uint8 Y,Uint8 X);
};

void XItem::CreateItem(Uint8 Type,Uint8 Y,Uint8 X)
{
    CreateMapObject(Type);

    Location[0] = Y;
    Location[1] = X;

    SpawnMapObject(Location);
}

And lastly, the entity move code. Only entities are capable of moving themselves.

Uint8 XEntity::MoveEntity(Uint8 YOffset,Uint8 XOffset)
{
    Uint8 
      NewY = Location[0] + YOffset,
      NewX = Location[1] + XOffset;

    if((NewY >= 0 && NewY < MAPY) && (NewX >= 0 && NewX < MAPX))
    {
        XTile *Tile = GetTile(NewY,NewX);

        if(Tile->MapList != NULL)
        {
            XMapObject *MapObject = Tile->MapList;

            while(MapObject != NULL)
            {
                if(MapObject->MapObjectFlags & MAPOBJECT_SOLID)
                {
                    printf("solid\n");
                    return 0;
                }

                MapObject = MapObject->NextMapObject;
            }
        }

        if(Tile->Flags & TILE_SOLID && EntityFlags & ENTITY_CLIPPING)
        {
            return 0;
        }

        this->MapObjectSetLocation(NewY,NewX);

        return 1;
    }

    return 0;
}

What is wierd, is that the bitwise operator always returns true when the MapObject is an Environ or an Item, but it works correctly for Entities. For debug I am using the printf "Solid", and also a printf containing the value of the flag for both Environs and Items.

Any help is greatly appreciated, as this is a major bug for the small game I am working on. I am also very new at Object Oriented programming, anything tips, suggestions and/or criticism are also welcome.

A: 

You can't (legally) be calling XEntity::MoveEntity on a MapObject or Environ because they don't have such a method. If you're using static_cast to change your object pointer into an XEntity so you can call MoveEntity on it, then you really have no guarantees about how the bit operation will work. In some implementations, things may appear to work in MoveEntity, but what's actually happening is it's interpreting the other object's memory as an XEntity. When it tries to access the offset where it believes MapObjectFlags exists, it's not actually there and always has that bit set to 1.

Mark B
+1  A: 

Your problem appears to be that you never initialize MapObjectFlags in any classes other than XEnviron so, as a basic type, it will have an unspecified value in XItem, XEntity and other XMapObject derived objects. I suggest that, as a member of XMapObject you explicitly initialize it to a known value.

As a rule, it is generally a good idea to ensure that all members of basic type are explicitly initialized in the initializer list of every constructor that you define.

e.g.

XMapObject()
    : MapObjectFlags(0)
    , // ... other initializers
{
    // Other initializations
}
Charles Bailey
It looks like it. The code could use some clean up, and the suggestions you posted are helpful.
Shawn B
A: 

I figured out the problem earlier today - It didn't have any relation to OO programming, inheritance, or bitwise; it was a simple scope error.

The problem was in the fact that during my quick test to get an Environ in game, I declared the new variable inside of the control switch sequence, so the next time any control was used, the Environ would act in unpredictable ways.

switch(Event.key.keysym.sym)
{
    ...
    case SDLK_c: { XEnviron Environ; Environ.InitEnviron(...); }
    ...
}
Shawn B