Tue, 18 Mar 2014 01:27:15 +0000
Update PTdecode to handle output from other Ptouch drivers
philpem@5 | 1 | /* |
philpem@5 | 2 | # |
philpem@5 | 3 | # File : jawbreaker.cpp |
philpem@5 | 4 | # ( C++ source file ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : A funny game featuring small colored balls. |
philpem@5 | 7 | # This file is a part of the CImg Library project. |
philpem@5 | 8 | # ( http://cimg.sourceforge.net ) |
philpem@5 | 9 | # |
philpem@5 | 10 | # Copyright : David Tschumperle |
philpem@5 | 11 | # ( http://www.greyc.ensicaen.fr/~dtschump/ ) |
philpem@5 | 12 | # |
philpem@5 | 13 | # License : CeCILL v2.0 |
philpem@5 | 14 | # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) |
philpem@5 | 15 | # |
philpem@5 | 16 | # This software is governed by the CeCILL license under French law and |
philpem@5 | 17 | # abiding by the rules of distribution of free software. You can use, |
philpem@5 | 18 | # modify and/ or redistribute the software under the terms of the CeCILL |
philpem@5 | 19 | # license as circulated by CEA, CNRS and INRIA at the following URL |
philpem@5 | 20 | # "http://www.cecill.info". |
philpem@5 | 21 | # |
philpem@5 | 22 | # As a counterpart to the access to the source code and rights to copy, |
philpem@5 | 23 | # modify and redistribute granted by the license, users are provided only |
philpem@5 | 24 | # with a limited warranty and the software's author, the holder of the |
philpem@5 | 25 | # economic rights, and the successive licensors have only limited |
philpem@5 | 26 | # liability. |
philpem@5 | 27 | # |
philpem@5 | 28 | # In this respect, the user's attention is drawn to the risks associated |
philpem@5 | 29 | # with loading, using, modifying and/or developing or reproducing the |
philpem@5 | 30 | # software by the user in light of its specific status of free software, |
philpem@5 | 31 | # that may mean that it is complicated to manipulate, and that also |
philpem@5 | 32 | # therefore means that it is reserved for developers and experienced |
philpem@5 | 33 | # professionals having in-depth computer knowledge. Users are therefore |
philpem@5 | 34 | # encouraged to load and test the software's suitability as regards their |
philpem@5 | 35 | # requirements in conditions enabling the security of their systems and/or |
philpem@5 | 36 | # data to be ensured and, more generally, to use and operate it in the |
philpem@5 | 37 | # same conditions as regards security. |
philpem@5 | 38 | # |
philpem@5 | 39 | # The fact that you are presently reading this means that you have had |
philpem@5 | 40 | # knowledge of the CeCILL license and that you accept its terms. |
philpem@5 | 41 | # |
philpem@5 | 42 | */ |
philpem@5 | 43 | |
philpem@5 | 44 | #include "CImg.h" |
philpem@5 | 45 | using namespace cimg_library; |
philpem@5 | 46 | |
philpem@5 | 47 | // The lines below are necessary when using a non-standard compiler as visualcpp6. |
philpem@5 | 48 | #ifdef cimg_use_visualcpp6 |
philpem@5 | 49 | #define std |
philpem@5 | 50 | #endif |
philpem@5 | 51 | #ifdef max |
philpem@5 | 52 | #undef max |
philpem@5 | 53 | #endif |
philpem@5 | 54 | |
philpem@5 | 55 | // Start main procedure |
philpem@5 | 56 | //---------------------- |
philpem@5 | 57 | int main(int argc, char **argv) { |
philpem@5 | 58 | |
philpem@5 | 59 | // Display help (if option '-h' or '--help' specified) and retrieve program arguments |
philpem@5 | 60 | cimg_usage("A small and funny game featuring colored balls.\n (by David Tschumperle)."); |
philpem@5 | 61 | const char *score_file = cimg_option("-s",(char*)0,"Specify score file to use (0=default file)."); |
philpem@5 | 62 | cimg_help("\n" |
philpem@5 | 63 | "** Quick Help *********************************************************\n\n" |
philpem@5 | 64 | "Goal : Delete the board by clicking on groups of adjacent colored balls\n" |
philpem@5 | 65 | " (a group is made of at least two balls with the same color).\n" |
philpem@5 | 66 | " Suppressing large sets gives higher scores.\n\n" |
philpem@5 | 67 | "In-game keys : - BACKSPACE or SPACE = Undo last move\n" |
philpem@5 | 68 | " - CTRL+F = Toggle fullscreen mode\n" |
philpem@5 | 69 | " - ESC = Quit application\n" |
philpem@5 | 70 | " - Q = End current game\n\n" |
philpem@5 | 71 | "*********************************************************************"); |
philpem@5 | 72 | |
philpem@5 | 73 | // Load score file if available |
philpem@5 | 74 | CImgList<unsigned int> score_history; |
philpem@5 | 75 | char filename_history[1024]; |
philpem@5 | 76 | std::sprintf(filename_history,"%s%s",score_file?"":cimg::temporary_path(),score_file?score_file:"/jawbreaker.score"); |
philpem@5 | 77 | std::FILE *file = std::fopen(filename_history,"r"); |
philpem@5 | 78 | if (file) { std::fclose(file); score_history = CImg<unsigned int>::get_load_dlm(filename_history).get_split('y'); } |
philpem@5 | 79 | |
philpem@5 | 80 | // Create ball graphics |
philpem@5 | 81 | const unsigned int W = 12, H = 14, Wi = (W<<5), Hi = (H<<5); |
philpem@5 | 82 | unsigned int score = 0, previous_score = 0, shape_score = 0, |
philpem@5 | 83 | best_score = score_history?score_history.max():0U; |
philpem@5 | 84 | |
philpem@5 | 85 | const CImg<> colors(3,7,1,1, 255,255,255, 205,0,230, 0,235,0, 235,255,0, 235,0,0, 0,128,255, 450,350,300); |
philpem@5 | 86 | const unsigned char |
philpem@5 | 87 | white[] = { 255,255,255 }, orange[] = { 255,128,64 }, yellow[] = { 255,255,64 }, red[] = { 255,64,64 }, six = 6; |
philpem@5 | 88 | CImgList<> balls0(7,32,32,1,3,0); |
philpem@5 | 89 | cimglist_for(balls0,l) if (l) { |
philpem@5 | 90 | balls0[l].draw_circle(16,16,14,colors.ptr(0,l)); |
philpem@5 | 91 | cimg_forXYV(balls0[l],x,y,k) if (balls0(l,x,y,k)) (balls0(l,x,y,k)*=(32-x+y)/60.0f)+=20; |
philpem@5 | 92 | balls0[l].draw_circle(16,16,14,colors.ptr(0,l),0.5f,~0U). |
philpem@5 | 93 | draw_circle(20,10,5,colors,0.2f).draw_circle(22,8,2,colors,0.4f).cut(0,255); |
philpem@5 | 94 | } |
philpem@5 | 95 | |
philpem@5 | 96 | // Create background graphics |
philpem@5 | 97 | CImgList<unsigned char> balls(balls0); |
philpem@5 | 98 | CImg<unsigned char> |
philpem@5 | 99 | mask = balls[1].get_cut(0,1).channel(0).dilate(3), |
philpem@5 | 100 | background = CImg<unsigned char>(Wi,Hi,1,3,0). |
philpem@5 | 101 | noise(255,1).blur(6,20,0).equalize(100,0,255).blur(2,4,0); |
philpem@5 | 102 | background.get_shared_channel(0)/=4; background.get_shared_channel(1)/=8; background.get_shared_channel(2)/=2; |
philpem@5 | 103 | |
philpem@5 | 104 | // Begin user-interaction loop. |
philpem@5 | 105 | CImg<unsigned char> board, previous_board, selected_board, shape, img(background); |
philpem@5 | 106 | CImgDisplay disp(img.dimx(),img.dimy(),"Jawbreaker",0); |
philpem@5 | 107 | bool redraw = true, gameover = false, title = true; |
philpem@5 | 108 | for (float opac = 0.0f; !disp.is_closed; ) { |
philpem@5 | 109 | |
philpem@5 | 110 | // Init board |
philpem@5 | 111 | if (!board) { |
philpem@5 | 112 | (++((board.assign(W,H,1,1,5).noise(5,1))%=5)).get_shared_line(0).fill(0); |
philpem@5 | 113 | opac = (float)(score = previous_score = shape_score = 0); |
philpem@5 | 114 | gameover = false; redraw = title = true; |
philpem@5 | 115 | previous_board = board; |
philpem@5 | 116 | } |
philpem@5 | 117 | |
philpem@5 | 118 | // Draw graphical board |
philpem@5 | 119 | if (redraw) { |
philpem@5 | 120 | (img=background).draw_text(2,2,"Score : %u",yellow,0,0.7f,24,score). |
philpem@5 | 121 | draw_text(Wi-90,2,"Best : %u",orange,0,0.9f,16,best_score); |
philpem@5 | 122 | if (selected_board) { |
philpem@5 | 123 | cimg_forXY(selected_board,x,y) if (selected_board(x,y)) |
philpem@5 | 124 | img.draw_image(x<<5,y<<5,balls[selected_board(x,y)],mask); |
philpem@5 | 125 | } else cimg_forXY(board,x,y) if (board(x,y)) img.draw_image(x<<5,y<<5,balls[board(x,y)],mask); |
philpem@5 | 126 | if (title) { |
philpem@5 | 127 | CImg<unsigned char> text1, text2; |
philpem@5 | 128 | text1.draw_text(0,0,"- Jawbreaker -",white,0,1,48); |
philpem@5 | 129 | text2.draw_text(0,0,"Press button to start",yellow,0,1,24); |
philpem@5 | 130 | (img/=2).draw_image((Wi-text1.dimx())/2, |
philpem@5 | 131 | (Hi-text1.dimy())/2, |
philpem@5 | 132 | text1,text1.get_dilate(7),1,255). |
philpem@5 | 133 | draw_image((Wi-text2.dimx())/2, |
philpem@5 | 134 | (Hi+text1.dimy()+10)/2, |
philpem@5 | 135 | text2,text2.get_dilate(5),0.7f,255); |
philpem@5 | 136 | for (float i=1; i<10 && !disp.is_keyESC; i+=0.25) |
philpem@5 | 137 | disp.display(img.get_crop((int)(Wi*(0.5f-i*i/200.0f)),(int)(Hi*(0.5f-i*i*i*i/20000.0f)), |
philpem@5 | 138 | (int)(Wi*(0.5f+i*i/200.0f)),(int)(Hi*(0.5f+i*i*i*i/20000.0f)))).wait(20); |
philpem@5 | 139 | } |
philpem@5 | 140 | } |
philpem@5 | 141 | if ((opac-=0.02f)>0) disp.display((+img).draw_text(disp.mouse_x-8,disp.mouse_y-54+(int)(30*opac),"+%u", |
philpem@5 | 142 | white,0,(float)std::sqrt(opac),32,shape_score)).wait(20); |
philpem@5 | 143 | else { if (redraw) { disp.display(img); redraw = false; } else disp.wait(); } |
philpem@5 | 144 | |
philpem@5 | 145 | // Handle key and window events |
philpem@5 | 146 | if (disp.is_resized) disp.resize(disp); |
philpem@5 | 147 | if (disp.is_keyBACKSPACE || disp.is_keySPACE) { |
philpem@5 | 148 | board = previous_board; score = previous_score; selected_board.assign(); redraw = true; disp.key = 0; |
philpem@5 | 149 | } |
philpem@5 | 150 | if (disp.is_keyQ) { gameover = true; disp.key = 0; } |
philpem@5 | 151 | if (disp.is_keyESC) disp.close(); |
philpem@5 | 152 | if (disp.is_keyCTRLLEFT && disp.key==cimg::keyF) disp.toggle_fullscreen().display(img); |
philpem@5 | 153 | |
philpem@5 | 154 | // Handle ball selection and removal |
philpem@5 | 155 | const int x = disp.mouse_x*board.dimx()/disp.dimx(), y = disp.mouse_y*board.dimy()/disp.dimy(); |
philpem@5 | 156 | if (disp.button&1 && x>=0 && y>=0) { |
philpem@5 | 157 | if (title) { title = false; redraw = true; } else { |
philpem@5 | 158 | disp.button=0; |
philpem@5 | 159 | if (!board(x,y)) { selected_board.assign(); redraw = true; } |
philpem@5 | 160 | else { |
philpem@5 | 161 | if (!selected_board || selected_board(x,y)!=6) { |
philpem@5 | 162 | (selected_board=board).draw_fill(x,y,0,&six,1,shape); |
philpem@5 | 163 | if ((shape_score=(unsigned int)shape.sum())<2) selected_board.assign(); |
philpem@5 | 164 | else { (shape_score-=1)*=shape_score; opac = 1.0f; redraw = true; } |
philpem@5 | 165 | } else { |
philpem@5 | 166 | selected_board.assign(); |
philpem@5 | 167 | previous_board = board; |
philpem@5 | 168 | previous_score = score; |
philpem@5 | 169 | score += shape_score; |
philpem@5 | 170 | board&=--shape; |
philpem@5 | 171 | redraw = true; |
philpem@5 | 172 | |
philpem@5 | 173 | // Handle board modification due to ball removal |
philpem@5 | 174 | for (int pmax = board.dimx(), p=0; p<pmax; ++p) { |
philpem@5 | 175 | for (int q = board.dimy()-1, qs = q; q>=0; --q) { |
philpem@5 | 176 | while (!board(p,qs)) --qs; |
philpem@5 | 177 | board(p,q) = (qs>=0?board(p,qs--):0); |
philpem@5 | 178 | } |
philpem@5 | 179 | if (!board(p,board.dimy()-1)) { |
philpem@5 | 180 | board.draw_image(p,board.get_crop(p,0,board.dimx()-1,board.dimy()-1).translate(1)); |
philpem@5 | 181 | if (p<pmax) { p--; pmax--; } |
philpem@5 | 182 | } |
philpem@5 | 183 | } |
philpem@5 | 184 | |
philpem@5 | 185 | // Test possible end of the game |
philpem@5 | 186 | gameover = true; |
philpem@5 | 187 | cimg_forXY(board,x,y) |
philpem@5 | 188 | if (board(x,y) && ((y && board(x,y)==board(x,y-1)) || (x && board(x,y)==board(x-1,y)))) gameover = false; |
philpem@5 | 189 | } |
philpem@5 | 190 | } |
philpem@5 | 191 | } |
philpem@5 | 192 | disp.button = 0; |
philpem@5 | 193 | } |
philpem@5 | 194 | |
philpem@5 | 195 | // If game is over... |
philpem@5 | 196 | if (gameover && opac<=0) { |
philpem@5 | 197 | CImg<unsigned char> text1, text2, text3, text4, text5, text6; |
philpem@5 | 198 | text1.draw_text(0,0,"Game Over !",white,0,1,48); |
philpem@5 | 199 | const unsigned int remaining_balls = (unsigned int)board.get_cut(0,1).sum(); |
philpem@5 | 200 | if (remaining_balls<8) { |
philpem@5 | 201 | const unsigned int bonus = (22-2*remaining_balls)*10; |
philpem@5 | 202 | score += bonus; |
philpem@5 | 203 | text2.draw_text(0,0,"Jawbreaker Bonus : +%u",white,0,1,24,bonus); |
philpem@5 | 204 | } |
philpem@5 | 205 | score_history.insert(CImg<unsigned int>::vector(score)); |
philpem@5 | 206 | text3.draw_text(0,0,"Final score : %u",yellow,0,1,24,score); |
philpem@5 | 207 | text4.draw_text(0,0,score>best_score?"** New record ! **":"Best score : %u", |
philpem@5 | 208 | orange,0,1,24,score>best_score?score:best_score); |
philpem@5 | 209 | text5.draw_text(0,0,"Average score : %u",red,0,1,24, |
philpem@5 | 210 | score_history?(unsigned int)score_history.mean():0U); |
philpem@5 | 211 | text6.draw_text(0,0,"Games played : %u",red,0,1,24,score_history.size); |
philpem@5 | 212 | if (score>best_score) best_score = score; |
philpem@5 | 213 | |
philpem@5 | 214 | unsigned int yt = (Hi-text1.dimy())/2-20; |
philpem@5 | 215 | (img/=2).draw_image((Wi-text1.dimx())/2,yt,text1,text1.get_dilate(7),1,255); yt+=80; |
philpem@5 | 216 | if (text2) { img.draw_image((Wi-text2.dimx())/2,yt,text2,text2.get_dilate(5),1,255); yt+=25; } |
philpem@5 | 217 | img.draw_image((Wi-text3.dimx())/2,yt,text3,text3.get_dilate(5),1,255). |
philpem@5 | 218 | draw_image((Wi-text4.dimx())/2,yt+25,text4,text4.get_dilate(5),1,255). |
philpem@5 | 219 | draw_image((Wi-text5.dimx())/2,yt+50,text5,text5.get_dilate(5),1,255). |
philpem@5 | 220 | draw_image((Wi-text6.dimx())/2,yt+75,text6,text6.get_dilate(5),1,255).display(disp); |
philpem@5 | 221 | for (disp.button = disp.key = 0; !disp.is_closed && !disp.key && !disp.button; disp.wait()) |
philpem@5 | 222 | if (disp.is_resized) disp.resize(disp); |
philpem@5 | 223 | disp.button = disp.key = 0; |
philpem@5 | 224 | board.assign(); |
philpem@5 | 225 | for (float i=10; i>0 && !disp.is_keyESC; i-=0.25) |
philpem@5 | 226 | disp.display(img.get_crop((int)(Wi*(0.5f-i*i*i*i/20000.0f)),(int)(Hi*(0.5f-i*i/200.0f)), |
philpem@5 | 227 | (int)(Wi*(0.5f+i*i*i*i/20000.0f)),(int)(Hi*(0.5f+i*i/200.0f)))).wait(20); |
philpem@5 | 228 | } |
philpem@5 | 229 | } |
philpem@5 | 230 | |
philpem@5 | 231 | // Save score history if possible, and exit. |
philpem@5 | 232 | if (score_history) { |
philpem@5 | 233 | file = std::fopen(filename_history,"w"); |
philpem@5 | 234 | if (file) { std::fclose(file); score_history.get_append('y').save_dlm(filename_history); } |
philpem@5 | 235 | } |
philpem@5 | 236 | return 0; |
philpem@5 | 237 | } |