/* * This code is released under the GNU General Public License. See COPYING for * details. Copyright 2003 John Spray: spray_john@users.sourceforge.net */ #include #include #include #include #include #include "Game.h" #include "Events.h" #include "SoundCore.h" #include "Particle.h" #include "Visual.h" #include "Player.h" #include "Missile.h" #include "Config.h" #include "Ufo.h" #include "Menu.h" #include "Obstacle.h" #include "Vector.h" #include "ScoreBoard.h" #include "PowerUp.h" extern Config GLOB_conf; extern ScoreBoard GLOB_scores; Game::Game(SDL_Surface* newscreen) { quit=0; verbose=GLOB_conf.verbose; screen=newscreen; timescale=1.0f; score=0; g.y=-0.0001; spawnticker=0; spawndelay=5000; } Game::~Game() { } int Game::Happen() { long frames=0; struct timeval tv; struct timeval oldtv; player=new Player(this); if(GLOB_conf.twoplayer) player2=new Player(this); visual=new Visual(this); visual->InitGL(); sound=new SoundCore; sound->SetEar(&visual->GetMainCam()->s,&visual->GetMainCam()->v, &visual->GetMainCam()->face,&visual->GetMainCam()->up); visual->ShowLoading(0.1f); visual->LoadTexture("arenamesh.png"); visual->LoadTexture("arenabase.png"); visual->LoadTexture("arenawall.png"); visual->LoadTexture("skyup.png"); visual->LoadTexture("skydown.png"); visual->LoadTexture("skyleft.png"); visual->LoadTexture("skyright.png"); visual->LoadTexture("skyback.png"); visual->LoadTexture("skyfront.png"); visual->ShowLoading(0.5f); visual->LoadTexture("shield.png"); visual->LoadTexture("explosion1.png"); visual->LoadTexture("missiletrail.png"); visual->LoadTexture("shadow.png"); visual->ShowLoading(0.65f); visual->LoadTexture("driveparticle.png"); visual->LoadTexture("lifeicon.png"); visual->LoadTexture("skyscraper.png"); visual->LoadTexture("menucursor.png"); visual->LoadTexture("smoke1.png"); visual->LoadTexture("powerupshell.png"); visual->LoadTexture("tracer.png"); visual->LoadTexture("muzzleflash.png"); visual->LoadTexture("halfdriveparticle.png"); visual->ShowLoading(0.8f); char *soundfiles[]={ "fire.wav","boom.wav","engine.wav","ufo.wav", "ufodie.wav","gotthelife.wav","cannon.wav", "spotted.wav","playerdie.wav","shield.wav", "mine.wav",NULL}; for(int i=0;soundfiles[i];i++){ if(verbose) printf("Game::Happen: Loading wavefile: %s\n",soundfiles[i]); sound->LoadSample(soundfiles[i]); } //Note that textures corresponding to meshes //don't need to be preloaded, LoadMesh does it visual->LoadMesh("player.mesh"); visual->LoadMesh("ufo.mesh"); visual->LoadMesh("mine.mesh"); evs=new Events; evs->game=this; evs->keyboardtarget=player; arena=new Arena(this); arena->MakeObstacles(); arena->GenerateGeometry(); missilelist= new LList; ufolist=new LList; minelist=new LList; poweruplist=new LList; visual->ShowLoading(0.9f); evs->targetcam=visual->GetMainCam(); player->Respawn(); if(GLOB_conf.twoplayer) player2->Respawn(); #ifdef RELEASE SDL_WM_GrabInput(SDL_GRAB_ON); #endif visual->ShowLoading(1.0f);//yes,yes, it's pointless #ifdef LINUX gettimeofday(&oldtv,NULL); #else ticks=SDL_GetTicks(); #endif startticks=SDL_GetTicks(); dtf=0.0f; while(!quit){ sleep(0); evs->HandleEvents(); visual->UpdateParticles(); //3x detail on player physics: more detailed //observations by player than other physics dtf/=3.0f; player->Physics(); player->Physics(); player->Physics(); dtf*=3.0f; if(GLOB_conf.twoplayer) player2->Physics(); SpawnUfos(); UpdateUfos(); UpdateMines(); UpdateMissiles(); UpdatePowerUps(); sound->Update(); visual->Draw(); frames++; #ifdef LINUX gettimeofday(&tv,NULL); dtf=(tv.tv_sec-oldtv.tv_sec)*1000.0f+(tv.tv_usec-oldtv.tv_usec)/1000.0f; if(timescale!=1.0f) dtf*=timescale; oldtv=tv; #else dtf=float(SDL_GetTicks()-ticks)*timescale; ticks=SDL_GetTicks(); #endif if(GLOB_conf.actslow) SDL_Delay(28); } SDL_WM_GrabInput(SDL_GRAB_OFF); player->Unspawn(); printf("Game::Happen: %d frames, %.2ffps, on average.\n",(int)frames,float(1000*frames)/float(SDL_GetTicks()-startticks)); printf("Game::Happen: score=%d\n",score); delete poweruplist; delete missilelist; delete ufolist; delete minelist; delete player; if(GLOB_conf.twoplayer) delete player2; delete sound; delete evs; delete visual; delete arena; return quit; } void Game::NewMissile(Missile* newmissile) { newmissile->game=this; missilelist->Add(newmissile); } void Game::NewPowerUp(PowerUp* newpowerup) { newpowerup->game=this; poweruplist->Add(newpowerup); } void Game::NewUfo(Ufo* newufo) { Ufo* created; newufo->game=this; created=ufolist->Add(newufo); created->Live(); } void Game::NewMine(Mine* newmine) { Mine* created; newmine->game=this; created=minelist->Add(newmine); created->Live(); } void Game::SpawnUfos() { int failcount; Vector place; Vector dist; spawnticker+=dtf; if(spawnticker1000.0f) spawndelay*=0.95f; if(ufolist->count>=20) return; Ufo newufo; place.y=3.0f; place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; failcount=0; //be careful that sniffradius fits in the arena! while(failcount<50&&(dist.Mag2() < newufo.sniffradius*newufo.sniffradius || arena->Blocked(place,0) || !arena->Collision(place,player->s))){ failcount++; if(verbose) printf("Game::SpawnUfos: place %f,%f,%f is unacceptable. " "It is %f from the player, at %f,%f,%f\n",place.x,place.y,place.z, dist.Mag(),player->s.x,player->s.y,player->s.z); place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; } if(verbose) printf("Game::SpawnUfos: place=%f,%f,%f\n",place.x,place.y,place.z); newufo.s=place; NewUfo(&newufo); } void Game::UpdateMissiles() { LListItem *item; Particle newpart; Vector temp,temp1,temp2,d; d=10000.0f; item=missilelist->head; while(item){ if(item->data.collided){ item=missilelist->Del(item); continue; } if(player->powerup==POWERUP_GUIDANCE && player->poweruptimer>0.0f){ Ufo* target=NULL; LListItem* ufoitem; target=NULL; ufoitem=ufolist->head; while(ufoitem){ temp=ufoitem->data.s-item->data.s; if(temp.Mag2() < 50.0f*50.0f && temp.Mag2() < d.Mag2()){ target=&ufoitem->data; d=temp; } ufoitem=ufoitem->next; } if(target){ d.Unitize(); item->data.a=d*0.001f; //item->data.v*=pow(0.99f,dtf); temp1=item->data.v; temp1.Unitize(); temp2=item->data.a; temp2.Unitize(); item->data.v*=temp1|temp2; } } item->data.Physics(); /*newpart.s=item->data.s; newpart.v=0.0f; strcpy(newpart.texfile,"missiletrail.png"); newpart.rad=1; newpart.blendmode=GL_ONE; newpart.life=300; visual->NewParticle(&newpart);*/ item=item->next; } } void Game::UpdatePowerUps() { LListItem* item; item=poweruplist->head; while(item){ item->data.Update(); if(item->data.done){ item=poweruplist->Del(item); continue; } item=item->next; } } void Game::UpdateUfos() { LListItem* ufoitem; ufoitem=ufolist->head; while(ufoitem){ if(!ufoitem->data.alive){ ufoitem=ufolist->Del(ufoitem); continue; } ufoitem->data.Physics(); ufoitem->data.Pilot(); ufoitem=ufoitem->next; } } void Game::UpdateMines() { LListItem* mineitem; mineitem=minelist->head; while(mineitem){ if(!mineitem->data.alive){ mineitem=minelist->Del(mineitem); continue; } mineitem->data.Physics(); mineitem=mineitem->next; } } //blatant repetitousness lies herein.... void Game::IncScore(int more) { PowerUp newpup; Vector place,dist; int failcount; if((score+more)-(score+more)%40000>score-score%40000){ newpup.nature=POWERUP_LIFE; place.y=newpup.s.y; place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; failcount=0; while(failcount<50&&arena->Blocked(place,0)){ failcount++; place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; } newpup.s=place; NewPowerUp(&newpup); } if((score+more)-(score+more)%3000>score-score%3000){ int rannum=rand(); if(rannumhalfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; failcount=0; while(failcount<50&&arena->Blocked(place,0)){ failcount++; place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth; dist=place-player->s; } newpup.s=place; NewPowerUp(&newpup); } score+=more; } void Game::ShowMenu() { menufunc menureturn; SDL_GrabMode grabcache=SDL_WM_GrabInput(SDL_GRAB_QUERY); SDL_WM_GrabInput(SDL_GRAB_OFF); float tempvolume=sound->GetVolume(); long start=SDL_GetTicks(); sound->SetPaused(1); Menu* menu; char* labels[]={"Continue","Volume:","Abort game","Exit Excido",NULL}; menufunc functions[]={MENU_CONTINUE,MENU_FLOATONE,MENU_QUIT,MENU_QUITFULL}; void* targets[]={NULL,(void*)&tempvolume,NULL,NULL}; menu=new Menu(labels,functions,targets,visual); menu->solidbackground=0; menureturn=menu->Happen(); delete menu; if(menureturn==MENU_QUIT) GameOver(1); else if(menureturn==MENU_QUITFULL) GameOver(2); sound->SetVolume(tempvolume); sound->SetPaused(0); startticks+=SDL_GetTicks()-start;//for benefit of avg frame rate calculation in Game::Happen ticks=SDL_GetTicks();//look at where Events::HandleEvents is called in Happen, doing this is sane-ish dtf=0;//ie skip a simulation step after returning - it's that or a real big one :-)*/ SDL_WM_GrabInput(grabcache); } void Game::Resize(int w,int h) { GLOB_conf.screenx=w; GLOB_conf.screeny=h; screen=SDL_SetVideoMode(GLOB_conf.screenx,GLOB_conf.screeny,GLOB_conf.bpp,SDL_OPENGLBLIT | SDL_RESIZABLE); visual->ResetCams(); evs->targetcam=visual->GetMainCam(); sound->SetEar(&visual->GetMainCam()->s,&visual->GetMainCam()->v, &visual->GetMainCam()->face,&visual->GetMainCam()->up); } void Game::GameOver(int quitstate) { char name[64]="Player"; if(GLOB_scores.IsHigh(score)){ Menu* menu; char* labels[]= { "Game over! High Score!", "Your Name:", "[Enter your name and press return]",NULL }; menufunc functions[]={MENU_NOSELECT,MENU_STRING,MENU_NOSELECT}; void* targets[]={NULL,name,NULL}; menu=new Menu(labels,functions,targets,visual); menu->solidbackground=0; menu->Happen(); delete menu; GLOB_scores.NewScore(score,name); visual->LoadTexture("menuback.png"); GLOB_scores.Display(visual); visual->UnLoadTexture("menuback.png"); } else if(quitstate!=2){ Menu* menu; char* labels[]={"Game over!",NULL}; menufunc functions[]={MENU_CONTINUE}; void* targets[]={NULL}; menu=new Menu(labels,functions,targets,visual); menu->solidbackground=0; menu->Happen(); delete menu; } quit=quitstate; }