#include #include #ifdef __EMSCRIPTEN__ #include #endif #include #include #include "World.h" using SDLInitType=std::pair>; struct Context { // initial window size int win_width=640; int win_height=480; // some other vars bool quit = false; Uint32 frameTime; Uint32 lastFrameTime = 0; Uint32 deltaTime = 0; Uint32 fpsTime = 0; Uint32 fpsCount = 0; Uint32 fps = 0; Uint32 mouse_x=0; Uint32 mouse_y=0; SDL_Renderer *ren; SDLInitType sdl_init; std::vector textures; int show_tiles_back=6; int show_tiles_front=10; int show_tiles_size=20; int show_tiles_vertical_move=12; int show_tiles_max_height=800; int show_tiles_water_level=-1; // init world LEVEL 1 World world=1; }; // audio from http://www.brainybetty.com/soundsforpowerpoint2.htm // https://freesound.org/people/ProjectsU012/sounds/341695/ void sdl_play_sound() { static bool first=true; static SDL_AudioSpec wavSpec; static Uint32 wavLength; static Uint8 *wavBuffer; static SDL_AudioDeviceID deviceId; if (first) { SDL_LoadWAV("coin.wav", &wavSpec, &wavBuffer, &wavLength); // open most reasonable default device for playback. deviceId = SDL_OpenAudioDevice(NULL, 0, &wavSpec, NULL, 0); SDL_PauseAudioDevice(deviceId, 0); first=false; } // SDL_LoadWAV("died.wav", &wavSpec, &wavBuffer, &wavLength); // SDL_LoadWAV("music.wav", &wavSpec, &wavBuffer, &wavLength); // SDL_LoadWAV("win.wav", &wavSpec, &wavBuffer, &wavLength); int success = SDL_QueueAudio(deviceId, wavBuffer, wavLength); #ifndef __EMSCRIPTEN__ #endif //SDL_CloseAudioDevice(deviceId); //SDL_FreeWAV(wavBuffer); } SDLInitType sdl_start(int win_width, int win_height) { //First we need to start up SDL, and make sure it went ok if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0){ std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl; return {1,{nullptr,nullptr}}; } std::cout << "SDL_Init OK" << std::endl; atexit(SDL_Quit); //Now create a window with title "Hello World" at 100, 100 on the screen with w:640 h:480 and show it SDL_Window *win = SDL_CreateWindow("Hello World!", 200, 100, win_width, win_height, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE); //Make sure creating our window went ok if (win == nullptr){ std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; return {1,{nullptr,nullptr}}; } std::cout << "SDL_CreateWindow OK" << std::endl; //Create a renderer that will draw to the window, -1 specifies that we want to load whichever //video driver supports the flags we're passing //Flags: SDL_RENDERER_ACCELERATED: We want to use hardware accelerated rendering //SDL_RENDERER_PRESENTVSYNC: We want the renderer's present function (update screen) to be //synchronized with the monitor's refresh rate SDL_Renderer *ren = SDL_CreateRenderer(win, -1,0);//, SDL_RENDERER_SOFTWARE);// | SDL_RENDERER_PRESENTVSYNC|SDL_HINT_RENDER_VSYNC); if (ren == nullptr){ SDL_DestroyWindow(win); std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl; return {1,{win,nullptr}}; } std::cout << "SDL_CreateRenderer OK" << std::endl; return {0,{win,ren}}; } void sdl_stop(SDLInitType sdl_init) { if(sdl_init.second.second!=nullptr)SDL_DestroyRenderer(sdl_init.second.second); if(sdl_init.second.first!=nullptr)SDL_DestroyWindow(sdl_init.second.first); } SDL_Texture* sdl_load_texture(std::string imagePath,int key_r, int key_g, int key_b,SDL_Renderer *ren) { //SDL 2.0 now uses textures to draw things but SDL_LoadBMP returns a surface //this lets us choose when to upload or remove textures from the GPU SDL_Surface *bmp = SDL_LoadBMP(imagePath.c_str()); SDL_SetColorKey(bmp, SDL_TRUE, SDL_MapRGB(bmp->format, key_r, key_g, key_b)); if (bmp == nullptr){ std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl; return nullptr; } std::cout << "SDL_LoadBMP ("<pixels,SDL_MapRGBA(bmp4->format, 255, 0, 255,220), bmp4->h * bmp4->pitch); // SDL_UnlockSurface(bmp4); //To use a hardware accelerated texture for rendering we can create one from //the surface we loaded return tex; } void sdl_put_letter(SDL_Renderer *ren, SDL_Texture *tex,int x,int y, int w, int h, int r, int g, int b, int ch) { if(ch>=123&&ch<=126)ch-=123-40; else if(ch>=97&&ch<=122)ch-=97; else if(ch>=65&&ch<=96)ch-=65; else if(ch>=32&&ch<=63)ch-=32-50; else ch=81; int col=ch%10; int row=ch/10; SDL_SetTextureColorMod(tex,r,g,b); SDL_Rect src={col*8,row*10,8,10}; SDL_Rect dst={x,y,w,h}; SDL_RenderCopy(ren,tex,&src,&dst); } void sdl_put_str(SDL_Renderer *ren, SDL_Texture *tex,int x, int y,int size,std::string str,int r,int g, int b,int length,int win_width,int win_height,int which,int of) { length++; int max_char_width=win_width/length; int max_char_height=max_char_width*10/8; int char_width=max_char_width*size/10; int char_height=max_char_height*size/10; int pos=0; x=(win_width-length*char_width)/2; if(y==-1) { y=win_height/2+(which-1)*max_char_height; } SDL_SetRenderDrawColor(ren, 40,40,40, 255); SDL_Rect rect2={0,y-2,win_width,char_height+4}; SDL_RenderFillRect(ren,&rect2); for(char ch:str) { pos++; sdl_put_letter(ren,tex,x+char_width*pos,y, char_width,char_height, r,g,b, ch); } } // void main_loop(void *arg) { Context *ctx=(Context *)arg; SDL_Event event; // calc delta and FPS ctx->frameTime = SDL_GetTicks(); ctx->deltaTime = ctx->frameTime - ctx->lastFrameTime; ctx->lastFrameTime = ctx->frameTime; ctx->fpsTime+=ctx->deltaTime; ctx->fpsCount++; if(ctx->fpsTime>1000) { ctx->fps=ctx->fpsCount*1000/ctx->fpsTime; ctx->fpsTime=0; ctx->fpsCount=0; } // handle events (mouse & kb & win resize) while(!ctx->quit && SDL_PollEvent(&event)) { if (event.type == SDL_WINDOWEVENT && (event.window.event==SDL_WINDOWEVENT_RESIZED || event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED)) { // SDL_Log("Window %d size changed to %dx%d", // event.window.windowID, event.window.data1, //event.window.data2); ctx->win_width=event.window.data1; ctx->win_height=event.window.data2; ctx->show_tiles_size=ctx->win_height/15; ctx->show_tiles_back=ctx->win_width/ctx->show_tiles_size*0.25; ctx->show_tiles_front=ctx->win_width/ctx->show_tiles_size*1; // while(show_tiles_size%20!=0)show_tiles_size--; } if (event.type==SDL_MOUSEBUTTONDOWN) { ctx->mouse_x=event.button.x; ctx->mouse_y=event.button.y; ctx->world.mouseclick((ctx->mouse_x+ctx->show_tiles_size/2-ctx->world.player.x2)/ctx->show_tiles_size+ctx->world.player.x-ctx->show_tiles_back,ctx->show_tiles_vertical_move-ctx->mouse_y/ctx->show_tiles_size); } switch (event.type) { case SDL_QUIT: ctx->quit = true; break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) ctx->quit = true; break; default: break; } } // clear screen SDL_SetRenderDrawColor(ctx->ren, 0, 0, 0, 255); SDL_RenderClear(ctx->ren); // render visible area for(int i=ctx->world.player.x-ctx->show_tiles_back;iworld.player.x+ctx->show_tiles_front;i++) { int tile_col=i-ctx->world.player.x+ctx->show_tiles_back; if(i<0||i>=ctx->world.bricks.size())continue; SDL_Rect rect={(int)(tile_col*ctx->show_tiles_size-ctx->show_tiles_size*ctx->world.player.x2),(ctx->show_tiles_vertical_move-ctx->world.coins_pos[i])*ctx->show_tiles_size,ctx->show_tiles_size,ctx->show_tiles_size}; SDL_RenderCopy(ctx->ren,ctx->textures[2],NULL,&rect); for(int j=0;jworld.bricks[i].size();j++) { SDL_Rect rect={(int)(tile_col*ctx->show_tiles_size-ctx->show_tiles_size*ctx->world.player.x2),(ctx->show_tiles_vertical_move-ctx->world.bricks[i][j].altitude)*ctx->show_tiles_size,ctx->show_tiles_size,ctx->show_tiles_size}; switch(ctx->world.bricks[i][j].type) { case 0: SDL_RenderCopy(ctx->ren,ctx->textures[4],NULL,&rect); SDL_SetRenderDrawColor(ctx->ren, 150+19*(i%2),115,70, 255); rect.h=ctx->show_tiles_max_height; rect.y+=ctx->show_tiles_size; SDL_RenderFillRect(ctx->ren,&rect); break; case 1: SDL_RenderCopy(ctx->ren,ctx->textures[4],NULL,&rect); break; case 2: SDL_RenderCopy(ctx->ren,ctx->textures[5],NULL,&rect); break; case 3: SDL_RenderCopy(ctx->ren,ctx->textures[6],NULL,&rect); break; case 4: SDL_RenderCopy(ctx->ren,ctx->textures[7],NULL,&rect); break; } } // water level SDL_SetRenderDrawColor(ctx->ren, 100*(i%2),100*(i%2),255, 255); SDL_Rect rect2={(int)(tile_col*ctx->show_tiles_size-ctx->show_tiles_size*ctx->world.player.x2),(int)(ctx->show_tiles_vertical_move*ctx->show_tiles_size-ctx->show_tiles_water_level*ctx->show_tiles_size+sin(ctx->frameTime/1000.0+i)*ctx->show_tiles_size*0.33),ctx->show_tiles_size,ctx->show_tiles_max_height}; SDL_RenderFillRect(ctx->ren,&rect2); } // render player char SDL_Rect rect={(int)((ctx->show_tiles_back-0.5)*ctx->show_tiles_size),(int)((ctx->show_tiles_vertical_move-ctx->world.player.y)*ctx->show_tiles_size-ctx->world.player.y2*ctx->show_tiles_size),ctx->show_tiles_size,ctx->show_tiles_size}; if(ctx->world.player.anim==0)SDL_RenderCopy(ctx->ren,ctx->textures[0],NULL,&rect); else SDL_RenderCopy(ctx->ren,ctx->textures[1],NULL,&rect); // HUD std::stringstream strbuffer; strbuffer << "SCORE:" << ctx->world.coins << " | LVL:" << ctx->world.level << " | FPS:" << ctx->fps << " | RES:"<win_width << "*" << ctx->win_height ; sdl_put_str(ctx->ren, ctx->textures[3],10, 10, 10,strbuffer.str(),222,255,222,60,ctx->win_width,ctx->win_height,1,1); if(ctx->world.player.dead) { sdl_put_str(ctx->ren, ctx->textures[3],300, -1, 10,"LUNATIC LEMMY DIED!",250,100,100,19,ctx->win_width,ctx->win_height,1,2); sdl_put_str(ctx->ren, ctx->textures[3],280, -1, 5,"[click to retry]",200,190,222,15,ctx->win_width,ctx->win_height,2,2); } if(ctx->world.player.win) { sdl_put_str(ctx->ren, ctx->textures[3],300, -1, 10," ! LEVEL UP ",100,250,100,22,ctx->win_width,ctx->win_height,1,2); sdl_put_str(ctx->ren, ctx->textures[3],280, -1, 5,"[click for next level]",100,250,222,22,ctx->win_width,ctx->win_height,2,2); } // // finish rendering SDL_RenderPresent(ctx->ren); if(ctx->world.player.collected_coin) { sdl_play_sound(); ctx->world.player.collected_coin=false; } // progress world simu ctx->world.sim(ctx->deltaTime); #ifdef __EMSCRIPTEN if(ctx->quit)emscripten_cancel_main_loop(); #endif } int main(int, char**){ Context ctx1; Context *ctx=&ctx1; // init SDL and assign SDL_Renderer on success ctx->sdl_init=sdl_start(ctx->win_width,ctx->win_height); if(ctx->sdl_init.first!=0) { sdl_stop(ctx->sdl_init); return ctx->sdl_init.first; } ctx->ren=ctx->sdl_init.second.second; // init Textures ctx->textures.push_back(sdl_load_texture("guy01.bmp",0,0,255,ctx->ren)); ctx->textures.push_back(sdl_load_texture("guy02.bmp",0,0,255,ctx->ren)); ctx->textures.push_back(sdl_load_texture("coin.bmp",255,255,255,ctx->ren)); ctx->textures.push_back(sdl_load_texture("fonts.bmp",0,0,0,ctx->ren)); ctx->textures.push_back(sdl_load_texture("earth01.bmp",0,0,0,ctx->ren)); ctx->textures.push_back(sdl_load_texture("gridder01.bmp",255,255,255,ctx->ren)); ctx->textures.push_back(sdl_load_texture("gridder02.bmp",255,255,255,ctx->ren)); ctx->textures.push_back(sdl_load_texture("gridder03.bmp",255,255,255,ctx->ren)); ctx->show_tiles_size=ctx->win_height/15; ctx->show_tiles_back=ctx->win_width/ctx->show_tiles_size*0.25; ctx->show_tiles_front=ctx->win_width/ctx->show_tiles_size*1; #ifdef __EMSCRIPTEN__ int simulate_infinite_loop = 1; emscripten_set_main_loop_arg(main_loop, ctx, -1, simulate_infinite_loop); #endif while (!ctx->quit) { main_loop(ctx); } //Clean up and quit for(SDL_Texture *tex:ctx->textures) { SDL_DestroyTexture(tex); } sdl_stop(ctx->sdl_init); return 0; }