diff options
| author | Thien-Thi Nguyen | 2007-08-27 18:49:42 +0000 |
|---|---|---|
| committer | Thien-Thi Nguyen | 2007-08-27 18:49:42 +0000 |
| commit | a79b55e56e8261ff2c9a49af5328285d0239c5e4 (patch) | |
| tree | e7e746069327f0487d511cad4fc3a6c68cf02ae1 | |
| parent | 2503f22288aa87e0ce77af4d45f7ef6e7b6eeab7 (diff) | |
| download | emacs-a79b55e56e8261ff2c9a49af5328285d0239c5e4.tar.gz emacs-a79b55e56e8261ff2c9a49af5328285d0239c5e4.zip | |
Initial revision
| -rw-r--r-- | lisp/play/bubbles.el | 1438 |
1 files changed, 1438 insertions, 0 deletions
diff --git a/lisp/play/bubbles.el b/lisp/play/bubbles.el new file mode 100644 index 00000000000..a3faecb54ab --- /dev/null +++ b/lisp/play/bubbles.el | |||
| @@ -0,0 +1,1438 @@ | |||
| 1 | ;;; bubbles.el --- Puzzle game for Emacs. | ||
| 2 | |||
| 3 | ;; Copyright (C) 2007 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; This file is NOT part of GNU Emacs. | ||
| 6 | |||
| 7 | ;; Author: Ulf Jasper <ulf.jasper@web.de> | ||
| 8 | ;; Filename: bubbles.el | ||
| 9 | ;; URL: http://ulf.epplejasper.de/ | ||
| 10 | ;; Created: 5. Feb. 2007 | ||
| 11 | ;; Keywords: Games | ||
| 12 | ;; Time-stamp: "27. August 2007, 19:51:08 (ulf)" | ||
| 13 | ;; CVS-Version: $Id: bubbles.el,v 1.16 2007-08-27 17:51:29 ulf Exp $ | ||
| 14 | |||
| 15 | ;; ====================================================================== | ||
| 16 | |||
| 17 | ;; This program is free software; you can redistribute it and/or modify | ||
| 18 | ;; it under the terms of the GNU General Public License as published by | ||
| 19 | ;; the Free Software Foundation; either version 2 of the License, or (at | ||
| 20 | ;; your option) any later version. | ||
| 21 | |||
| 22 | ;; This program is distributed in the hope that it will be useful, but | ||
| 23 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 24 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 25 | ;; General Public License for more details. | ||
| 26 | |||
| 27 | ;; You should have received a copy of the GNU General Public License | ||
| 28 | ;; along with this program; if not, write to the Free Software Foundation, | ||
| 29 | ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 30 | |||
| 31 | (defconst bubbles-version "0.4" "Version number of bubbles.el.") | ||
| 32 | |||
| 33 | ;; ====================================================================== | ||
| 34 | |||
| 35 | ;;; Commentary: | ||
| 36 | |||
| 37 | ;; Bubbles is a puzzle game. Its goal is to remove as many bubbles as | ||
| 38 | ;; possible in as few moves as possible. | ||
| 39 | |||
| 40 | ;; Bubbles is an implementation of the "Same Game", similar to "Same | ||
| 41 | ;; GNOME" and many others, see http://en.wikipedia.org/wiki/SameGame. | ||
| 42 | |||
| 43 | ;; Installation | ||
| 44 | ;; ------------ | ||
| 45 | |||
| 46 | ;; Add the following lines to your Emacs startup file (`~/.emacs'). | ||
| 47 | ;; (add-to-list 'load-path "/path/to/bubbles/") | ||
| 48 | ;; (autoload 'bubbles "bubbles" "Play Bubbles" t) | ||
| 49 | |||
| 50 | ;; ====================================================================== | ||
| 51 | |||
| 52 | ;;; History: | ||
| 53 | |||
| 54 | ;; 0.4 (2007-08-27) | ||
| 55 | ;; - Allow for undoing last move. | ||
| 56 | ;; - Bonus for removing all bubbles. | ||
| 57 | ;; - Speed improvements. | ||
| 58 | ;; - Animation enhancements. | ||
| 59 | ;; - Added `bubbles-mode-hook'. | ||
| 60 | ;; - Fixes: Don't move point. | ||
| 61 | ;; - New URL. | ||
| 62 | |||
| 63 | ;; 0.3 (2007-03-11) | ||
| 64 | ;; - Renamed shift modes and thus names of score files. All | ||
| 65 | ;; highscores are lost, unless you rename the score files from | ||
| 66 | ;; bubbles-shift-... to bubbles-...! | ||
| 67 | ;; - Bugfixes: Check for successful image creation. | ||
| 68 | ;; Disable menus and counter when game is over. | ||
| 69 | ;; Tested with GNU Emacs 22.0.93 | ||
| 70 | |||
| 71 | ;; 0.2 (2007-02-24) | ||
| 72 | ;; - Introduced game themes. | ||
| 73 | ;; - Introduced graphics themes (changeable while playing). | ||
| 74 | ;; - Added menu. | ||
| 75 | ;; - Customization: grid size, colors, chars, shift mode. | ||
| 76 | ;; - More keybindings. | ||
| 77 | ;; - Changed shift direction from to-right to to-left. | ||
| 78 | ;; - Bugfixes: Don't remove single-bubble regions; | ||
| 79 | ;; Animation glitches fixed. | ||
| 80 | ;; Tested with GNU Emacs 22.0.93 and 21.4.1. | ||
| 81 | |||
| 82 | ;; 0.1 (2007-02-11) | ||
| 83 | ;; Initial release. Tested with GNU Emacs 22.0.93 and 21.4.1. | ||
| 84 | |||
| 85 | ;; ====================================================================== | ||
| 86 | |||
| 87 | ;;; Code: | ||
| 88 | |||
| 89 | (require 'gamegrid) | ||
| 90 | (require 'cl) | ||
| 91 | |||
| 92 | ;; User options | ||
| 93 | |||
| 94 | ;; Careful with that axe, Eugene! Order does matter in the custom | ||
| 95 | ;; section below. | ||
| 96 | |||
| 97 | (defcustom bubbles-game-theme | ||
| 98 | 'easy | ||
| 99 | "Overall game theme. | ||
| 100 | The overall game theme specifies a grid size, a set of colors, | ||
| 101 | and a shift mode." | ||
| 102 | :type '(radio (const :tag "Easy" easy) | ||
| 103 | (const :tag "Medium" medium) | ||
| 104 | (const :tag "Difficult" difficult) | ||
| 105 | (const :tag "Hard" hard) | ||
| 106 | (const :tag "User defined" user-defined)) | ||
| 107 | :group 'bubbles) | ||
| 108 | |||
| 109 | (defun bubbles-set-game-easy () | ||
| 110 | "Set game theme to 'easy'." | ||
| 111 | (interactive) | ||
| 112 | (setq bubbles-game-theme 'easy) | ||
| 113 | (bubbles)) | ||
| 114 | |||
| 115 | (defun bubbles-set-game-medium () | ||
| 116 | "Set game theme to 'medium'." | ||
| 117 | (interactive) | ||
| 118 | (setq bubbles-game-theme 'medium) | ||
| 119 | (bubbles)) | ||
| 120 | |||
| 121 | (defun bubbles-set-game-difficult () | ||
| 122 | "Set game theme to 'difficult'." | ||
| 123 | (interactive) | ||
| 124 | (setq bubbles-game-theme 'difficult) | ||
| 125 | (bubbles)) | ||
| 126 | |||
| 127 | (defun bubbles-set-game-hard () | ||
| 128 | "Set game theme to 'hard'." | ||
| 129 | (interactive) | ||
| 130 | (setq bubbles-game-theme 'hard) | ||
| 131 | (bubbles)) | ||
| 132 | |||
| 133 | (defun bubbles-set-game-userdefined () | ||
| 134 | "Set game theme to 'user-defined'." | ||
| 135 | (interactive) | ||
| 136 | (setq bubbles-game-theme 'user-defined) | ||
| 137 | (bubbles)) | ||
| 138 | |||
| 139 | (defgroup bubbles nil | ||
| 140 | "Bubbles, a puzzle game." | ||
| 141 | :group 'games) | ||
| 142 | |||
| 143 | (defcustom bubbles-graphics-theme | ||
| 144 | 'circles | ||
| 145 | "Graphics theme. | ||
| 146 | It is safe to choose a graphical theme. If Emacs cannot display | ||
| 147 | images the `ascii' theme will be used." | ||
| 148 | :type '(radio (const :tag "Circles" circles) | ||
| 149 | (const :tag "Squares" squares) | ||
| 150 | (const :tag "Diamonds" diamonds) | ||
| 151 | (const :tag "Balls" balls) | ||
| 152 | (const :tag "Emacs" emacs) | ||
| 153 | (const :tag "ASCII (no images)" ascii)) | ||
| 154 | :group 'bubbles) | ||
| 155 | |||
| 156 | (defconst bubbles--grid-small '(10 . 10) | ||
| 157 | "Predefined small bubbles grid.") | ||
| 158 | |||
| 159 | (defconst bubbles--grid-medium '(15 . 10) | ||
| 160 | "Predefined medium bubbles grid.") | ||
| 161 | |||
| 162 | (defconst bubbles--grid-large '(20 . 15) | ||
| 163 | "Predefined large bubbles grid.") | ||
| 164 | |||
| 165 | (defconst bubbles--grid-huge '(30 . 20) | ||
| 166 | "Predefined huge bubbles grid.") | ||
| 167 | |||
| 168 | (defcustom bubbles-grid-size | ||
| 169 | bubbles--grid-medium | ||
| 170 | "Size of bubbles grid." | ||
| 171 | :type `(radio (const :tag "Small" ,bubbles--grid-small) | ||
| 172 | (const :tag "Medium" ,bubbles--grid-medium) | ||
| 173 | (const :tag "Large" ,bubbles--grid-large) | ||
| 174 | (const :tag "Huge" ,bubbles--grid-huge) | ||
| 175 | (cons :tag "User defined" | ||
| 176 | (integer :tag "Width") | ||
| 177 | (integer :tag "Height"))) | ||
| 178 | :group 'bubbles) | ||
| 179 | |||
| 180 | (defconst bubbles--colors-2 '("orange" "violet") | ||
| 181 | "Predefined bubbles color list with two colors.") | ||
| 182 | |||
| 183 | (defconst bubbles--colors-3 '("lightblue" "palegreen" "pink") | ||
| 184 | "Predefined bubbles color list with three colors.") | ||
| 185 | |||
| 186 | (defconst bubbles--colors-4 '("firebrick" "sea green" "steel blue" "chocolate") | ||
| 187 | "Predefined bubbles color list with four colors.") | ||
| 188 | |||
| 189 | (defconst bubbles--colors-5 '("firebrick" "sea green" "steel blue" | ||
| 190 | "sandy brown" "bisque3") | ||
| 191 | "Predefined bubbles color list with five colors.") | ||
| 192 | |||
| 193 | (defcustom bubbles-colors | ||
| 194 | bubbles--colors-3 | ||
| 195 | "List of bubble colors. | ||
| 196 | The length of this list determines how many different bubble | ||
| 197 | types are present." | ||
| 198 | :type `(radio (const :tag "Red, darkgreen" ,bubbles--colors-2) | ||
| 199 | (const :tag "Red, darkgreen, blue" ,bubbles--colors-3) | ||
| 200 | (const :tag "Red, darkgreen, blue, orange" ,bubbles--colors-4) | ||
| 201 | (const :tag "Red, darkgreen, blue, orange, violet" | ||
| 202 | ,bubbles--colors-5) | ||
| 203 | (repeat :tag "User defined" color)) | ||
| 204 | :group 'bubbles) | ||
| 205 | |||
| 206 | (defcustom bubbles-chars | ||
| 207 | '(?+ ?O ?# ?X ?. ?* ?& ?§) | ||
| 208 | "Characters used for bubbles. | ||
| 209 | Note that the actual number of different bubbles is determined by | ||
| 210 | the number of colors, see `bubbles-colors'." | ||
| 211 | :type '(repeat character) | ||
| 212 | :group 'bubbles) | ||
| 213 | |||
| 214 | (defcustom bubbles-shift-mode | ||
| 215 | 'default | ||
| 216 | "Shift mode. | ||
| 217 | Available modes are `shift-default' and`shift-always'." | ||
| 218 | :type '(radio (const :tag "Default" default) | ||
| 219 | (const :tag "Shifter" always) | ||
| 220 | ;;(const :tag "Mega Shifter" 'mega) | ||
| 221 | ) | ||
| 222 | :group 'bubbles) | ||
| 223 | |||
| 224 | (defcustom bubbles-mode-hook nil | ||
| 225 | "Hook run by Bubbles mode." | ||
| 226 | :group 'bubbles | ||
| 227 | :type 'hook) | ||
| 228 | |||
| 229 | (defun bubbles-customize () | ||
| 230 | "Open customization buffer for bubbles." | ||
| 231 | (interactive) | ||
| 232 | (customize-group 'bubbles)) | ||
| 233 | |||
| 234 | ;; ====================================================================== | ||
| 235 | ;; internal variables | ||
| 236 | |||
| 237 | (defvar bubbles--score 0 | ||
| 238 | "Current Bubbles score.") | ||
| 239 | |||
| 240 | (defvar bubbles--neighbourhood-score 0 | ||
| 241 | "Score of active bubbles neighbourhood.") | ||
| 242 | |||
| 243 | (defvar bubbles--faces nil | ||
| 244 | "List of currently used faces.") | ||
| 245 | |||
| 246 | (defvar bubbles--playing nil | ||
| 247 | "Play status indicator.") | ||
| 248 | |||
| 249 | (defvar bubbles--empty-image nil | ||
| 250 | "Image used for removed bubbles (empty grid cells).") | ||
| 251 | |||
| 252 | (defvar bubbles--images nil | ||
| 253 | "List of images for bubbles.") | ||
| 254 | |||
| 255 | (defvar bubbles--images-ok nil | ||
| 256 | "Indicate whether images have been created successfully.") | ||
| 257 | |||
| 258 | (defvar bubbles--col-offset 0 | ||
| 259 | "Horizontal offset for centering the bubbles grid.") | ||
| 260 | |||
| 261 | (defvar bubbles--row-offset 0 | ||
| 262 | "Vertical offset for centering the bubbles grid.") | ||
| 263 | |||
| 264 | (defvar bubbles--save-data nil | ||
| 265 | "List containing bubbles save data (SCORE BUFFERCONTENTS).") | ||
| 266 | |||
| 267 | (defconst bubbles--image-template-circle | ||
| 268 | "/* XPM */ | ||
| 269 | static char * dot_xpm[] = { | ||
| 270 | \"20 20 2 1\", | ||
| 271 | \" c None\", | ||
| 272 | \". c #FFFFFF\", | ||
| 273 | \" ...... \", | ||
| 274 | \" .......... \", | ||
| 275 | \" .............. \", | ||
| 276 | \" ................ \", | ||
| 277 | \" ................ \", | ||
| 278 | \" .................. \", | ||
| 279 | \" .................. \", | ||
| 280 | \"....................\", | ||
| 281 | \"....................\", | ||
| 282 | \"....................\", | ||
| 283 | \"....................\", | ||
| 284 | \"....................\", | ||
| 285 | \"....................\", | ||
| 286 | \" .................. \", | ||
| 287 | \" .................. \", | ||
| 288 | \" ................ \", | ||
| 289 | \" ................ \", | ||
| 290 | \" .............. \", | ||
| 291 | \" .......... \", | ||
| 292 | \" ...... \"};") | ||
| 293 | |||
| 294 | (defconst bubbles--image-template-square | ||
| 295 | "/* XPM */ | ||
| 296 | static char * dot_xpm[] = { | ||
| 297 | \"20 20 2 1\", | ||
| 298 | \"0 c None\", | ||
| 299 | \"1 c #FFFFFF\", | ||
| 300 | \"00000000000000000000\", | ||
| 301 | \"01111111111111111110\", | ||
| 302 | \"01111111111111111110\", | ||
| 303 | \"01111111111111111110\", | ||
| 304 | \"01111111111111111110\", | ||
| 305 | \"01111111111111111110\", | ||
| 306 | \"01111111111111111110\", | ||
| 307 | \"01111111111111111110\", | ||
| 308 | \"01111111111111111110\", | ||
| 309 | \"01111111111111111110\", | ||
| 310 | \"01111111111111111110\", | ||
| 311 | \"01111111111111111110\", | ||
| 312 | \"01111111111111111110\", | ||
| 313 | \"01111111111111111110\", | ||
| 314 | \"01111111111111111110\", | ||
| 315 | \"01111111111111111110\", | ||
| 316 | \"01111111111111111110\", | ||
| 317 | \"01111111111111111110\", | ||
| 318 | \"01111111111111111110\", | ||
| 319 | \"00000000000000000000\"};") | ||
| 320 | |||
| 321 | (defconst bubbles--image-template-diamond | ||
| 322 | "/* XPM */ | ||
| 323 | static char * dot_xpm[] = { | ||
| 324 | \"20 20 2 1\", | ||
| 325 | \"0 c None\", | ||
| 326 | \"1 c #FFFFFF\", | ||
| 327 | \"00000000011000000000\", | ||
| 328 | \"00000000111100000000\", | ||
| 329 | \"00000001111110000000\", | ||
| 330 | \"00000011111111000000\", | ||
| 331 | \"00000111111111100000\", | ||
| 332 | \"00001111111111110000\", | ||
| 333 | \"00011111111111111000\", | ||
| 334 | \"00111111111111111100\", | ||
| 335 | \"01111111111111111110\", | ||
| 336 | \"11111111111111111111\", | ||
| 337 | \"01111111111111111110\", | ||
| 338 | \"00111111111111111100\", | ||
| 339 | \"00011111111111111000\", | ||
| 340 | \"00001111111111110000\", | ||
| 341 | \"00000111111111100000\", | ||
| 342 | \"00000011111111000000\", | ||
| 343 | \"00000001111110000000\", | ||
| 344 | \"00000000111100000000\", | ||
| 345 | \"00000000011000000000\", | ||
| 346 | \"00000000000000000000\"};") | ||
| 347 | |||
| 348 | (defconst bubbles--image-template-emacs | ||
| 349 | "/* XPM */ | ||
| 350 | static char * emacs_24_xpm[] = { | ||
| 351 | \"24 24 129 2\", | ||
| 352 | \" c None\", | ||
| 353 | \". c #837DA4\", | ||
| 354 | \"+ c #807AA0\", | ||
| 355 | \"@ c #9894B2\", | ||
| 356 | \"# c #CCCAD9\", | ||
| 357 | \"$ c #C2C0D2\", | ||
| 358 | \"% c #B6B3C9\", | ||
| 359 | \"& c #A19DB9\", | ||
| 360 | \"* c #8681A5\", | ||
| 361 | \"= c #7D779B\", | ||
| 362 | \"- c #B6B3C7\", | ||
| 363 | \"; c #ABA7BE\", | ||
| 364 | \"> c #9792AF\", | ||
| 365 | \", c #AAA6BD\", | ||
| 366 | \"' c #CBC9D7\", | ||
| 367 | \") c #AAA7BE\", | ||
| 368 | \"! c #908BAA\", | ||
| 369 | \"~ c #797397\", | ||
| 370 | \"{ c #948FAC\", | ||
| 371 | \"] c #9A95B1\", | ||
| 372 | \"^ c #EBEAEF\", | ||
| 373 | \"/ c #F1F1F5\", | ||
| 374 | \"( c #BCB9CB\", | ||
| 375 | \"_ c #A9A5BD\", | ||
| 376 | \": c #757093\", | ||
| 377 | \"< c #918DA9\", | ||
| 378 | \"[ c #DDDBE4\", | ||
| 379 | \"} c #FFFFFF\", | ||
| 380 | \"| c #EAE9EF\", | ||
| 381 | \"1 c #A7A4BA\", | ||
| 382 | \"2 c #716C8F\", | ||
| 383 | \"3 c #8D89A5\", | ||
| 384 | \"4 c #9C98B1\", | ||
| 385 | \"5 c #DBDAE3\", | ||
| 386 | \"6 c #A4A1B7\", | ||
| 387 | \"7 c #6E698A\", | ||
| 388 | \"8 c #8B87A1\", | ||
| 389 | \"9 c #928EA7\", | ||
| 390 | \"0 c #C5C3D1\", | ||
| 391 | \"a c #F8F8F9\", | ||
| 392 | \"b c #CCCAD6\", | ||
| 393 | \"c c #A29FB4\", | ||
| 394 | \"d c #6A6585\", | ||
| 395 | \"e c #88849D\", | ||
| 396 | \"f c #B5B2C2\", | ||
| 397 | \"g c #F0F0F3\", | ||
| 398 | \"h c #E1E0E6\", | ||
| 399 | \"i c #A5A2B5\", | ||
| 400 | \"j c #A09DB1\", | ||
| 401 | \"k c #676281\", | ||
| 402 | \"l c #85819A\", | ||
| 403 | \"m c #9591A7\", | ||
| 404 | \"n c #E1E0E5\", | ||
| 405 | \"o c #F0EFF2\", | ||
| 406 | \"p c #B3B0C0\", | ||
| 407 | \"q c #9D9AAE\", | ||
| 408 | \"r c #635F7C\", | ||
| 409 | \"s c #827F96\", | ||
| 410 | \"t c #9997AA\", | ||
| 411 | \"u c #F7F7F9\", | ||
| 412 | \"v c #C8C7D1\", | ||
| 413 | \"w c #89869D\", | ||
| 414 | \"x c #9B99AB\", | ||
| 415 | \"y c #5F5B78\", | ||
| 416 | \"z c #7F7C93\", | ||
| 417 | \"A c #CFCDD6\", | ||
| 418 | \"B c #B7B5C2\", | ||
| 419 | \"C c #9996A9\", | ||
| 420 | \"D c #5C5873\", | ||
| 421 | \"E c #7A778D\", | ||
| 422 | \"F c #F5F5F6\", | ||
| 423 | \"G c #8E8C9E\", | ||
| 424 | \"H c #7D798F\", | ||
| 425 | \"I c #58546F\", | ||
| 426 | \"J c #6C6981\", | ||
| 427 | \"K c #D5D4DB\", | ||
| 428 | \"L c #F5F4F6\", | ||
| 429 | \"M c #9794A5\", | ||
| 430 | \"N c #625F78\", | ||
| 431 | \"O c #79768C\", | ||
| 432 | \"P c #55516A\", | ||
| 433 | \"Q c #605C73\", | ||
| 434 | \"R c #CAC9D1\", | ||
| 435 | \"S c #EAE9EC\", | ||
| 436 | \"T c #B4B3BE\", | ||
| 437 | \"U c #777488\", | ||
| 438 | \"V c #514E66\", | ||
| 439 | \"W c #DEDEE2\", | ||
| 440 | \"X c #F4F4F5\", | ||
| 441 | \"Y c #9D9BA9\", | ||
| 442 | \"Z c #747185\", | ||
| 443 | \"` c #4E4B62\", | ||
| 444 | \" . c #DEDDE1\", | ||
| 445 | \".. c #A6A5B0\", | ||
| 446 | \"+. c #716F81\", | ||
| 447 | \"@. c #4A475D\", | ||
| 448 | \"#. c #A4A3AE\", | ||
| 449 | \"$. c #F4F3F5\", | ||
| 450 | \"%. c #777586\", | ||
| 451 | \"&. c #6E6C7D\", | ||
| 452 | \"*. c #464358\", | ||
| 453 | \"=. c #514E62\", | ||
| 454 | \"-. c #B9B8C0\", | ||
| 455 | \";. c #D1D0D5\", | ||
| 456 | \">. c #747282\", | ||
| 457 | \",. c #6B6979\", | ||
| 458 | \"'. c #434054\", | ||
| 459 | \"). c #5A5769\", | ||
| 460 | \"!. c #D0CFD4\", | ||
| 461 | \"~. c #5B5869\", | ||
| 462 | \"{. c #696676\", | ||
| 463 | \"]. c #403D50\", | ||
| 464 | \"^. c #DBDADE\", | ||
| 465 | \"/. c #F3F3F4\", | ||
| 466 | \"(. c #646271\", | ||
| 467 | \"_. c #666473\", | ||
| 468 | \":. c #3D3A4C\", | ||
| 469 | \"<. c #555362\", | ||
| 470 | \"[. c #9E9DA6\", | ||
| 471 | \"}. c #9E9CA5\", | ||
| 472 | \"|. c #646170\", | ||
| 473 | \"1. c #393647\", | ||
| 474 | \"2. c #514E5D\", | ||
| 475 | \"3. c #83818C\", | ||
| 476 | \"4. c #A8A7AE\", | ||
| 477 | \"5. c #E6E6E8\", | ||
| 478 | \"6. c #DAD9DC\", | ||
| 479 | \"7. c #353343\", | ||
| 480 | \"8. c #32303E\", | ||
| 481 | \" . . . . . . . . . . . . . . . . . . \", | ||
| 482 | \" + @ # $ % % % % % % % % % % % % % % & * + + \", | ||
| 483 | \" = - ; > > > > > > > > , ' ) > > > > > > ! = \", | ||
| 484 | \"~ ~ { { { { { { { { { { { ] ^ / ( { { { { _ ~ ~ \", | ||
| 485 | \": : < < < < < < < < < < < < [ } } | < < < 1 : : \", | ||
| 486 | \"2 2 3 3 3 3 3 3 3 3 3 3 4 5 } } } 5 3 3 3 6 2 2 \", | ||
| 487 | \"7 7 8 8 8 8 8 8 8 8 9 0 a } } } b 8 8 8 8 c 7 7 \", | ||
| 488 | \"d d e e e e e e e f g } } } h i e e e e e j d d \", | ||
| 489 | \"k k l l l l l m n } } } o p l l l l l l l q k k \", | ||
| 490 | \"r r s s s s t u } } } v w s s s s s s s s x r r \", | ||
| 491 | \"y y z z z z A } } } B z z z z z z z z z z C y y \", | ||
| 492 | \"D D D D D D E F } } G D D D D D D D D D D H D D \", | ||
| 493 | \"I I I I I I I J K } L M N I I I I I I I I O I I \", | ||
| 494 | \"P P P P P P Q R } } } S T P P P P P P P P U P P \", | ||
| 495 | \"V V V V V V W } } X Y V V V V V V V V V V Z V V \", | ||
| 496 | \"` ` ` ` ` ` .} } ..` ` ` ` ` ` ` ` ` ` ` +.` ` \", | ||
| 497 | \"@.@.@.@.@.@.@.#.$.$.%.@.@.@.@.@.@.@.@.@.@.&.@.@.\", | ||
| 498 | \"*.*.*.*.*.*.*.*.=.-.} ;.>.*.*.*.*.*.*.*.*.,.*.*.\", | ||
| 499 | \"'.'.'.'.'.'.'.'.'.'.).!.} !.~.'.'.'.'.'.'.{.'.'.\", | ||
| 500 | \"].].].].].].].].].].].].^.} /.(.].].].].]._.].].\", | ||
| 501 | \":.:.:.:.:.:.:.:.:.:.<.[./.} } }.:.:.:.:.:.|.:.:.\", | ||
| 502 | \" 1.1.1.1.1.1.1.1.2.3.4.5.6.3.1.1.1.1.1.1.1.1. \", | ||
| 503 | \" 7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7. \", | ||
| 504 | \" 8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8. \"};") | ||
| 505 | |||
| 506 | (defconst bubbles--image-template-ball | ||
| 507 | "/* XPM */ | ||
| 508 | static char * dot3d_xpm[] = { | ||
| 509 | \"20 20 190 2\", | ||
| 510 | \" c None\", | ||
| 511 | \". c #F9F6F6\", | ||
| 512 | \"+ c #D6D0D0\", | ||
| 513 | \"@ c #BFBBBB\", | ||
| 514 | \"# c #AAA4A4\", | ||
| 515 | \"$ c #ABAAAB\", | ||
| 516 | \"% c #A8A8A8\", | ||
| 517 | \"& c #A29D9D\", | ||
| 518 | \"* c #B5B2B2\", | ||
| 519 | \"= c #CDC9C9\", | ||
| 520 | \"- c #D7D0D0\", | ||
| 521 | \"; c #B3AFAF\", | ||
| 522 | \"> c #B5B5B5\", | ||
| 523 | \", c #B7B7B7\", | ||
| 524 | \"' c #B8B8B8\", | ||
| 525 | \") c #B6B6B6\", | ||
| 526 | \"! c #B3B3B3\", | ||
| 527 | \"~ c #AFAFAF\", | ||
| 528 | \"{ c #A9A9A9\", | ||
| 529 | \"] c #A2A2A2\", | ||
| 530 | \"^ c #9C9A9A\", | ||
| 531 | \"/ c #C9C5C5\", | ||
| 532 | \"( c #FDFBFB\", | ||
| 533 | \"_ c #C3BCBC\", | ||
| 534 | \": c #BBBBBB\", | ||
| 535 | \"< c #C0C0C0\", | ||
| 536 | \"[ c #C3C2C2\", | ||
| 537 | \"} c #C3C3C3\", | ||
| 538 | \"| c #C2C2C2\", | ||
| 539 | \"1 c #BEBEBE\", | ||
| 540 | \"2 c #B9B9B9\", | ||
| 541 | \"3 c #B2B2B2\", | ||
| 542 | \"4 c #ABAAAA\", | ||
| 543 | \"5 c #999999\", | ||
| 544 | \"6 c #ACA7A7\", | ||
| 545 | \"7 c #C2BBBB\", | ||
| 546 | \"8 c #C5C5C5\", | ||
| 547 | \"9 c #CACBCB\", | ||
| 548 | \"0 c #CECECE\", | ||
| 549 | \"a c #CFCFCF\", | ||
| 550 | \"b c #CDCDCD\", | ||
| 551 | \"c c #C8C9C9\", | ||
| 552 | \"d c #9F9F9F\", | ||
| 553 | \"e c #959595\", | ||
| 554 | \"f c #A9A5A5\", | ||
| 555 | \"g c #D5CFCE\", | ||
| 556 | \"h c #BDBDBD\", | ||
| 557 | \"i c #C6C6C6\", | ||
| 558 | \"j c #D5D5D5\", | ||
| 559 | \"k c #D9D9D9\", | ||
| 560 | \"l c #DADADA\", | ||
| 561 | \"m c #D8D8D8\", | ||
| 562 | \"n c #D2D2D2\", | ||
| 563 | \"o c #CBCBCB\", | ||
| 564 | \"p c #A4A4A5\", | ||
| 565 | \"q c #9A9A9A\", | ||
| 566 | \"r c #8F8F8F\", | ||
| 567 | \"s c #C3BFBF\", | ||
| 568 | \"t c #AFACAB\", | ||
| 569 | \"u c #CCCCCC\", | ||
| 570 | \"v c #D6D6D6\", | ||
| 571 | \"w c #DEDEDE\", | ||
| 572 | \"x c #E4E4E4\", | ||
| 573 | \"y c #E5E5E5\", | ||
| 574 | \"z c #E2E2E2\", | ||
| 575 | \"A c #DBDBDB\", | ||
| 576 | \"B c #C9C8C8\", | ||
| 577 | \"C c #A8A9A8\", | ||
| 578 | \"D c #9D9E9D\", | ||
| 579 | \"E c #929292\", | ||
| 580 | \"F c #8A8888\", | ||
| 581 | \"G c #D3CECE\", | ||
| 582 | \"H c #B0B0B0\", | ||
| 583 | \"I c #D1D1D1\", | ||
| 584 | \"J c #DCDCDC\", | ||
| 585 | \"K c #E6E6E6\", | ||
| 586 | \"L c #EEEEEE\", | ||
| 587 | \"M c #F1F1F0\", | ||
| 588 | \"N c #EBEBEB\", | ||
| 589 | \"O c #D7D7D8\", | ||
| 590 | \"P c #ABABAB\", | ||
| 591 | \"Q c #A0A0A0\", | ||
| 592 | \"R c #949494\", | ||
| 593 | \"S c #898989\", | ||
| 594 | \"T c #C0BDBD\", | ||
| 595 | \"U c #B9B6B6\", | ||
| 596 | \"V c #B1B1B1\", | ||
| 597 | \"W c #BCBCBC\", | ||
| 598 | \"X c #C8C8C8\", | ||
| 599 | \"Y c #D3D3D3\", | ||
| 600 | \"Z c #DFDFDE\", | ||
| 601 | \"` c #EAEAEA\", | ||
| 602 | \" . c #F5F5F5\", | ||
| 603 | \".. c #FAFAFA\", | ||
| 604 | \"+. c #F1F1F1\", | ||
| 605 | \"@. c #CECFCF\", | ||
| 606 | \"#. c #ACACAC\", | ||
| 607 | \"$. c #A1A1A1\", | ||
| 608 | \"%. c #8A8A8A\", | ||
| 609 | \"&. c #9B9999\", | ||
| 610 | \"*. c #C7C7C7\", | ||
| 611 | \"=. c #DDDDDD\", | ||
| 612 | \"-. c #E8E8E8\", | ||
| 613 | \";. c #F2F2F2\", | ||
| 614 | \">. c #898A89\", | ||
| 615 | \",. c #7A7878\", | ||
| 616 | \"'. c #AEAEAE\", | ||
| 617 | \"). c #C4C4C4\", | ||
| 618 | \"!. c #CBCBCA\", | ||
| 619 | \"~. c #AAAAAA\", | ||
| 620 | \"{. c #939393\", | ||
| 621 | \"]. c #888888\", | ||
| 622 | \"^. c #7C7C7C\", | ||
| 623 | \"/. c #AAAAAB\", | ||
| 624 | \"(. c #BFBFBF\", | ||
| 625 | \"_. c #C9C9C9\", | ||
| 626 | \":. c #DFDEDF\", | ||
| 627 | \"<. c #A6A6A6\", | ||
| 628 | \"[. c #9B9B9B\", | ||
| 629 | \"}. c #909191\", | ||
| 630 | \"|. c #858586\", | ||
| 631 | \"1. c #797979\", | ||
| 632 | \"2. c #989494\", | ||
| 633 | \"3. c #A5A6A5\", | ||
| 634 | \"4. c #B9B9B8\", | ||
| 635 | \"5. c #C1C1C1\", | ||
| 636 | \"6. c #CFCFCE\", | ||
| 637 | \"7. c #979797\", | ||
| 638 | \"8. c #8D8D8D\", | ||
| 639 | \"9. c #828282\", | ||
| 640 | \"0. c #747171\", | ||
| 641 | \"a. c #ADAAAA\", | ||
| 642 | \"b. c #A9A8A9\", | ||
| 643 | \"c. c #B8B9B9\", | ||
| 644 | \"d. c #A5A5A5\", | ||
| 645 | \"e. c #9C9C9C\", | ||
| 646 | \"f. c #7E7E7D\", | ||
| 647 | \"g. c #929191\", | ||
| 648 | \"h. c #C9C4C4\", | ||
| 649 | \"i. c #989898\", | ||
| 650 | \"j. c #ADADAD\", | ||
| 651 | \"k. c #9D9D9D\", | ||
| 652 | \"l. c #8C8C8C\", | ||
| 653 | \"m. c #787878\", | ||
| 654 | \"n. c #B8B6B6\", | ||
| 655 | \"o. c #939191\", | ||
| 656 | \"p. c #A5A5A6\", | ||
| 657 | \"q. c #ABABAA\", | ||
| 658 | \"r. c #A8A8A9\", | ||
| 659 | \"s. c #A3A3A3\", | ||
| 660 | \"t. c #858585\", | ||
| 661 | \"u. c #757474\", | ||
| 662 | \"v. c #C5C1C1\", | ||
| 663 | \"w. c #969696\", | ||
| 664 | \"x. c #9B9B9C\", | ||
| 665 | \"y. c #A4A4A4\", | ||
| 666 | \"z. c #9E9E9E\", | ||
| 667 | \"A. c #939394\", | ||
| 668 | \"B. c #7D7D7D\", | ||
| 669 | \"C. c #747474\", | ||
| 670 | \"D. c #B7B5B5\", | ||
| 671 | \"E. c #A5A1A1\", | ||
| 672 | \"F. c #919191\", | ||
| 673 | \"G. c #9A9999\", | ||
| 674 | \"H. c #838383\", | ||
| 675 | \"I. c #757575\", | ||
| 676 | \"J. c #939090\", | ||
| 677 | \"K. c #A29E9E\", | ||
| 678 | \"L. c #868686\", | ||
| 679 | \"M. c #8D8D8C\", | ||
| 680 | \"N. c #8E8E8E\", | ||
| 681 | \"O. c #8D8D8E\", | ||
| 682 | \"P. c #8B8C8C\", | ||
| 683 | \"Q. c #848485\", | ||
| 684 | \"R. c #7F7F80\", | ||
| 685 | \"S. c #7A7A7A\", | ||
| 686 | \"T. c #737373\", | ||
| 687 | \"U. c #929090\", | ||
| 688 | \"V. c #828080\", | ||
| 689 | \"W. c #818181\", | ||
| 690 | \"X. c #808080\", | ||
| 691 | \"Y. c #7E7E7E\", | ||
| 692 | \"Z. c #737272\", | ||
| 693 | \"`. c #B7B4B4\", | ||
| 694 | \" + c #BCBABA\", | ||
| 695 | \".+ c #959494\", | ||
| 696 | \"++ c #747172\", | ||
| 697 | \"@+ c #767676\", | ||
| 698 | \"#+ c #6F6D6D\", | ||
| 699 | \"$+ c #8F8E8E\", | ||
| 700 | \" . + @ # $ % & * = . \", | ||
| 701 | \" - ; > , ' ) ! ~ { ] ^ / \", | ||
| 702 | \" ( _ > : < [ } | 1 2 3 4 ] 5 6 ( \", | ||
| 703 | \" 7 ) 1 8 9 0 a b c | : 3 { d e f \", | ||
| 704 | \" g ! h i 0 j k l m n o | 2 ~ p q r s \", | ||
| 705 | \". t ' | u v w x y z A n B 1 ! C D E F . \", | ||
| 706 | \"G H : i I J K L M N z O b | ) P Q R S T \", | ||
| 707 | \"U V W X Y Z ` ...+.y l @.} ' #.$.e %.&.\", | ||
| 708 | \"& H W *.n =.-.;. .L x k 0 [ , #.Q e >.,.\", | ||
| 709 | \"] '.2 ).a k z -.` K w j !.< > ~.d {.].^.\", | ||
| 710 | \"d /.> (._.I k =.:.J v 0 8 : V <.[.}.|.1.\", | ||
| 711 | \"2.3.~ 4.5._.6.n Y I u i 1 > P $.7.8.9.0.\", | ||
| 712 | \"a.d b.V c.(.).*.X i | h ) '.d.e.E ].f.g.\", | ||
| 713 | \"h.i.$.C ~ > 2 W W : ' ! j.d.k.e l.9.m.n.\", | ||
| 714 | \". o.i.d p.q.'.H V H j.r.s.k.e 8.t.^.u.. \", | ||
| 715 | \" v.r w.x.Q s.d.d.y.] z.5 A.8.t.B.C.D. \", | ||
| 716 | \" E.l.F.e i.G.q 5 7.{.r %.H.^.I.J. \", | ||
| 717 | \" ( K.L.%.M.N.N.O.P.S Q.R.S.T.U.( \", | ||
| 718 | \" @ V.W.H.H.9.X.Y.S.I.Z.`. \", | ||
| 719 | \" . +.+++@+C.#+$+D.. \"};") | ||
| 720 | |||
| 721 | ;; ====================================================================== | ||
| 722 | ;; Functions | ||
| 723 | |||
| 724 | (defsubst bubbles--grid-width () | ||
| 725 | "Return the grid width for the current game theme." | ||
| 726 | (car (case bubbles-game-theme | ||
| 727 | ('easy | ||
| 728 | bubbles--grid-small) | ||
| 729 | ('medium | ||
| 730 | bubbles--grid-medium) | ||
| 731 | ('difficult | ||
| 732 | bubbles--grid-large) | ||
| 733 | ('hard | ||
| 734 | bubbles--grid-huge) | ||
| 735 | ('user-defined | ||
| 736 | bubbles-grid-size)))) | ||
| 737 | |||
| 738 | (defsubst bubbles--grid-height () | ||
| 739 | "Return the grid height for the current game theme." | ||
| 740 | (cdr (case bubbles-game-theme | ||
| 741 | ('easy | ||
| 742 | bubbles--grid-small) | ||
| 743 | ('medium | ||
| 744 | bubbles--grid-medium) | ||
| 745 | ('difficult | ||
| 746 | bubbles--grid-large) | ||
| 747 | ('hard | ||
| 748 | bubbles--grid-huge) | ||
| 749 | ('user-defined | ||
| 750 | bubbles-grid-size)))) | ||
| 751 | |||
| 752 | (defsubst bubbles--colors () | ||
| 753 | "Return the color list for the current game theme." | ||
| 754 | (case bubbles-game-theme | ||
| 755 | ('easy | ||
| 756 | bubbles--colors-2) | ||
| 757 | ('medium | ||
| 758 | bubbles--colors-3) | ||
| 759 | ('difficult | ||
| 760 | bubbles--colors-4) | ||
| 761 | ('hard | ||
| 762 | bubbles--colors-5) | ||
| 763 | ('user-defined | ||
| 764 | bubbles-colors))) | ||
| 765 | |||
| 766 | (defsubst bubbles--shift-mode () | ||
| 767 | "Return the shift mode for the current game theme." | ||
| 768 | (case bubbles-game-theme | ||
| 769 | ('easy | ||
| 770 | 'default) | ||
| 771 | ('medium | ||
| 772 | 'default) | ||
| 773 | ('difficult | ||
| 774 | 'always) | ||
| 775 | ('hard | ||
| 776 | 'always) | ||
| 777 | ('user-defined | ||
| 778 | bubbles-shift-mode))) | ||
| 779 | |||
| 780 | (defun bubbles-save-settings () | ||
| 781 | "Save current customization settings." | ||
| 782 | (interactive) | ||
| 783 | (custom-set-variables | ||
| 784 | (list 'bubbles-game-theme `(quote ,bubbles-game-theme) t) | ||
| 785 | (list 'bubbles-graphics-theme `(quote ,bubbles-graphics-theme) t)) | ||
| 786 | (customize-save-customized)) | ||
| 787 | |||
| 788 | (defsubst bubbles--empty-char () | ||
| 789 | "The character used for removed bubbles (empty grid cells)." | ||
| 790 | ? ) | ||
| 791 | |||
| 792 | (defun bubbles-set-graphics-theme-ascii () | ||
| 793 | "Set graphics theme to `ascii'." | ||
| 794 | (interactive) | ||
| 795 | (setq bubbles-graphics-theme 'ascii) | ||
| 796 | (bubbles--update-faces-or-images)) | ||
| 797 | |||
| 798 | (defun bubbles-set-graphics-theme-circles () | ||
| 799 | "Set graphics theme to `circles'." | ||
| 800 | (interactive) | ||
| 801 | (setq bubbles-graphics-theme 'circles) | ||
| 802 | (bubbles--initialize-images) | ||
| 803 | (bubbles--update-faces-or-images)) | ||
| 804 | |||
| 805 | (defun bubbles-set-graphics-theme-squares () | ||
| 806 | "Set graphics theme to `squares'." | ||
| 807 | (interactive) | ||
| 808 | (setq bubbles-graphics-theme 'squares) | ||
| 809 | (bubbles--initialize-images) | ||
| 810 | (bubbles--update-faces-or-images)) | ||
| 811 | |||
| 812 | (defun bubbles-set-graphics-theme-diamonds () | ||
| 813 | "Set graphics theme to `diamonds'." | ||
| 814 | (interactive) | ||
| 815 | (setq bubbles-graphics-theme 'diamonds) | ||
| 816 | (bubbles--initialize-images) | ||
| 817 | (bubbles--update-faces-or-images)) | ||
| 818 | |||
| 819 | (defun bubbles-set-graphics-theme-balls () | ||
| 820 | "Set graphics theme to `balls'." | ||
| 821 | (interactive) | ||
| 822 | (setq bubbles-graphics-theme 'balls) | ||
| 823 | (bubbles--initialize-images) | ||
| 824 | (bubbles--update-faces-or-images)) | ||
| 825 | |||
| 826 | (defun bubbles-set-graphics-theme-emacs () | ||
| 827 | "Set graphics theme to `emacs'." | ||
| 828 | (interactive) | ||
| 829 | (setq bubbles-graphics-theme 'emacs) | ||
| 830 | (bubbles--initialize-images) | ||
| 831 | (bubbles--update-faces-or-images)) | ||
| 832 | |||
| 833 | ;; bubbles mode map | ||
| 834 | (defvar bubbles-mode-map | ||
| 835 | (make-keymap 'bubbles-mode-map)) | ||
| 836 | (define-key bubbles-mode-map "q" 'bubbles-quit) | ||
| 837 | (define-key bubbles-mode-map "\n" 'bubbles-plop) | ||
| 838 | (define-key bubbles-mode-map " " 'bubbles-plop) | ||
| 839 | (define-key bubbles-mode-map [double-down-mouse-1] 'bubbles-plop) | ||
| 840 | (define-key bubbles-mode-map [mouse-2] 'bubbles-plop) | ||
| 841 | (define-key bubbles-mode-map "\C-m" 'bubbles-plop) | ||
| 842 | (define-key bubbles-mode-map "u" 'bubbles-undo) | ||
| 843 | (define-key bubbles-mode-map "p" 'previous-line) | ||
| 844 | (define-key bubbles-mode-map "n" 'next-line) | ||
| 845 | (define-key bubbles-mode-map "f" 'forward-char) | ||
| 846 | (define-key bubbles-mode-map "b" 'backward-char) | ||
| 847 | |||
| 848 | |||
| 849 | ;; game theme menu | ||
| 850 | (defvar bubbles-game-theme-menu (make-sparse-keymap "Game Theme")) | ||
| 851 | (define-key bubbles-game-theme-menu [bubbles-set-game-userdefined] | ||
| 852 | (list 'menu-item "User defined" 'bubbles-set-game-userdefined | ||
| 853 | :button '(:radio . (eq bubbles-game-theme 'user-defined)))) | ||
| 854 | (define-key bubbles-game-theme-menu [bubbles-set-game-hard] | ||
| 855 | (list 'menu-item "Hard" 'bubbles-set-game-hard | ||
| 856 | :button '(:radio . (eq bubbles-game-theme 'hard)))) | ||
| 857 | (define-key bubbles-game-theme-menu [bubbles-set-game-difficult] | ||
| 858 | (list 'menu-item "Difficult" 'bubbles-set-game-difficult | ||
| 859 | :button '(:radio . (eq bubbles-game-theme 'difficult)))) | ||
| 860 | (define-key bubbles-game-theme-menu [bubbles-set-game-medium] | ||
| 861 | (list 'menu-item "Medium" 'bubbles-set-game-medium | ||
| 862 | :button '(:radio . (eq bubbles-game-theme 'medium)))) | ||
| 863 | (define-key bubbles-game-theme-menu [bubbles-set-game-easy] | ||
| 864 | (list 'menu-item "Easy" 'bubbles-set-game-easy | ||
| 865 | :button '(:radio . (eq bubbles-game-theme 'easy)))) | ||
| 866 | |||
| 867 | ;; graphics theme menu | ||
| 868 | (defvar bubbles-graphics-theme-menu (make-sparse-keymap "Graphics Theme")) | ||
| 869 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-ascii] | ||
| 870 | (list 'menu-item "ASCII" 'bubbles-set-graphics-theme-ascii | ||
| 871 | :button '(:radio . (eq bubbles-graphics-theme 'ascii)))) | ||
| 872 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-emacs] | ||
| 873 | (list 'menu-item "Emacs" 'bubbles-set-graphics-theme-emacs | ||
| 874 | :button '(:radio . (eq bubbles-graphics-theme 'emacs)))) | ||
| 875 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-balls] | ||
| 876 | (list 'menu-item "Balls" 'bubbles-set-graphics-theme-balls | ||
| 877 | :button '(:radio . (eq bubbles-graphics-theme 'balls)))) | ||
| 878 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-diamonds] | ||
| 879 | (list 'menu-item "Diamonds" 'bubbles-set-graphics-theme-diamonds | ||
| 880 | :button '(:radio . (eq bubbles-graphics-theme 'diamonds)))) | ||
| 881 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-squares] | ||
| 882 | (list 'menu-item "Squares" 'bubbles-set-graphics-theme-squares | ||
| 883 | :button '(:radio . (eq bubbles-graphics-theme 'squares)))) | ||
| 884 | (define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-circles] | ||
| 885 | (list 'menu-item "Circles" 'bubbles-set-graphics-theme-circles | ||
| 886 | :button '(:radio . (eq bubbles-graphics-theme 'circles)))) | ||
| 887 | |||
| 888 | ;; menu | ||
| 889 | (defvar bubbles-menu (make-sparse-keymap "Bubbles")) | ||
| 890 | (define-key bubbles-menu [bubbles-quit] | ||
| 891 | (list 'menu-item "Quit" 'bubbles-quit)) | ||
| 892 | (define-key bubbles-menu [bubbles] | ||
| 893 | (list 'menu-item "New game" 'bubbles)) | ||
| 894 | (define-key bubbles-menu [bubbles-separator-1] | ||
| 895 | '("--")) | ||
| 896 | (define-key bubbles-menu [bubbles-save-settings] | ||
| 897 | (list 'menu-item "Save all settings" 'bubbles-save-settings)) | ||
| 898 | (define-key bubbles-menu [bubbles-customize] | ||
| 899 | (list 'menu-item "Edit all settings" 'bubbles-customize)) | ||
| 900 | (define-key bubbles-menu [bubbles-game-theme-menu] | ||
| 901 | (list 'menu-item "Game Theme" bubbles-game-theme-menu)) | ||
| 902 | (define-key bubbles-menu [bubbles-graphics-theme-menu] | ||
| 903 | (list 'menu-item "Graphics Theme" bubbles-graphics-theme-menu | ||
| 904 | :enable 'bubbles--playing)) | ||
| 905 | (define-key bubbles-menu [bubbles-separator-2] | ||
| 906 | '("--")) | ||
| 907 | (define-key bubbles-menu [bubbles-undo] | ||
| 908 | (list 'menu-item "Undo last move" 'bubbles-undo | ||
| 909 | :enable '(and bubbles--playing bubbles--save-data))) | ||
| 910 | |||
| 911 | ;; bind menu to mouse | ||
| 912 | (define-key bubbles-mode-map [down-mouse-3] bubbles-menu) | ||
| 913 | ;; Put menu in menu-bar | ||
| 914 | (define-key bubbles-mode-map [menu-bar Bubbles] | ||
| 915 | (cons "Bubbles" bubbles-menu)) | ||
| 916 | |||
| 917 | (defun bubbles-mode () | ||
| 918 | "Major mode for playing bubbles. | ||
| 919 | \\{bubbles-mode-map}" | ||
| 920 | (kill-all-local-variables) | ||
| 921 | (use-local-map bubbles-mode-map) | ||
| 922 | (setq major-mode 'bubbles-mode) | ||
| 923 | (setq mode-name "Bubbles") | ||
| 924 | (setq buffer-read-only t) | ||
| 925 | (buffer-enable-undo) | ||
| 926 | (add-hook 'post-command-hook 'bubbles--mark-neighbourhood t t) | ||
| 927 | (run-hooks 'bubbles-mode-hook)) | ||
| 928 | |||
| 929 | ;;;###autoload | ||
| 930 | (defun bubbles () | ||
| 931 | "Play Bubbles game." | ||
| 932 | (interactive) | ||
| 933 | (switch-to-buffer (get-buffer-create "*bubbles*")) | ||
| 934 | (when (or (not bubbles--playing) | ||
| 935 | (y-or-n-p "Start new game? ")) | ||
| 936 | (setq bubbles--save-data nil) | ||
| 937 | (setq bubbles--playing t) | ||
| 938 | (bubbles--initialize))) | ||
| 939 | |||
| 940 | (defun bubbles-quit () | ||
| 941 | "Quit Bubbles." | ||
| 942 | (interactive) | ||
| 943 | (message "bubbles-quit") | ||
| 944 | (bury-buffer)) | ||
| 945 | |||
| 946 | (defun bubbles--compute-offsets () | ||
| 947 | "Update horizontal and vertical offsets for centering the bubbles grid. | ||
| 948 | Set `bubbles--col-offset' and `bubbles--row-offset'." | ||
| 949 | (cond ((and (display-images-p) | ||
| 950 | bubbles--images-ok | ||
| 951 | (not (eq bubbles-graphics-theme 'ascii)) | ||
| 952 | (fboundp 'window-inside-pixel-edges)) | ||
| 953 | ;; compute offset in units of pixels | ||
| 954 | (let ((bubbles--image-size | ||
| 955 | (car (image-size (car bubbles--images) t)))) | ||
| 956 | (setq bubbles--col-offset | ||
| 957 | (list | ||
| 958 | (max 0 (/ (- (nth 2 (window-inside-pixel-edges)) | ||
| 959 | (nth 0 (window-inside-pixel-edges)) | ||
| 960 | (* ( + bubbles--image-size 2) ;; margin | ||
| 961 | (bubbles--grid-width))) 2)))) | ||
| 962 | (setq bubbles--row-offset | ||
| 963 | (list | ||
| 964 | (max 0 (/ (- (nth 3 (window-inside-pixel-edges)) | ||
| 965 | (nth 1 (window-inside-pixel-edges)) | ||
| 966 | (* (+ bubbles--image-size 1) ;; margin | ||
| 967 | (bubbles--grid-height))) 2)))))) | ||
| 968 | (t | ||
| 969 | ;; compute offset in units of chars | ||
| 970 | (setq bubbles--col-offset | ||
| 971 | (max 0 (/ (- (window-width) | ||
| 972 | (bubbles--grid-width)) 2))) | ||
| 973 | (setq bubbles--row-offset | ||
| 974 | (max 0 (/ (- (window-height) | ||
| 975 | (bubbles--grid-height) 2) 2)))))) | ||
| 976 | |||
| 977 | (defun bubbles--remove-overlays () | ||
| 978 | "Remove all overlays." | ||
| 979 | (if (fboundp 'remove-overlays) | ||
| 980 | (remove-overlays))) | ||
| 981 | |||
| 982 | (defun bubbles--initialize () | ||
| 983 | "Initialize Bubbles game." | ||
| 984 | (bubbles--initialize-faces) | ||
| 985 | (bubbles--initialize-images) | ||
| 986 | (bubbles--remove-overlays) | ||
| 987 | |||
| 988 | (switch-to-buffer (get-buffer-create "*bubbles*")) | ||
| 989 | (bubbles--compute-offsets) | ||
| 990 | (let ((inhibit-read-only t)) | ||
| 991 | (set-buffer-modified-p nil) | ||
| 992 | (erase-buffer) | ||
| 993 | (insert " ") | ||
| 994 | (add-text-properties | ||
| 995 | (point-min) (point) (list 'intangible t 'display | ||
| 996 | (cons 'space | ||
| 997 | (list :height bubbles--row-offset)))) | ||
| 998 | (insert "\n") | ||
| 999 | (let ((max-char (length (bubbles--colors)))) | ||
| 1000 | (dotimes (i (bubbles--grid-height)) | ||
| 1001 | (let ((p (point))) | ||
| 1002 | (insert " ") | ||
| 1003 | (add-text-properties | ||
| 1004 | p (point) (list 'intangible t | ||
| 1005 | 'display (cons 'space | ||
| 1006 | (list :width | ||
| 1007 | bubbles--col-offset))))) | ||
| 1008 | (dotimes (j (bubbles--grid-width)) | ||
| 1009 | (let* ((index (random max-char)) | ||
| 1010 | (char (nth index bubbles-chars))) | ||
| 1011 | (insert char) | ||
| 1012 | (add-text-properties (1- (point)) (point) (list 'index index)))) | ||
| 1013 | (insert "\n")) | ||
| 1014 | (insert "\n ") | ||
| 1015 | (add-text-properties | ||
| 1016 | (1- (point)) (point) (list 'intangible t 'display | ||
| 1017 | (cons 'space | ||
| 1018 | (list :width bubbles--col-offset))))) | ||
| 1019 | (put-text-property (point-min) (point-max) 'pointer 'arrow)) | ||
| 1020 | (bubbles-mode) | ||
| 1021 | (bubbles--reset-score) | ||
| 1022 | (bubbles--update-faces-or-images) | ||
| 1023 | (bubbles--goto 0 0)) | ||
| 1024 | |||
| 1025 | (defun bubbles--initialize-faces () | ||
| 1026 | "Prepare faces for playing `bubbles'." | ||
| 1027 | (copy-face 'default 'bubbles--highlight-face) | ||
| 1028 | (set-face-background 'bubbles--highlight-face "#8080f4") | ||
| 1029 | (when (display-color-p) | ||
| 1030 | (setq bubbles--faces | ||
| 1031 | (mapcar (lambda (color) | ||
| 1032 | (let ((fname (intern (format "bubbles--face-%s" color)))) | ||
| 1033 | (unless (facep fname) | ||
| 1034 | (copy-face 'default fname) | ||
| 1035 | (set-face-foreground fname color)) | ||
| 1036 | fname)) | ||
| 1037 | (bubbles--colors))))) | ||
| 1038 | |||
| 1039 | (defsubst bubbles--row (pos) | ||
| 1040 | "Return row of point POS." | ||
| 1041 | (save-excursion | ||
| 1042 | (goto-char pos) | ||
| 1043 | (beginning-of-line) | ||
| 1044 | (1- (count-lines (point-min) (point))))) | ||
| 1045 | |||
| 1046 | (defsubst bubbles--col (pos) | ||
| 1047 | "Return column of point POS." | ||
| 1048 | (save-excursion | ||
| 1049 | (goto-char pos) | ||
| 1050 | (1- (current-column)))) | ||
| 1051 | |||
| 1052 | (defun bubbles--goto (row col) | ||
| 1053 | "Move point to bubble at coordinates ROW and COL." | ||
| 1054 | (if (or (< row 0) | ||
| 1055 | (< col 0) | ||
| 1056 | (>= row (bubbles--grid-height)) | ||
| 1057 | (>= col (bubbles--grid-width))) | ||
| 1058 | ;; Error! return nil | ||
| 1059 | nil | ||
| 1060 | ;; go | ||
| 1061 | (goto-char (point-min)) | ||
| 1062 | (forward-line (1+ row)) | ||
| 1063 | (forward-char (1+ col)) | ||
| 1064 | (point))) | ||
| 1065 | |||
| 1066 | (defun bubbles--char-at (row col) | ||
| 1067 | "Return character at bubble ROW and COL." | ||
| 1068 | (save-excursion | ||
| 1069 | (if (bubbles--goto row col) | ||
| 1070 | (char-after (point)) | ||
| 1071 | nil))) | ||
| 1072 | |||
| 1073 | (defun bubbles--mark-direct-neighbours (row col char) | ||
| 1074 | "Mark direct neighbours of bubble at ROW COL with same CHAR." | ||
| 1075 | (save-excursion | ||
| 1076 | (let ((count 0)) | ||
| 1077 | (when (and (bubbles--goto row col) | ||
| 1078 | (eq char (char-after (point))) | ||
| 1079 | (not (get-text-property (point) 'active))) | ||
| 1080 | (add-text-properties (point) (1+ (point)) | ||
| 1081 | '(active t face 'bubbles--highlight-face)) | ||
| 1082 | (setq count (+ 1 | ||
| 1083 | (bubbles--mark-direct-neighbours row (1+ col) char) | ||
| 1084 | (bubbles--mark-direct-neighbours row (1- col) char) | ||
| 1085 | (bubbles--mark-direct-neighbours (1+ row) col char) | ||
| 1086 | (bubbles--mark-direct-neighbours (1- row) col char)))) | ||
| 1087 | count))) | ||
| 1088 | |||
| 1089 | (defun bubbles--mark-neighbourhood (&optional pos) | ||
| 1090 | "Mark neighbourhood of point. | ||
| 1091 | Use optional parameter POS instead of point if given." | ||
| 1092 | (when bubbles--playing | ||
| 1093 | (unless pos (setq pos (point))) | ||
| 1094 | (condition-case err | ||
| 1095 | (let ((char (char-after pos)) | ||
| 1096 | (inhibit-read-only t) | ||
| 1097 | (row (bubbles--row (point))) | ||
| 1098 | (col (bubbles--col (point)))) | ||
| 1099 | (add-text-properties (point-min) (point-max) | ||
| 1100 | '(face default active nil)) | ||
| 1101 | (let ((count 0)) | ||
| 1102 | (when (and row col (not (eq char (bubbles--empty-char)))) | ||
| 1103 | (setq count (bubbles--mark-direct-neighbours row col char)) | ||
| 1104 | (unless (> count 1) | ||
| 1105 | (add-text-properties (point-min) (point-max) | ||
| 1106 | '(face default active nil)) | ||
| 1107 | (setq count 0))) | ||
| 1108 | (bubbles--update-neighbourhood-score count)) | ||
| 1109 | (put-text-property (point-min) (point-max) 'pointer 'arrow) | ||
| 1110 | (bubbles--update-faces-or-images) | ||
| 1111 | (sit-for 0)) | ||
| 1112 | (error (message "Bubbles: Internal error %s" err))))) | ||
| 1113 | |||
| 1114 | (defun bubbles--neighbourhood-available () | ||
| 1115 | "Return t if another valid neighbourhood is available." | ||
| 1116 | (catch 'found | ||
| 1117 | (save-excursion | ||
| 1118 | (dotimes (i (bubbles--grid-height)) | ||
| 1119 | (dotimes (j (bubbles--grid-width)) | ||
| 1120 | (let ((c (bubbles--char-at i j))) | ||
| 1121 | (if (and (not (eq c (bubbles--empty-char))) | ||
| 1122 | (or (eq c (bubbles--char-at (1+ i) j)) | ||
| 1123 | (eq c (bubbles--char-at i (1+ j))))) | ||
| 1124 | (throw 'found t))))) | ||
| 1125 | nil))) | ||
| 1126 | |||
| 1127 | (defun bubbles--count () | ||
| 1128 | "Count remaining bubbles." | ||
| 1129 | (let ((count 0)) | ||
| 1130 | (save-excursion | ||
| 1131 | (dotimes (i (bubbles--grid-height)) | ||
| 1132 | (dotimes (j (bubbles--grid-width)) | ||
| 1133 | (let ((c (bubbles--char-at i j))) | ||
| 1134 | (if (not (eq c (bubbles--empty-char))) | ||
| 1135 | (setq count (1+ count))))))) | ||
| 1136 | count)) | ||
| 1137 | |||
| 1138 | (defun bubbles--reset-score () | ||
| 1139 | "Reset bubbles score." | ||
| 1140 | (setq bubbles--neighbourhood-score 0 | ||
| 1141 | bubbles--score 0) | ||
| 1142 | (bubbles--update-score)) | ||
| 1143 | |||
| 1144 | (defun bubbles--update-score () | ||
| 1145 | "Calculate and display new bubble score." | ||
| 1146 | (setq bubbles--score (+ bubbles--score bubbles--neighbourhood-score)) | ||
| 1147 | (bubbles--show-scores)) | ||
| 1148 | |||
| 1149 | (defun bubbles--update-neighbourhood-score (size) | ||
| 1150 | "Calculate and display score of active neighbourhood from its SIZE." | ||
| 1151 | (if (> size 1) | ||
| 1152 | (setq bubbles--neighbourhood-score (expt (- size 1) 2)) | ||
| 1153 | (setq bubbles--neighbourhood-score 0)) | ||
| 1154 | (bubbles--show-scores)) | ||
| 1155 | |||
| 1156 | (defun bubbles--show-scores () | ||
| 1157 | "Display current scores." | ||
| 1158 | (save-excursion | ||
| 1159 | (goto-char (or (next-single-property-change (point-min) 'status) | ||
| 1160 | (point-max))) | ||
| 1161 | (let ((inhibit-read-only t) | ||
| 1162 | (pos (point))) | ||
| 1163 | (delete-region (point) (point-max)) | ||
| 1164 | (insert (format "Selected: %4d\n" bubbles--neighbourhood-score)) | ||
| 1165 | (insert " ") | ||
| 1166 | (add-text-properties (1- (point)) (point) | ||
| 1167 | (list 'intangible t 'display | ||
| 1168 | (cons 'space | ||
| 1169 | (list :width bubbles--col-offset)))) | ||
| 1170 | (insert (format "Score: %4d" bubbles--score)) | ||
| 1171 | (put-text-property pos (point) 'status t)))) | ||
| 1172 | |||
| 1173 | (defun bubbles--game-over () | ||
| 1174 | "Finish bubbles game." | ||
| 1175 | (bubbles--update-faces-or-images) | ||
| 1176 | (setq bubbles--playing nil | ||
| 1177 | bubbles--save-data nil) | ||
| 1178 | ;; add bonus if all bubbles were removed | ||
| 1179 | (when (= 0 (bubbles--count)) | ||
| 1180 | (setq bubbles--score (+ bubbles--score (* (bubbles--grid-height) | ||
| 1181 | (bubbles--grid-width)))) | ||
| 1182 | (bubbles--show-scores)) | ||
| 1183 | ;; Game over message | ||
| 1184 | (goto-char (point-max)) | ||
| 1185 | (let* ((inhibit-read-only t)) | ||
| 1186 | (insert "\n ") | ||
| 1187 | (add-text-properties (1- (point)) (point) | ||
| 1188 | (list 'intangible t 'display | ||
| 1189 | (cons 'space | ||
| 1190 | (list :width bubbles--col-offset)))) | ||
| 1191 | (insert "Game Over!")) | ||
| 1192 | ;; save score | ||
| 1193 | (gamegrid-add-score (format "bubbles-%s-%d-%d-%d-scores" | ||
| 1194 | (symbol-name (bubbles--shift-mode)) | ||
| 1195 | (length (bubbles--colors)) | ||
| 1196 | (bubbles--grid-width) (bubbles--grid-height)) | ||
| 1197 | bubbles--score)) | ||
| 1198 | |||
| 1199 | (defun bubbles-plop () | ||
| 1200 | "Remove active bubbles region." | ||
| 1201 | (interactive) | ||
| 1202 | (when (and bubbles--playing | ||
| 1203 | (> bubbles--neighbourhood-score 0)) | ||
| 1204 | (setq bubbles--save-data (list bubbles--score (buffer-string))) | ||
| 1205 | (setq buffer-undo-list '(apply bubbles-undo . nil)) | ||
| 1206 | (let ((inhibit-read-only t)) | ||
| 1207 | ;; blank out current neighbourhood | ||
| 1208 | (let ((row (bubbles--row (point))) | ||
| 1209 | (col (bubbles--col (point)))) | ||
| 1210 | (goto-char (point-max)) | ||
| 1211 | (while (not (bobp)) | ||
| 1212 | (backward-char) | ||
| 1213 | (while (get-text-property (point) 'active) | ||
| 1214 | (delete-char 1) | ||
| 1215 | (insert (bubbles--empty-char)) | ||
| 1216 | (add-text-properties (1- (point)) (point) (list 'removed t | ||
| 1217 | 'index -1)))) | ||
| 1218 | (bubbles--goto row col)) | ||
| 1219 | ;; show new score | ||
| 1220 | (bubbles--update-score) | ||
| 1221 | ;; update display and wait | ||
| 1222 | (bubbles--update-faces-or-images) | ||
| 1223 | (sit-for 0) | ||
| 1224 | (sleep-for 0.2) | ||
| 1225 | (discard-input) | ||
| 1226 | ;; drop down | ||
| 1227 | (let ((something-dropped nil)) | ||
| 1228 | (save-excursion | ||
| 1229 | (dotimes (i (bubbles--grid-height)) | ||
| 1230 | (dotimes (j (bubbles--grid-width)) | ||
| 1231 | (bubbles--goto i j) | ||
| 1232 | (while (get-text-property (point) 'removed) | ||
| 1233 | (setq something-dropped (or (bubbles--shift 'top i j) | ||
| 1234 | something-dropped)))))) | ||
| 1235 | ;; update display and wait | ||
| 1236 | (bubbles--update-faces-or-images) | ||
| 1237 | (when something-dropped | ||
| 1238 | (sit-for 0))) | ||
| 1239 | (discard-input) | ||
| 1240 | ;; shift to left | ||
| 1241 | (put-text-property (point-min) (point-max) 'removed nil) | ||
| 1242 | (save-excursion | ||
| 1243 | (goto-char (point-min)) | ||
| 1244 | (let ((removed-string (format "%c" (bubbles--empty-char)))) | ||
| 1245 | (while (search-forward removed-string nil t) | ||
| 1246 | (put-text-property (1- (point)) (point) 'removed t)))) | ||
| 1247 | (let ((shifted nil)) | ||
| 1248 | (cond ((eq (bubbles--shift-mode) 'always) | ||
| 1249 | (save-excursion | ||
| 1250 | (dotimes (i (bubbles--grid-height)) | ||
| 1251 | (dotimes (j (bubbles--grid-width)) | ||
| 1252 | (bubbles--goto i j) | ||
| 1253 | (while (get-text-property (point) 'removed) | ||
| 1254 | (setq shifted (or (bubbles--shift 'right i j) shifted)))))) | ||
| 1255 | (bubbles--update-faces-or-images) | ||
| 1256 | (sleep-for 0.5)) | ||
| 1257 | (t ;; default shift-mode | ||
| 1258 | (save-excursion | ||
| 1259 | (dotimes (j (bubbles--grid-width)) | ||
| 1260 | (bubbles--goto (1- (bubbles--grid-height)) j) | ||
| 1261 | (let ((shifted-cols 0)) | ||
| 1262 | (while (get-text-property (point) 'removed) | ||
| 1263 | (setq shifted-cols (1+ shifted-cols)) | ||
| 1264 | (bubbles--shift 'right (1- (bubbles--grid-height)) j)) | ||
| 1265 | (dotimes (k shifted-cols) | ||
| 1266 | (let ((i (- (bubbles--grid-height) 2))) | ||
| 1267 | (while (>= i 0) | ||
| 1268 | (setq shifted (or (bubbles--shift 'right i j) shifted)) | ||
| 1269 | (setq i (1- i)))))))))) | ||
| 1270 | (when shifted | ||
| 1271 | ;;(sleep-for 0.5) | ||
| 1272 | (bubbles--update-faces-or-images) | ||
| 1273 | (sit-for 0))) | ||
| 1274 | (put-text-property (point-min) (point-max) 'removed nil) | ||
| 1275 | (unless (bubbles--neighbourhood-available) | ||
| 1276 | (bubbles--game-over))))) | ||
| 1277 | |||
| 1278 | (defun bubbles-undo () | ||
| 1279 | "Undo last move." | ||
| 1280 | (interactive) | ||
| 1281 | (when bubbles--save-data | ||
| 1282 | (let ((inhibit-read-only t) | ||
| 1283 | (pos (point))) | ||
| 1284 | (erase-buffer) | ||
| 1285 | (insert (cadr bubbles--save-data)) | ||
| 1286 | (bubbles--update-faces-or-images) | ||
| 1287 | (setq bubbles--score (car bubbles--save-data)) | ||
| 1288 | (goto-char pos)))) | ||
| 1289 | |||
| 1290 | (defun bubbles--shift (from row col) | ||
| 1291 | "Move bubbles FROM one side to position ROW COL. | ||
| 1292 | Return t if new char is non-empty." | ||
| 1293 | (save-excursion | ||
| 1294 | (when (bubbles--goto row col) | ||
| 1295 | (let ((char-org (char-after (point))) | ||
| 1296 | (char-new (bubbles--empty-char)) | ||
| 1297 | (removed nil) | ||
| 1298 | (trow row) | ||
| 1299 | (tcol col) | ||
| 1300 | (index -1)) | ||
| 1301 | (cond ((eq from 'top) | ||
| 1302 | (setq trow (1- row))) | ||
| 1303 | ((eq from 'left) | ||
| 1304 | (setq tcol (1- col))) | ||
| 1305 | ((eq from 'right) | ||
| 1306 | (setq tcol (1+ col)))) | ||
| 1307 | (save-excursion | ||
| 1308 | (when (bubbles--goto trow tcol) | ||
| 1309 | (setq char-new (char-after (point))) | ||
| 1310 | (setq removed (get-text-property (point) 'removed)) | ||
| 1311 | (setq index (get-text-property (point) 'index)) | ||
| 1312 | (bubbles--shift from trow tcol))) | ||
| 1313 | (insert char-new) | ||
| 1314 | (delete-char 1) | ||
| 1315 | (add-text-properties (1- (point)) (point) (list 'index index | ||
| 1316 | 'removed removed)) | ||
| 1317 | (not (eq char-new (bubbles--empty-char))))))) | ||
| 1318 | |||
| 1319 | (defun bubbles--initialize-images () | ||
| 1320 | "Prepare images for playing `bubbles'." | ||
| 1321 | (when (and (display-images-p) | ||
| 1322 | (not (eq bubbles-graphics-theme 'ascii))) | ||
| 1323 | (let ((template (case bubbles-graphics-theme | ||
| 1324 | ('circles bubbles--image-template-circle) | ||
| 1325 | ('balls bubbles--image-template-ball) | ||
| 1326 | ('squares bubbles--image-template-square) | ||
| 1327 | ('diamonds bubbles--image-template-diamond) | ||
| 1328 | ('emacs bubbles--image-template-emacs)))) | ||
| 1329 | (setq bubbles--empty-image | ||
| 1330 | (create-image (replace-regexp-in-string | ||
| 1331 | "^\"\\(.*\\)\t.*c .*\",$" | ||
| 1332 | "\"\\1\tc #FFFFFF\"," template) | ||
| 1333 | 'xpm t | ||
| 1334 | ;;:mask 'heuristic | ||
| 1335 | :margin '(2 . 1))) | ||
| 1336 | (setq bubbles--images | ||
| 1337 | (mapcar (lambda (color) | ||
| 1338 | (let* ((rgb (color-values color)) | ||
| 1339 | (red (nth 0 rgb)) | ||
| 1340 | (green (nth 1 rgb)) | ||
| 1341 | (blue (nth 2 rgb))) | ||
| 1342 | (with-temp-buffer | ||
| 1343 | (insert template) | ||
| 1344 | (goto-char (point-min)) | ||
| 1345 | (re-search-forward | ||
| 1346 | "^\"[0-9]+ [0-9]+ \\(.*?\\) .*\",$" nil t) | ||
| 1347 | (goto-char (point-min)) | ||
| 1348 | (while (re-search-forward | ||
| 1349 | "^\"\\(.*\\)\t.*c \\(#.*\\)\",$" nil t) | ||
| 1350 | (let* ((crgb (color-values (match-string 2))) | ||
| 1351 | (r (nth 0 crgb)) | ||
| 1352 | (g (nth 1 crgb)) | ||
| 1353 | (b (nth 2 crgb)) | ||
| 1354 | (brightness (/ (+ r g b) 3.0 256 256)) | ||
| 1355 | (val (sin (* brightness (/ pi 2)))) | ||
| 1356 | (rr (* red val)) | ||
| 1357 | (gg (* green val)) | ||
| 1358 | (bb (* blue val)) | ||
| 1359 | ;;(rr (/ (+ red r) 2)) | ||
| 1360 | ;;(gg (/ (+ green g) 2)) | ||
| 1361 | ;;(bb (/ (+ blue b) 2)) | ||
| 1362 | (color (format "#%02x%02x%02x" | ||
| 1363 | (/ rr 256) (/ gg 256) | ||
| 1364 | (/ bb 256)))) | ||
| 1365 | (replace-match (format "\"\\1\tc %s\"," | ||
| 1366 | (upcase color))))) | ||
| 1367 | (create-image (buffer-string) 'xpm t | ||
| 1368 | :margin '(2 . 1) | ||
| 1369 | ;;:mask 'heuristic | ||
| 1370 | )))) | ||
| 1371 | (bubbles--colors)))) | ||
| 1372 | ;; check images | ||
| 1373 | (setq bubbles--images-ok bubbles--empty-image) | ||
| 1374 | (mapc (lambda (elt) | ||
| 1375 | (setq bubbles--images-ok (and bubbles--images-ok elt))) | ||
| 1376 | bubbles--images))) | ||
| 1377 | |||
| 1378 | (defun bubbles--update-faces-or-images () | ||
| 1379 | "Update faces and/or images, depending on graphics mode." | ||
| 1380 | (bubbles--set-faces) | ||
| 1381 | (bubbles--show-images)) | ||
| 1382 | |||
| 1383 | (defun bubbles--set-faces () | ||
| 1384 | "Update faces in the bubbles buffer." | ||
| 1385 | (unless (and (display-images-p) | ||
| 1386 | bubbles--images-ok | ||
| 1387 | (not (eq bubbles-graphics-theme 'ascii))) | ||
| 1388 | (when (display-color-p) | ||
| 1389 | (save-excursion | ||
| 1390 | (let ((inhibit-read-only t)) | ||
| 1391 | (dotimes (i (bubbles--grid-height)) | ||
| 1392 | (dotimes (j (bubbles--grid-width)) | ||
| 1393 | (bubbles--goto i j) | ||
| 1394 | (let* ((index (get-text-property (point) 'index)) | ||
| 1395 | (face (nth index bubbles--faces)) | ||
| 1396 | (fg-col (face-foreground face))) | ||
| 1397 | (when (get-text-property (point) 'active) | ||
| 1398 | (set-face-foreground 'bubbles--highlight-face "#ff0000") | ||
| 1399 | (setq face 'bubbles--highlight-face)) | ||
| 1400 | (put-text-property (point) (1+ (point)) | ||
| 1401 | 'face face))))))))) | ||
| 1402 | |||
| 1403 | (defun bubbles--show-images () | ||
| 1404 | "Update images in the bubbles buffer." | ||
| 1405 | (bubbles--remove-overlays) | ||
| 1406 | (if (and (display-images-p) | ||
| 1407 | bubbles--images-ok | ||
| 1408 | (not (eq bubbles-graphics-theme 'ascii))) | ||
| 1409 | (save-excursion | ||
| 1410 | (goto-char (point-min)) | ||
| 1411 | (forward-line 1) | ||
| 1412 | (let ((inhibit-read-only t) | ||
| 1413 | char) | ||
| 1414 | (dotimes (i (bubbles--grid-height)) | ||
| 1415 | (dotimes (j (bubbles--grid-width)) | ||
| 1416 | (forward-char 1) | ||
| 1417 | (let ((index (get-text-property (point) 'index))) | ||
| 1418 | (let ((img bubbles--empty-image)) | ||
| 1419 | (if (>= index 0) | ||
| 1420 | (setq img (nth index bubbles--images))) | ||
| 1421 | (put-text-property (point) (1+ (point)) | ||
| 1422 | 'display (cons img nil))))) | ||
| 1423 | (forward-line 1)))) | ||
| 1424 | (save-excursion | ||
| 1425 | (let ((inhibit-read-only t)) | ||
| 1426 | (goto-char (point-min)) | ||
| 1427 | (while (not (eobp)) | ||
| 1428 | (let ((disp-prop (get-text-property (point) 'display))) | ||
| 1429 | (if (and (listp disp-prop) | ||
| 1430 | (listp (car disp-prop)) | ||
| 1431 | (eq (caar disp-prop) 'image)) | ||
| 1432 | (put-text-property (point) (1+ (point)) 'display nil)) | ||
| 1433 | (forward-char 1))) | ||
| 1434 | (put-text-property (point-min) (point-max) 'pointer 'arrow))))) | ||
| 1435 | |||
| 1436 | (provide 'bubbles) | ||
| 1437 | |||
| 1438 | ;;; bubbles.el ends here | ||