Multiple definition errors; C++ working with Allegro

779 views Asked by At

So I'm working on a game for my computer science class using Dev-C++ 5.11 and Allegro 4.2, I'm reaching the end of the project and I want to finally fix a problem I've been having for a while now. My code is rather long to use a single .cpp file, but when I try to place my functions in a separate file I receive a series of "multiple definition of '' first defined here" errors. I've looked online and most people who have this problem have included their .cpp file in their main or something equivalent, but I have not done this and can't figure out why it won't work. The errors points to all the variables I declare in the header (the bitmaps as well as the char array and ints).

Files:

asteroidsMain.cpp
functions.cpp
asteroids.h

I also use a data file.

Main file:

#include <allegro.h>
#include <alfont.h> 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "asteroids.h"
#include "asteroids_data.h"

//Creating movement timer (also used for typing)
volatile long speed_counterA = 0;

void increment_speed_counterA() {
    speed_counterA++;
}
END_OF_FUNCTION(increment_speed_counterA); 

//Creating laser timer
volatile long speed_counterB = 0;

void increment_speed_counterB() {
    speed_counterB++;
}
END_OF_FUNCTION(increment_speed_counterB); 

//Creating asteroid timer
volatile long speed_counterC = 0;

void increment_speed_counterC() {
    speed_counterC++;
}
END_OF_FUNCTION(increment_speed_counterC); 


