#include <world.h> #include <ui.h> #define getWidth(w) ((w->lineCount-GEN_INC)*HLINE) // Calculates the width of world 'w' #define GEN_INC 10 // Defines at what interval y values should be calculated for the array 'line'. // As explained in World(), the last few lines in the array 'line' are incorrectly calculated // or not calculated at all, so GEN_INC is also used to decrease 'lineCount' in functions like draw() // and detect(). #define GRASS_HEIGHT 4 // Defines how long the grass layer of a line should be in multiples of HLINE. #define DRAW_Y_OFFSET 50 // Defines how many pixels each layer should be offset from each other on the y axis when drawn. #define DRAW_SHADE 30 // Defines a shade increment for draw() #define INDOOR_FLOOR_HEIGHT 100 // Defines how high the base floor of an IndoorWorld should be extern std::vector<Entity *>entity; void safeSetColor(int r,int g,int b){ // safeSetColor() is an alternative to directly using glColor3ub() to set if(r>255)r=255; // the color for OpenGL drawing. safeSetColor() checks for values that are if(g>255)g=255; // outside the range of an unsigned character and sets them to a safer value. if(b>255)b=255; if(r<0)r=0; if(g<0)g=0; if(b<0)b=0; glColor3ub(r,g,b); } World::World(void){ } void World::generate(unsigned int width){ // Generates the world and sets all variables contained in the World class. unsigned int i; // Used for 'for' loops float inc; // See line 40 lineCount=width+GEN_INC; // Sets line count to the desired width plus GEN_INC to remove incorrect line calculations. line=(struct line_t *)calloc(lineCount,sizeof(struct line_t)); // Allocate memory for the array 'line' line[0].y=80; // Sets a starting value for the world generation to be based off of for(i=GEN_INC;i<lineCount;i+=GEN_INC){ // For every GEN_INCth line in the array 'line' line[i].y=rand()%8-4+line[i-GEN_INC].y; // Generate a y value for it, and correct it if it is too high or low. if(line[i].y<60)line[i].y=60; // y value minimum if(line[i].y>110)line[i].y=110; // y value maximum } for(i=0;i<lineCount-GEN_INC;i++){ // Calculate the rest of the lines as well as set color values for them. if(!i||!(i%GEN_INC)){ // If this is one of the GEN_INCth lines that are already calculated inc=(line[i+GEN_INC].y-line[i].y)/(float)GEN_INC; // Calculate the slope between this line and the line ahead of it, then // divide it by the number of lines inbetween the two. }else{ // If this line's y hasn't been set yet line[i].y=line[i-1].y+inc; // Set it by incrementing the previous line's y by 'inc'. } line[i].color=rand()%20+100; // Generate a color for the dirt area of this line. This value will be used // in the form (where n represents the color) glColor3ub(n,n-50,n-100) line[i].gh[0]=(getRand()%16)/3.5+2; // Create a random grass height so it looks cool line[i].gh[1]=(getRand()%16)/3.5+2; line[i].gs=true; } x_start=0-getWidth(this)/2+GEN_INC/2*HLINE; // Calculate x_start (explained in world.h) behind=infront=NULL; // Set pointers to other worlds to NULL toLeft=toRight=NULL; // to avoid accidental calls to goWorld... functions } World::~World(void){ free(line); // Free (de-allocate) the array 'line' } void World::draw(Player *p){ static float yoff=DRAW_Y_OFFSET; // Initialize stuff static int shade=0; static World *current; int i,is,ie,v_offset,cx_start; struct line_t *cline; current=this; // yeah glClearColor(.1,.3,.6,0); LOOP1: // Check for worlds behind the current one and set 'current' to them if they exist if(current->behind){ // so that once LOOP1 is exited 'current' contains the furthest back world. yoff+=DRAW_Y_OFFSET; shade+=DRAW_SHADE; current=current->behind; goto LOOP1; } LOOP2: // Draw each world v_offset=(p->loc.x-current->x_start)/HLINE; // Calculate the player's offset in the array 'line' using the player's location 'vec' is=v_offset-SCREEN_WIDTH/(yoff/(DRAW_Y_OFFSET/2)); // Set 'i' to that somehow if(is<0)is=0; // If the player is past the start of that world 'i' should start at the beginning // of the world ie=v_offset+SCREEN_WIDTH/(yoff/(DRAW_Y_OFFSET/2)); // Set how many lines should be drawn (the drawing for loop loops from 'i' to 'ie') if(ie>current->lineCount)ie=current->lineCount; // If the player is past the end of that world 'ie' should contain the end of that world cline=current->line; // 'cline' and 'cx_start' only exist to make the for loop clear (and maybe make it faster) cx_start=current->x_start; //shade*=-1; glBegin(GL_QUADS); for(i=is;i<ie-GEN_INC;i++){ // For lines in array 'line' from 'i' to 'ie' cline[i].y+=(yoff-DRAW_Y_OFFSET); // 'yoff' is always one incrementation ahead of what it should be safeSetColor(cline[i].color+shade,cline[i].color-50+shade,cline[i].color-100+shade); // Set the shaded dirt color (safely) glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT); glVertex2i(cx_start+i*HLINE+HLINE,cline[i].y-GRASS_HEIGHT); glVertex2i(cx_start+i*HLINE+HLINE,0); glVertex2i(cx_start+i*HLINE ,0); cline[i].y-=(yoff-DRAW_Y_OFFSET); // Reset 'cline[i]'`s y to what it was } glEnd(); if(current==this){ int ph; ph=(p->loc.x+p->width/2-x_start)/HLINE; // Calculate what line the player is currently on for(i=0;i<lineCount-GEN_INC;i++){ if(p->ground==1&&i<ph+6&&i>ph-6)cline[i].gs=false; else cline[i].gs=true; } for(i=0;i<entity.size()+1;i++){ if(entity[i]->inWorld==this){ entity[i]->draw(); } } p->draw(); } float cgh[2]; glBegin(GL_QUADS); for(i=is;i<ie-GEN_INC;i++){ memcpy(cgh,cline[i].gh,2*sizeof(float)); if(!cline[i].gs){ cgh[0]/=4; cgh[1]/=4; } cline[i].y+=(yoff-DRAW_Y_OFFSET); safeSetColor(shade,150+shade,shade); glVertex2i(cx_start+i*HLINE ,cline[i].y+cgh[0]); glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[0]); glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT); glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT); glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[1]); glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y+cgh[1]); glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y-GRASS_HEIGHT); glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT); cline[i].y-=(yoff-DRAW_Y_OFFSET); } glEnd(); //shade*=-1; safeSetColor(255+shade*2,0+shade,0+shade); for(i=0;i<current->platform.size();i++){ glRectf(current->platform[i].p1.x,current->platform[i].p1.y+yoff-DRAW_Y_OFFSET, current->platform[i].p2.x,current->platform[i].p2.y+yoff-DRAW_Y_OFFSET); } if(current->infront){ // If there's a world in front of the one that was just drawn yoff-=DRAW_Y_OFFSET; // draw it as well. shade-=DRAW_SHADE; current=current->infront; goto LOOP2; }else{ // Otherwise reset static values and return yoff=DRAW_Y_OFFSET; shade=0; } } void World::singleDetect(Entity *e){ unsigned int i; if(e->alive){ i=(e->loc.x+e->width/2-x_start)/HLINE; // Calculate what line the player is currently on if(e->type==STRUCTURET||e->loc.y<line[i].y){ e->vel.y=0; e->ground=true; e->loc.y=line[i].y-.001*deltaTime; if(e->type==STRUCTURET){ //std::cout<<e->loc.x<<" "<<e->loc.y<<std::endl; return; } }else if(e->loc.y>line[i].y-.002*deltaTime){ // Snap the player to the top of that line if the player is inside it for(i=0;i<platform.size();i++){ if(((e->loc.x+e->width>platform[i].p1.x)&(e->loc.x+e->width<platform[i].p2.x))|| ((e->loc.x<platform[i].p2.x)&(e->loc.x>platform[i].p1.x))){ if(e->loc.y>platform[i].p1.y&&e->loc.y<platform[i].p2.y){ if(e->vel.y<0){ e->vel.y=0; e->loc.y=platform[i].p2.y; e->ground=2; return; } } } } e->vel.y-=.001*deltaTime; } if(e->loc.x<x_start){ // Keep the player inside world bounds (ui.cpp handles world jumping) e->vel.x=0; e->loc.x=x_start+HLINE/2; }else if(e->loc.x+e->width+HLINE>x_start+getWidth(this)){ e->vel.x=0; e->loc.x=x_start+getWidth(this)-e->width-HLINE; } } } extern unsigned int newEntityCount; void World::detect(Player *p){ unsigned int i; singleDetect(p); for(i=0;i<entity.size()+1;i++){ if(entity[i]->inWorld==this){ singleDetect(entity[i]); } } } /* * The rest of these functions are explained well enough in world.h ;) */ void World::addLayer(unsigned int width){ if(behind){ behind->addLayer(width); return; } behind=new World(); behind->generate(width); behind->infront=this; } World *World::goWorldLeft(Player *p){ if(toLeft&&p->loc.x<x_start+HLINE*10){ p->loc.x=toLeft->x_start+getWidth(toLeft)-HLINE*10; p->loc.y=toLeft->line[0].y; return toLeft; } return this; } World *World::goWorldRight(Player *p){ if(toRight&&p->loc.x+p->width>x_start+getWidth(this)-HLINE*10){ p->loc.x=toRight->x_start+HLINE*10; p->loc.y=toRight->line[toRight->lineCount-GEN_INC-1].y; return toRight; } return this; } World *World::goWorldBack(Player *p){ if(behind&&p->loc.x>(int)(0-getWidth(behind)/2)&&p->loc.x<getWidth(behind)/2){ return behind; } return this; } World *World::goWorldFront(Player *p){ if(infront&&p->loc.x>(int)(0-getWidth(infront)/2)&&p->loc.x<getWidth(infront)/2){ return infront; } return this; } void World::addPlatform(float x,float y,float w,float h){ platform.push_back((Platform){{x,y},{x+w,y+h}}); } World *World::goInsideStructure(Player *p){ unsigned int i; for(i=0;i<entity.size();i++){ if(entity[i]->type==-1){ if(entity[i]->inWorld==this){ if(p->loc.x>entity[i]->loc.x&&p->loc.x+p->width<entity[i]->loc.x+entity[i]->width){ return (World *)((Structures *)entity[i])->inside; } }else if(((Structures *)entity[i])->inside==this){ p->loc.x=entity[i]->loc.x+entity[i]->width/2-p->width/2; p->loc.y=entity[i]->loc.y+HLINE; return (World *)entity[i]->inWorld; } } } return this; } void World::addHole(unsigned int start,unsigned int end){ unsigned int i; for(i=start;i<end;i++){ line[i].y=0; } } IndoorWorld::IndoorWorld(void){ } IndoorWorld::~IndoorWorld(void){ free(line); } void IndoorWorld::generate(unsigned int width){ // Generates a flat area of width 'width' unsigned int i; // Used for 'for' loops lineCount=width+GEN_INC; // Sets line count to the desired width plus GEN_INC to remove incorrect line calculations. line=(struct line_t *)calloc(lineCount,sizeof(struct line_t)); // Allocate memory for the array 'line' for(i=0;i<lineCount;i++){ // Indoor areas don't have to be directly on the ground (i.e. 0)... line[i].y=INDOOR_FLOOR_HEIGHT; } behind=infront=NULL; // Set pointers to other worlds to NULL toLeft=toRight=NULL; // to avoid accidental calls to goWorld... functions x_start=0-getWidth(this)/2+GEN_INC/2*HLINE; // Calculate x_start (explained in world.h) } void IndoorWorld::draw(Player *p){ int i,ie,v_offset; v_offset=(p->loc.x-x_start)/HLINE; // Calculate the player's offset in the array 'line' using the player's location 'vec' i=v_offset-SCREEN_WIDTH/2; // um if(i<0)i=0; // If the player is past the start of that world 'i' should start at the beginning // of the world ie=v_offset+SCREEN_WIDTH/2; // Set how many lines should be drawn (the drawing for loop loops from 'i' to 'ie') if(ie>lineCount)ie=lineCount; // If the player is past the end of that world 'ie' should contain the end of that world glClearColor(.3,.1,0,0); glBegin(GL_QUADS); for(i=i;i<ie-GEN_INC;i++){ // For lines in array 'line' from 'i' to 'ie' safeSetColor(150,100,50); glVertex2i(x_start+i*HLINE ,line[i].y); // Draw the base floor glVertex2i(x_start+i*HLINE+HLINE,line[i].y); glVertex2i(x_start+i*HLINE+HLINE,line[i].y-50); glVertex2i(x_start+i*HLINE ,line[i].y-50); } glEnd(); for(i=0;i<entity.size()+1;i++){ if(entity[i]->inWorld==this) entity[i]->draw(); } p->draw(); }