#include // fopen #include // see millis() #include #include #include #include /* * TICKS_PER_SEC & MSEC_PER_TICK * * The game's main loop mainly takes care of two things: drawing to the * screen and handling game logic, from user input to world gravity stuff. * The call for rendering is made every time the main loop loops, and then * uses interpolation for smooth drawing to the screen. However, the call * for logic would be preferred to be run every set amount of time. * * The logic loop is currently implemented to run at a certain interval * that we call a 'tick'. As one may now guess, TICKS_PER_SEC defines the * amount of ticks that should be made every second. MSEC_PER_TICK then * does a simple calculation of how many milliseconds elapse per each * 'tick'. Simple math is then done in the main loop using MSEC_PER_TICK * to call the logic handler when necessary. * */ #define TICKS_PER_SEC 20 #define MSEC_PER_TICK (1000/TICKS_PER_SEC) /* * window & mainGLContext * * In order to draw using SDL and its openGL facilities SDL requires * an SDL_Window object, which spawns a window for the program to draw * to. Once the SDL_Window is initialized, an SDL_GLContext is made so * that openGL calls can be made to SDL. The game requires both of these * variables to initialize. * */ SDL_Window *window = NULL; SDL_GLContext mainGLContext = NULL; /* * bgImage contains the GLuint returned when creating a texture for the * background image. Currently there is only one background image for the * main world; this will be changed and bgImage likely removed once better * backgrounds are implemented. * */ static GLuint bgImage; /* * gameRunning * * This is one of the most important variables in the program. The main * loop of the game is set to break once this variable is set to false. * The only call to modify this variable is made in src/ui.cpp, where it * is set to false if either an SDL_QUIT message is received (the user * closes the window through their window manager) or if escape is pressed. * */ bool gameRunning = true; /* * currentWorld - This is a pointer to the current world that the player * is in. Most drawing/entity handling is done through this * variable. This should only be changed when layer switch * buttons are pressed (see src/ui.cpp), or when the player * enters a Structure/Indoor World (see src/ui.cpp again). * * player - This points to a Player object, containing everything for * the player. Most calls made with currentWorld require a * Player object as an argument, and glOrtho is set based * off of the player's coordinates. This is probably the one * Entity-derived object that is not pointed to in the entity * array. * * entity - Contains pointers to 'all' entities that have been created in * the game, including NPCs, Structures, and Mobs. World draws * and entity handling done by the world cycle through entities * using this array. Entities made that aren't added to this * array probably won't be noticable by the game. * * npc - An array of all NPCs in the game. It's not exactly clear how * NPC initing is done, their constructed in this array, then set * to be pointed to by entity, then maybe spawned with Entity->spawn(). * See src/entities.cpp for more. * This variable might be referenced as an extern in other files. * * build - An array of all Structures in the game. Entries in entity point to * these, allowing worlds to handle the drawing and stuff of these. * See src/entities.cpp for more. * * mob - An array of all Mobs in the game, entity entries should point to these * so the world can take care of them. See src/entities.cpp for more. * */ World *currentWorld=NULL; Player *player; std::vector entity; std::vector npc; std::vector build; std::vector mob; /* * tickCount contains the number of ticks generated since main loop entrance. * This variable might be used anywhere. * * deltaTime is used for interpolation stuff. * * Pretty sure these variables are considered static as they might be externally * referenced somewhere. */ unsigned int tickCount = 0; unsigned int deltaTime = 0; /* * names is used to open a file containing all possible NPC names. It is externally * referenced in src/entities.cpp for getting random names. * */ FILE *names; /* * These variables are used by SDL_mixer to create sound. * horn is not currently used, although it may be set. * */ Mix_Music *music; Mix_Chunk *horn; /* * loops is used for texture animation. It is believed to be passed to entity * draw functions, although it may be externally referenced instead. * */ unsigned int loops = 0; // Used for texture animation extern void initEverything(void); // Sets up the worlds and NPCs, see gameplay.cpp void logic(); // Handles entity logic and input events void render(); // Draws everything to the screen void mainLoop(void); // Runs render() and logic() // Used for millisecond timing unsigned int millis(void){ std::chrono::system_clock::time_point now=std::chrono::system_clock::now(); return std::chrono::duration_cast(now.time_since_epoch()).count(); } int main(int argc, char *argv[]){ // Initialize SDL if(glewInit() < 0){ std::cout << "GLEW was not able to initialize! Error: " << std::endl; return -1; } if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0){ std::cout << "SDL was not able to initialize! Error: " << SDL_GetError() << std::endl; return -1; } atexit(SDL_Quit); // Initialize SDL_image if(!(IMG_Init(IMG_INIT_PNG|IMG_INIT_JPG)&(IMG_INIT_PNG|IMG_INIT_JPG))){ std::cout<<"Could not init image libraries!\n"<= millis()){ logic(); prevTime = millis(); } player->loc.y+=player->vel.y*deltaTime; player->loc.x+=(player->vel.x*player->speed)*deltaTime; for(int i=0;i<=entity.size();i++){ entity[i]->loc.x += entity[i]->vel.x * deltaTime; entity[i]->loc.y += entity[i]->vel.y * deltaTime; if(entity[i]->vel.x<0)entity[i]->left=true; if(entity[i]->vel.x>0)entity[i]->left=false; } if(++debugDiv==20){ fps=1000/deltaTime; debugDiv=0; }else if(!(debugDiv%10)){ debugY = player->loc.y; } //mx = (ui::mouse.x+player->loc.x) - (SCREEN_WIDTH/2); //my = SCREEN_HEIGHT - ui::mouse.y; render(); } void render(){ //a matrix is a blank canvas for the computer to draw on, the matrices are stored in a "stack" //GL_PROJECTION has 2 matrices //GL_MODELVIEW has 32 matrices glMatrixMode(GL_PROJECTION); //set the matrix mode as projection so we can set the ortho size and the camera settings later on glPushMatrix(); //push the matrix to the top of the matrix stack glLoadIdentity(); //replace the entire matrix stack with the updated GL_PROJECTION mode glOrtho(player->loc.x-SCREEN_WIDTH/2,player->loc.x+SCREEN_WIDTH/2,0,SCREEN_HEIGHT,-1,1); glMatrixMode(GL_MODELVIEW); //set the matrix to modelview so we can draw objects glPushMatrix(); //push the matrix to the top of the matrix stack glLoadIdentity(); //replace the entire matrix stack with the updated GL_MODELVIEW mode glPushMatrix(); //basically here we put a blank canvas (new matrix) on the screen to draw on glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT ); glClear(GL_COLOR_BUFFER_BIT); //clear the matrix on the top of the stack /************************** **** RENDER STUFF HERE **** **************************/ glEnable(GL_TEXTURE_2D); // Draw the background image glBindTexture(GL_TEXTURE_2D,bgImage); // glBegin(GL_QUADS); // glTexCoord2i(0,1);glVertex2i(-SCREEN_WIDTH*2,0); // glTexCoord2i(1,1);glVertex2i( SCREEN_WIDTH*2,0); // glTexCoord2i(1,0);glVertex2i( SCREEN_WIDTH*2,SCREEN_HEIGHT); // glTexCoord2i(0,0);glVertex2i(-SCREEN_WIDTH*2,SCREEN_HEIGHT); // glEnd(); // glDisable(GL_TEXTURE_2D); // player->near=true; // Ensure the player's name is always drawn currentWorld->draw(player); // Draw the world & the player player->inv->draw(); // ui::draw(); // Draw any UI elements if they need to be if(ui::debug){ // Draw the debug screen if it has been enabled ui::setFontSize(16); ui::putText(player->loc.x-SCREEN_WIDTH/2,SCREEN_HEIGHT-ui::fontSize,"FPS: %d\nG:%d\nRes: %ux%u\nE: %d\nPOS: (x)%+.2f\n (y)%+.2f\nQc: %u", fps,player->ground,SCREEN_WIDTH,SCREEN_HEIGHT,entity.size(),player->loc.x,debugY,player->qh.current.size()); } glColor3ub(255,255,255); // Draw the mouse glBegin(GL_TRIANGLES); // glVertex2i(ui::mouse.x,ui::mouse.y); // glVertex2i(ui::mouse.x+HLINE*3.5,ui::mouse.y); // glVertex2i(ui::mouse.x,ui::mouse.y-HLINE*3.5); // glEnd(); // /************************** **** END RENDERING **** **************************/ glPopMatrix(); //take the matrix(s) off the stack to pass them to the renderer SDL_GL_SwapWindow(window); //give the stack to SDL to render it } void logic(){ ui::handleEvents(); // Handle keyboard / mouse input currentWorld->detect(player); // Handle gravity and world bounds for(int i=0;i<=entity.size();i++){ // Loop through the whole entity stack if(entity[i]->alive){ // If the entity is alive switch(entity[i]->type){ case NPCT: // Handle NPCs if(entity[i]->canMove)entity[i]->wander((rand()%120 + 30), &entity[i]->vel); // Make the NPC wander // Check if the NPC is near the player and handle potential interaction if(pow((entity[i]->loc.x - player->loc.x),2) + pow((entity[i]->loc.y - player->loc.y),2) <= pow(40*HLINE,2)){ // NPC in range if(ui::mouse.x >= entity[i]->loc.x && ui::mouse.x <= entity[i]->loc.x + entity[i]->width && // Mouse is hovering over NPC ui::mouse.y >= entity[i]->loc.y && ui::mouse.y <= entity[i]->loc.y + entity[i]->width){ entity[i]->near=true; // Allows the NPC's name to be drawn if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT)){ // If right click entity[i]->interact(); // Interact with the player //Mix_PlayChannel( -1, horn, 0); // Audio feedback } }else entity[i]->near=false; } break; case MOBT: // Handle mobs entity[i]->wander((rand()%240 + 15),&entity[i]->vel); // Make the mob wander break; default: break; } } } loops++; }