int main() {
    //Initializations
    allegro_init();
    alfont_init();
    set_color_depth(desktop_color_depth());
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);
    install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL);
    install_keyboard();
    install_mouse();
    install_timer();

    //Movement timer
    LOCK_VARIABLE(speed_counterA);
    LOCK_FUNCTION(increment_speed_counterA);
    install_int_ex(increment_speed_counterA, BPS_TO_TIMER(60));
    //Laser firing timer
    LOCK_VARIABLE(speed_counterB);
    LOCK_FUNCTION(increment_speed_counterB);
    install_int_ex(increment_speed_counterB, BPS_TO_TIMER(60));
    //Asteroid generation timer
    LOCK_VARIABLE(speed_counterC);
    LOCK_FUNCTION(increment_speed_counterC);
    install_int_ex(increment_speed_counterC, BPS_TO_TIMER(60));

    //Randomizing seed
    srand(time(0));

    //Creating/loading BITMAPs formain game
    BITMAP *buffer = create_bitmap(800, 600);
    shipStopped = loadImage("shipStopped");
    shipMoving = loadImage("shipMoving");
    laserSprite = loadImage("laser");
    smallAsteroid = loadImage("small_Asteroid");
    medAsteroid = loadImage("med_Asteroid");
    largeAsteroid = loadImage("large_Asteroid");

    //General use variables for screen control
    int onScreen = 0, i = 0, j = 0;
    bool quit = false;

    //Retrieving and sorting scores
    numOfScores = getHighs(name, score);
    sortHighs(name, score);

    //Loads main screen
    onScreen = changeScreen(0);

    while (!quit) {
        //If there was an issue loading datafile, error is returned
        if (onScreen == -1) {
            return 1;
        }
        else if (onScreen == 0) {   
            while (onScreen == 0) {
                //Primary check is for quitting program, other buttons lead to separate screens
                if ((mouse_b & 1) && mouse_x > 400 - 75 && 
                mouse_x < 400 + 75 && mouse_y > 450 && mouse_y < 4505 + quitButton->h) { //For clicking Quit
                    quit = true;
                    onScreen = -2;
                }
                else if ((mouse_b & 1) && mouse_x > 400 - 75 && 
                mouse_x < 400 + 75 && mouse_y > 150 && mouse_y < 150 + playButton->h) { //For clicking Play
                    onScreen = changeScreen(2);
                }
                else if ((mouse_b & 1) && mouse_x > 400 - 75 && 
                mouse_x < 400 + 75 && mouse_y > 250 && mouse_y < 250 + highsButton->h) { //For clicking Highscores
                    onScreen = changeScreen(3);
                }
                else if((mouse_b & 1) && mouse_x > 400 - 75 && 
                mouse_x < 400 + 75 && mouse_y > 350 && mouse_y < 350 + creditsButton->h) { //For clicking Credits
                    onScreen = changeScreen(4);
                }
            }
        }
        else if (onScreen == 2) { //Difficulty choice screen
            if ((mouse_b & 1) && mouse_x > 600 - (playButton->w / 2) && 
                mouse_x < 600 + (playButton->w / 2) && mouse_y > 192 && mouse_y < 192 + playButton->h) {
                    onScreen = changeScreen(1);
        }
            else if ((mouse_b & 1) && mouse_x > 635 && mouse_x < 775 && 
            mouse_y > 575 - menuButton->h && mouse_y < 575) { //Returns to main menu
                onScreen = changeScreen(0);
            }
        }
        else if (onScreen == 3) { //Highscore screen
            if ((mouse_b & 1) && mouse_x > 635 && mouse_x < 775 && 
            mouse_y > 575 - menuButton->h && mouse_y < 575) { //Returns to main menu
                onScreen = changeScreen(0);
            }
        }
        else if (onScreen == 4) { //Credit screen
            if ((mouse_b & 1) && mouse_x > 635 && mouse_x < 775 && 
            mouse_y > 575 - menuButton->h && mouse_y < 575) { //Returns to main menu
                onScreen = changeScreen(0);
            }
        }
        else if (onScreen == 51) { //Game over screen

            if (points <= score[9]) {
                if ((mouse_b & 1) && mouse_x > 25 && mouse_x < 175 &&
                    mouse_y > 575 - menuButton->h && mouse_y < 575) { //Begins a new game
                    onScreen = changeScreen(2);
                }
                else if ((mouse_b & 1) && mouse_x > 635 && mouse_x < 775 &&
                         mouse_y > 575 - menuButton->h && mouse_y < 575) { //Returns to main menu
                    onScreen = changeScreen(0);
                }
            }
            else {
                if (mouse_b & 1) {
                    //Adding new highschore to array
                    score[9] = points;
                    strcpy(name[9], "");

                    if (numOfScores < 10) {
                        numOfScores++;
                    }
                    onScreen = changeScreen(52);
                }
            }
        }
        else if (onScreen == 52) {
            speed_counterA = 0;

            int keyEnt = 0, len = 0;
            char keyHold;

            //Text entering for highscore name, moves to next screen on enter
            while (!key[KEY_ENTER]) {
                //Preventing multiple printing of same key
                while (keypressed() && speed_counterA % 3 == 0) {
                    //Reading key from keyboard and making it useable
                    keyEnt = readkey();
                    keyHold = keyEnt & 0xff;

                    if (keyHold >= 32 && keyHold <= 126) { //Adding chars to name string
                        len = strlen(name[9]);

                        if (len < 19) { //Restricting the number of characters you can enter, beyond this does not save properly
                        name[9][len] = keyHold;
                        name[9][len + 1] = '\0';
                    }
                    }
                    else if (key[KEY_BACKSPACE]) { //Deleting if backspace char is pressed
                        name[9][len] = '\0';
                        len--;
                    }
                    clear_keybuf();
                }
                //Printing text
                textprintf_centre_ex(buffer, font, 400, 300, WHITE, -1, "What is your name: %s", name[9]);
                textprintf_centre_ex(buffer, font, 400, 500, WHITE, -1, "** press enter to conitue **");
                // Blit the buffer
                blit(buffer, screen, 0, 0, 0, 0, 800, 600); 
                clear(buffer);

            }
            onScreen = changeScreen(53);
        }
        else if (onScreen == 53) {
            if ((mouse_b & 1) && mouse_x > 25 && mouse_x < 175 &&
                mouse_y > 575 - menuButton->h && mouse_y < 575) { //Begins a new game
                onScreen = changeScreen(2);
            }
            else if ((mouse_b & 1) && mouse_x > 635 && mouse_x < 775 &&
                     mouse_y > 575 - menuButton->h && mouse_y < 575) { //Returns to main menu
                onScreen = changeScreen(0);
            }
        }
        else if (onScreen == 1) { //Game screen
            //Defining 
            Laser laser[50];
            Asteroid asteroid[10];

            //Initializing lasers and asteroids
            for (i = 0; i < 50; i++) {
                laser[i].pos.x = 0;
                laser[i].pos.y = 0;
                laser[i].angle = 0;
                laser[i].onScreen = false;
            }
            for (i = 0; i < 10; i++) {
                asteroid[i].pos.x = 0;
                asteroid[i].pos.y = 0;
                asteroid[i].angle = 0;
                asteroid[i].onScreen = false;
            }

            //Game specific variables, reset for every new game
            int move = 0, lasers= 0, asteroids = 0;
            float angle = 0;
            bool collide = false;

            points = 0;

            //Spawning ship when game screen loads
            Coordinate ship;
            ship.x = 400 - (shipStopped->w / 2);
            ship.y = 300 - (shipStopped->h / 2);

            //Reseting game timers
            speed_counterA = 0;
            speed_counterB = 0;
            speed_counterC = 0;

            while (onScreen == 1) { //Game screen
                while (speed_counterA > 0) {

                    //Ship movement
                    if (key[KEY_UP] && key[KEY_RIGHT]){
                        ship.y -= 3;
                        ship.x += 3;
                        angle = 32;
                    }
                    else if (key[KEY_UP] && key[KEY_LEFT]){
                        ship.y -= 3;
                        ship.x -= 3;
                        angle = 224;
                    }
                    else if (key[KEY_DOWN] && key[KEY_RIGHT]) {
                        ship.y += 3;
                        ship.x += 3;
                        angle = 96;
                    }
                    else if (key[KEY_DOWN] && key[KEY_LEFT]) {
                        ship.y += 3;
                        ship.x -= 3;
                        angle = 160;
                    }
                    else if (key[KEY_UP]){
                        ship.y -= 6;
                        angle = 0;
                    }
                    else if (key[KEY_RIGHT]) {
                        ship.x += 6;
                        angle = 64;
                    }
                    else if (key[KEY_LEFT]) {
                        ship.x -= 6;
                        angle = 192;
                    }
                    else if (key[KEY_DOWN]) {
                        ship.y += 6;
                        angle = 128;
                    }

                    //Laser movement
                    for (i = 0; i < 50; i++) {
                        laser[i] = moveLaser(laser[i]);
                    }

                    //Asteroid movement
                    for (i = 0; i < 10; i++) {
                        asteroid[i] = moveAsteroid(asteroid[i]);
                    }

                    speed_counterA--;
                }

                //Key trigger for laser firing, if is first as having timer first 
                //would cause many hits not to be properly registered especially if 
                //tapping too fast, this allows firing at will with a fastest firing of every 0.15s
                if (key[KEY_SPACE]) {
                    //Laser firing timer
                    while (speed_counterB > 9) {
                        //Laser shooting
                        laser[lasers] = fireLaser(angle, ship.x, ship.y);

                        if (lasers >= 49) {
                            lasers = 0;
                        }

                        lasers++;

                    speed_counterB -= 10;
                    }
                }   

                //Asteroid spawning counter
                while (speed_counterC > 59) { //Spawns an asteroiud every second

                    //Keeping asteroids within array range
                    if (asteroids >= 9){
                        asteroids = 0;
                    }

                    //Generating asteroid
                    asteroid[asteroids] = genAsteroid(1, -1);

                    asteroids++;
                    speed_counterC -= 60;
                }

                //If ship goes out top, it comes in bottom; vice versa
                if (ship.y <= 0 - ((shipStopped->h / 2))) {
                    ship.y += (600);
                }
                else if (ship.y >= 600 - ((shipStopped->h / 2))) {
                    ship.y -= (600);
                }

                //If ship goes out left, it comes in right; vice versa
                if (ship.x <= 0 - ((shipStopped->w / 2))) {
                    ship.x += (800);
                }
                else if (ship.x >= 800 - ((shipStopped->w / 2))) {
                    ship.x -= (800);
                }

                //Drawing lasers
                for (i = 0; i < 50; i++) {
                    if (laser[i].onScreen) {
                        rotate_sprite(buffer, laserSprite, laser[i].pos.x, laser[i].pos.y, ftofix(laser[i].angle)); 
                    }
                }

                //Drawing ship
                if (key[KEY_UP] || key[KEY_DOWN] || key[KEY_LEFT] || key[KEY_RIGHT]) {
                    rotate_sprite(buffer, shipMoving, ship.x, ship.y, ftofix(angle)); //Ship sprite with engines firing
                }
                else {
                    rotate_sprite(buffer, shipStopped, ship.x, ship.y, ftofix(angle)); //Ship sprite no flames

                }

                //Drawing asteroids
                for (i = 0; i < 10; i++) {
                    if (asteroid[i].onScreen == true) {
                        //Drawing sprite based on asteroid type
                        if (asteroid[i].type == 1) { //Large asteroid
                            draw_sprite(buffer, largeAsteroid, asteroid[i].pos.x, asteroid[i].pos.y);
                        }
                        else if (asteroid[i].type == 2) { //Medium asteroid
                            draw_sprite(buffer, medAsteroid, asteroid[i].pos.x, asteroid[i].pos.y);
                        }
                        else if (asteroid[i].type == 3) { //Small asteroid
                            draw_sprite(buffer, smallAsteroid, asteroid[i].pos.x, asteroid[i].pos.y);
                        }
                    }
                }

                //Checks bb collisions
                for (i = 0; i < 50; i++) {
                    for (j = 0; j < 10; j++) {
                        if (laser[i].onScreen && asteroid[j].onScreen) {
                            collide = checkCollision(asteroid[j].type, 0, asteroid[j].pos, laser[i].pos);
                            if (collide) {
                                laser[i].onScreen = false;
                                asteroid[j].onScreen = false;

                                points += 100 * asteroid[j].type;

                                //Splitting med or large asteroids into 2 asteroids one type down
                                if (asteroid[j].type < 3) { 
                                    asteroid[asteroids] = splitAsteroid(1, asteroid[j]);
                                    asteroid[j] = splitAsteroid(2, asteroid[j]);
                                    asteroids++;
                                }
                            }
                        }
                    }
                }

                //Printing score
                textprintf_ex(buffer, font, 10, 5, WHITE, -1, "Score: %d", points);
                textprintf_ex(buffer, font, 660, 5, WHITE, -1, "Press esc to quit");

                // Blit the buffer
                blit(buffer, screen, 0, 0, 0, 0, 800, 600); 
                clear(buffer);

                for (i = 0; i < 10; i++) {
                    if (asteroid[i].onScreen) {
                        collide = checkCollision(asteroid[i].type, 1, asteroid[i].pos, ship);

                        if (collide) {
                            onScreen = changeScreen(51);
                        }   
                    }
                }

                //To leave game screen and return to main menu
                if (key[KEY_ESC]) {
                    onScreen = changeScreen(0);
                }
            }
        }
    }

    //Freeing memory
    destroy_bitmap(playButton);
    destroy_bitmap(highsButton);
    destroy_bitmap(creditsButton);
    destroy_bitmap(quitButton);
    destroy_bitmap(shipStopped);
    destroy_bitmap(shipMoving);
    destroy_bitmap(laserSprite);
    destroy_bitmap(smallAsteroid);
    destroy_bitmap(medAsteroid);
    destroy_bitmap(largeAsteroid);

    return 0;
}
END_OF_MAIN()

Header file is:

#ifndef ASTEROIDS_H_
#define ASTEROIDS_H_

#define WHITE makecol(255, 255, 255)
#define BLACK makecol(0, 0, 0)

#include <allegro.h>
#include <alfont.h> 

//Made global so size pointers can be used throughout
BITMAP *playButton;
BITMAP *playAgainButton;
BITMAP *highsButton;
BITMAP *creditsButton;
BITMAP *quitButton;
BITMAP *menuButton;
BITMAP *shipStopped;
BITMAP *shipMoving;
BITMAP *laserSprite;
BITMAP *smallAsteroid;
BITMAP *medAsteroid;
BITMAP *largeAsteroid;

//Highscore variables are made global; technically bad, 
//but makes simplifies printing greatly
int score[10], numOfScores, points;
char name[10][20];

struct Coordinate {
    int x, y;
};

struct Laser {
    Coordinate pos;
    float angle;
    bool onScreen;
};

struct Asteroid {
    Coordinate pos;
    int type;
    int angle;
    bool onScreen;
};

int changeScreen(int toLoad);
BITMAP* loadImage(const char image[20]);
int getHighs(char name[][20], int high[]);
void sortHighs(char name[][20], int score[]);
void saveHighs(const char name[][20], const int score[], int numOfScores);
Laser fireLaser(float angle, int ship_x, int ship_y);
Laser moveLaser(Laser laser);
Asteroid genAsteroid(int difficulty, int type);
Asteroid splitAsteroid(int order, Asteroid asteroid);
Asteroid moveAsteroid(Asteroid asteroid);
bool checkCollision(int typeA, int typeB, Coordinate asteroid, Coordinate object);

#endif

The functions file boils down to:

#include <allegro.h>
#include <alfont.h> 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "asteroids.h"
#include "asteroids_data.h"

/*~500 lines of functions*/

Everything works when I simply put the functions below the main

Copy of compile log:

g++.exe -c asteroidsMain.cpp -o asteroidsMain.o -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++" -I"C:/Program Files (x86)/Dev-Cpp/include" -m32
g++.exe asteroidsMain.o functions.o -o POPE_Greg_Asteroids.exe -L"C:/Program Files (x86)/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib32" -L"C:/Program Files (x86)/Dev-Cpp/lib" -lalfont -lalleg -m32
functions.o:functions.cpp:(.bss+0x0): multiple definition of `playButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x0): first defined here
functions.o:functions.cpp:(.bss+0x4): multiple definition of `playAgainButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x4): first defined here
functions.o:functions.cpp:(.bss+0x8): multiple definition of `highsButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x8): first defined here
functions.o:functions.cpp:(.bss+0xc): multiple definition of `creditsButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0xc): first defined here
functions.o:functions.cpp:(.bss+0x10): multiple definition of `quitButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x10): first defined here
functions.o:functions.cpp:(.bss+0x14): multiple definition of `menuButton'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x14): first defined here
functions.o:functions.cpp:(.bss+0x18): multiple definition of `shipStopped'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x18): first defined here
functions.o:functions.cpp:(.bss+0x1c): multiple definition of `shipMoving'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x1c): first defined here
functions.o:functions.cpp:(.bss+0x20): multiple definition of `laserSprite'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x20): first defined here
functions.o:functions.cpp:(.bss+0x24): multiple definition of `smallAsteroid'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x24): first defined here
functions.o:functions.cpp:(.bss+0x28): multiple definition of `medAsteroid'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x28): first defined here
functions.o:functions.cpp:(.bss+0x2c): multiple definition of `largeAsteroid'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x2c): first defined here
functions.o:functions.cpp:(.bss+0x40): multiple definition of `score'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x40): first defined here
functions.o:functions.cpp:(.bss+0x68): multiple definition of `numOfScores'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x68): first defined here
functions.o:functions.cpp:(.bss+0x6c): multiple definition of `points'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x6c): first defined here
functions.o:functions.cpp:(.bss+0x80): multiple definition of `name'
asteroidsMain.o:asteroidsMain.cpp:(.bss+0x80): first defined here
collect2.exe: error: ld returned 1 exit status

C:\Users\Greg\Documents\Asteroids B\Makefile.win:25: recipe for target 'POPE_Greg_Asteroids.exe' failed
mingw32-make.exe: *** [POPE_Greg_Asteroids.exe] Error 1
2

There are 2 answers

0
Michal Horanský On

Add those files in same project.

0
BugSquasher On

Variables declared in a header need to be marked as extern and be mirrored in a separate source file. If each source file includes your header as is, there will be multiple definitions of the same objects in different files and the redefinition errors will occur upon linking.